Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Allow setting proguard via Options and/or external resources #1728

Merged
merged 10 commits into from
Sep 20, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

* Feat: Allow setting proguard via Options and/or external resources (#1728)
* Feat: Add breadcrumbs for the Apollo integration (#1726)
* Fix: Don't set lastEventId for transactions (#1727)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.res.AssetManager;
import android.os.Build;
import io.sentry.ILogger;
import io.sentry.SendCachedEnvelopeFireAndForgetIntegration;
Expand All @@ -13,7 +14,12 @@
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.util.Objects;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -190,6 +196,35 @@ private static void readDefaultOptionValues(
options.getLogger().log(SentryLevel.ERROR, "Could not generate distinct Id.", e);
}
}

if (options.getProguardUuid() == null) {
options.setProguardUuid(getProguardUUID(context, options.getLogger()));
}
}

private static @Nullable String getProguardUUID(
final @NotNull Context context, final @NotNull ILogger logger) {
final AssetManager assets = context.getAssets();
// one may have thousands of asset files and looking up this list might slow down the SDK init.
// quite a bit, for this reason, we try to open the file directly and take care of errors
// like FileNotFoundException
try (final InputStream is =
new BufferedInputStream(assets.open("sentry-debug-meta.properties"))) {
final Properties properties = new Properties();
properties.load(is);

final String uuid = properties.getProperty("io.sentry.ProguardUuids");
logger.log(SentryLevel.DEBUG, "Proguard UUID found: %s", uuid);
return uuid;
} catch (FileNotFoundException e) {
logger.log(SentryLevel.INFO, "sentry-debug-meta.properties file was not found.");
} catch (IOException e) {
logger.log(SentryLevel.ERROR, "Error getting Proguard UUIDs.", e);
} catch (RuntimeException e) {
logger.log(SentryLevel.ERROR, "sentry-debug-meta.properties file is malformed.", e);
}

return null;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Environment;
Expand All @@ -30,30 +29,22 @@
import io.sentry.android.core.util.MainThreadChecker;
import io.sentry.android.core.util.RootChecker;
import io.sentry.protocol.App;
import io.sentry.protocol.DebugImage;
import io.sentry.protocol.DebugMeta;
import io.sentry.protocol.Device;
import io.sentry.protocol.OperatingSystem;
import io.sentry.protocol.SentryThread;
import io.sentry.protocol.SentryTransaction;
import io.sentry.protocol.User;
import io.sentry.util.ApplyScopeUtils;
import io.sentry.util.Objects;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand Down Expand Up @@ -106,10 +97,6 @@ public DefaultAndroidEventProcessor(

private @NotNull Map<String, Object> loadContextData() {
Map<String, Object> map = new HashMap<>();
String[] proguardUUIDs = getProguardUUIDs();
if (proguardUUIDs != null) {
map.put(PROGUARD_UUID, proguardUUIDs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var PROGUARD_UUID can be removed from this class now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, missed that.

}

map.put(ROOTED, rootChecker.isDeviceRooted());

Expand Down Expand Up @@ -138,7 +125,6 @@ public DefaultAndroidEventProcessor(
// enriched on restart, so non static data might be wrong, eg lowMemory or availMem will
// be different if the App. crashes because of OOM.
processNonCachedEvent(event);
mergeDebugImages(event);
setThreads(event);
}

Expand Down Expand Up @@ -245,55 +231,6 @@ private void setDist(final @NotNull SentryBaseEvent event, final @NotNull String
}
}

private void mergeDebugImages(final @NotNull SentryEvent event) {
final List<DebugImage> debugImages = getDebugImages();
if (debugImages == null) {
return;
}

DebugMeta debugMeta = event.getDebugMeta();

if (debugMeta == null) {
debugMeta = new DebugMeta();
}

// sets the imageList or append to the list if it already exists
if (debugMeta.getImages() == null) {
debugMeta.setImages(debugImages);
} else {
debugMeta.getImages().addAll(debugImages);
}
event.setDebugMeta(debugMeta);
}

private @Nullable List<DebugImage> getDebugImages() {
String[] proguardUUIDs = null;
try {
Object proguardUUIDsObject = contextData.get().get(PROGUARD_UUID);
if (proguardUUIDsObject != null) {
proguardUUIDs = (String[]) proguardUUIDsObject;
}
} catch (Exception e) {
logger.log(SentryLevel.ERROR, "Error getting Proguard UUIDs.", e);
return null;
}

if (proguardUUIDs == null || proguardUUIDs.length == 0) {
return null;
}

List<DebugImage> images = new ArrayList<>();

for (String item : proguardUUIDs) {
DebugImage debugImage = new DebugImage();
debugImage.setType("proguard");
debugImage.setUuid(item);
images.add(debugImage);
}

return images;
}

private void setAppExtras(final @NotNull App app) {
app.setAppName(getApplicationName());
app.setAppStartTime(AppStartState.getInstance().getAppStartTime());
Expand Down Expand Up @@ -894,40 +831,6 @@ private void setAppPackageInfo(final @NotNull App app, final @NotNull PackageInf
return null;
}

private @Nullable String[] getProguardUUIDs() {
final AssetManager assets = context.getAssets();
// one may have thousands of asset files and looking up this list might slow down the SDK init.
// quite a bit, for this reason, we try to open the file directly and take care of errors
// like FileNotFoundException
try (final InputStream is =
new BufferedInputStream(assets.open("sentry-debug-meta.properties"))) {
final Properties properties = new Properties();
properties.load(is);

final String uuid = properties.getProperty("io.sentry.ProguardUuids");
if (uuid != null && !uuid.isEmpty()) {
final String[] proguardUUIDs = uuid.split("\\|", -1);

// it should be only 1 proguard uuid, but the API accepts an array so we are keeping it for
// consistency
for (final String item : proguardUUIDs) {
logger.log(SentryLevel.DEBUG, "Proguard UUID found: %s", item);
}
return proguardUUIDs;
}
logger.log(
SentryLevel.INFO, "io.sentry.ProguardUuids property was not found or it is invalid.");
} catch (FileNotFoundException e) {
logger.log(SentryLevel.INFO, "sentry-debug-meta.properties file was not found.");
} catch (IOException e) {
logger.log(SentryLevel.ERROR, "Error getting Proguard UUIDs.", e);
} catch (RuntimeException e) {
logger.log(SentryLevel.ERROR, "sentry-debug-meta.properties file is malformed.", e);
}

return null;
}

@SuppressWarnings("deprecation")
private @Nullable Map<String, String> getSideLoadedInfo() {
String packageName = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ final class ManifestMetadataReader {
static final String TRACING_ORIGINS = "io.sentry.traces.tracing-origins";

static final String ATTACH_THREADS = "io.sentry.attach-threads";
static final String PROGUARD_UUID = "io.sentry.proguard-uuid";

/** ManifestMetadataReader ctor */
private ManifestMetadataReader() {}
Expand Down Expand Up @@ -214,6 +215,9 @@ static void applyMetadata(
options.addTracingOrigin(tracingOrigin);
}
}

options.setProguardUuid(
readString(metadata, logger, PROGUARD_UUID, options.getProguardUuid()));
}

options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,16 @@ class AndroidOptionsInitializerTest {
installation.deleteOnExit()
}

@Test
fun `init should set proguard uuid id on start`() {
val sentryOptions = SentryAndroidOptions()
val mockContext = ContextUtilsTest.mockMetaData(metaData = createBundleWithProguardUuid())

AndroidOptionsInitializer.init(sentryOptions, mockContext)

assertEquals("proguard-uuid", sentryOptions.proguardUuid)
}

@Test
fun `init should set Android transport gate`() {
val sentryOptions = SentryAndroidOptions()
Expand Down Expand Up @@ -327,6 +337,12 @@ class AndroidOptionsInitializerTest {
}
}

private fun createBundleWithProguardUuid(): Bundle {
return Bundle().apply {
putString(ManifestMetadataReader.PROGUARD_UUID, "proguard-uuid")
}
}

private fun createBuildInfo(minApi: Int = 16): IBuildInfoProvider {
val buildInfo = mock<IBuildInfoProvider>()
whenever(buildInfo.sdkInfoVersion).thenReturn(minApi)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@ import io.sentry.SentryTracer
import io.sentry.TransactionContext
import io.sentry.android.core.DefaultAndroidEventProcessor.EMULATOR
import io.sentry.android.core.DefaultAndroidEventProcessor.KERNEL_VERSION
import io.sentry.android.core.DefaultAndroidEventProcessor.PROGUARD_UUID
import io.sentry.android.core.DefaultAndroidEventProcessor.ROOTED
import io.sentry.android.core.DefaultAndroidEventProcessor.SIDE_LOADED
import io.sentry.protocol.DebugImage
import io.sentry.protocol.DebugMeta
import io.sentry.protocol.OperatingSystem
import io.sentry.protocol.SdkVersion
import io.sentry.protocol.SentryThread
Expand Down Expand Up @@ -106,11 +103,6 @@ class DefaultAndroidEventProcessorTest {
assertNotNull(sut.process(SentryEvent(), null)) {
assertNotNull(it.contexts.app)
assertNotNull(it.dist)
assertNotNull(it.debugMeta) { debugMeta ->
assertNotNull(debugMeta.images) { images ->
assertEquals("test", images[0].uuid)
}
}
}
}

Expand All @@ -124,46 +116,6 @@ class DefaultAndroidEventProcessorTest {
}
}

@Test
fun `When debug meta is not null, set the image list`() {
val sut = fixture.getSut(context)
val event = SentryEvent().apply {
debugMeta = DebugMeta()
}

assertNotNull(sut.process(event, null)) {
assertNotNull(it.debugMeta) { debugMeta ->
assertNotNull(debugMeta.images) { images ->
assertEquals("test", images[0].uuid)
}
}
}
}

@Test
fun `When debug meta is not null and image list is not empty, append to the list`() {
val sut = fixture.getSut(context)

val image = DebugImage().apply {
uuid = "abc"
type = "proguard"
}
val event = SentryEvent().apply {
debugMeta = DebugMeta().apply {
images = mutableListOf(image)
}
}

assertNotNull(sut.process(event, null)) {
assertNotNull(it.debugMeta) { debugMeta ->
assertNotNull(debugMeta.images) { images ->
assertEquals("abc", images.first().uuid)
assertEquals("test", images.last().uuid)
}
}
}
}

@Test
fun `Current should be true if it comes from main thread`() {
val sut = fixture.getSut(context)
Expand Down Expand Up @@ -276,7 +228,6 @@ class DefaultAndroidEventProcessorTest {
val contextData = sut.contextData.get()

assertNotNull(contextData)
assertEquals("test", (contextData[PROGUARD_UUID] as Array<*>)[0])
assertNotNull(contextData[ROOTED])
assertNotNull(contextData[KERNEL_VERSION])
assertNotNull(contextData[EMULATOR])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,31 @@ class ManifestMetadataReaderTest {
ManifestMetadataReader.applyMetadata(context, fixture.options)

// Assert
println(fixture.options.tracingOrigins)
assertTrue(fixture.options.tracingOrigins.isEmpty())
}

@Test
fun `applyMetadata reads proguardUuid to options`() {
// Arrange
val bundle = bundleOf(ManifestMetadataReader.PROGUARD_UUID to "proguard-id")
val context = fixture.getContext(metaData = bundle)

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options)

// Assert
assertEquals("proguard-id", fixture.options.proguardUuid)
}

@Test
fun `applyMetadata reads proguardUuid to options and keeps default`() {
// Arrange
val context = fixture.getContext()

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options)

// Assert
assertNull(fixture.options.proguardUuid)
}
}
Loading