diff --git a/CHANGELOG.md b/CHANGELOG.md index 774aa0b630..3b9151ff97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,17 @@ ## Unreleased +### Fixes + +- Fix ANRs when collecting device context ([#4970](https://github.com/getsentry/sentry-java/pull/4970)) + - **IMPORTANT:** This disables collecting external storage size (total/free) by default, to enable it back + use `options.isCollectExternalStorageContext = true` or `` + ### Improvements - Discard envelopes on `4xx` and `5xx` response ([#4950](https://github.com/getsentry/sentry-java/pull/4950)) - This aims to not overwhelm Sentry after an outage or load shedding (including HTTP 429) where too many events are sent at once - ## 8.29.0 ### Fixes diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 08895e6713..eda1eef206 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -336,6 +336,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr public fun isAttachScreenshot ()Z public fun isAttachViewHierarchy ()Z public fun isCollectAdditionalContext ()Z + public fun isCollectExternalStorageContext ()Z public fun isEnableActivityLifecycleBreadcrumbs ()Z public fun isEnableActivityLifecycleTracingAutoFinish ()Z public fun isEnableAppComponentBreadcrumbs ()Z @@ -360,6 +361,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr public fun setBeforeScreenshotCaptureCallback (Lio/sentry/android/core/SentryAndroidOptions$BeforeCaptureCallback;)V public fun setBeforeViewHierarchyCaptureCallback (Lio/sentry/android/core/SentryAndroidOptions$BeforeCaptureCallback;)V public fun setCollectAdditionalContext (Z)V + public fun setCollectExternalStorageContext (Z)V public fun setDebugImagesLoader (Lio/sentry/android/core/IDebugImagesLoader;)V public fun setEnableActivityLifecycleBreadcrumbs (Z)V public fun setEnableActivityLifecycleTracingAutoFinish (Z)V diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/DeviceInfoUtil.java b/sentry-android-core/src/main/java/io/sentry/android/core/DeviceInfoUtil.java index 166f89bbc6..5c06a55810 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/DeviceInfoUtil.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/DeviceInfoUtil.java @@ -9,6 +9,7 @@ import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Build; +import android.os.Environment; import android.os.LocaleList; import android.os.StatFs; import android.os.SystemClock; @@ -148,7 +149,7 @@ public Device collectDeviceInformation( // setting such values require IO hence we don't run for transactions if (collectDeviceIO && options.isCollectAdditionalContext()) { - setDeviceIO(device, collectDynamicData); + setDeviceIO(device, collectDynamicData, options.isCollectExternalStorageContext()); } return device; @@ -195,7 +196,10 @@ public ContextUtils.SplitApksInfo getSplitApksInfo() { return splitApksInfo; } - private void setDeviceIO(final @NotNull Device device, final boolean includeDynamicData) { + private void setDeviceIO( + final @NotNull Device device, + final boolean includeDynamicData, + final boolean includeExternalStorage) { final Intent batteryIntent = getBatteryIntent(); if (batteryIntent != null) { device.setBatteryLevel(getBatteryLevel(batteryIntent, options)); @@ -232,18 +236,21 @@ private void setDeviceIO(final @NotNull Device device, final boolean includeDyna .getRuntimeManager() .runWithRelaxedPolicy( () -> { - final @Nullable File internalStorageFile = context.getExternalFilesDir(null); - if (internalStorageFile != null) { - StatFs internalStorageStat = new StatFs(internalStorageFile.getPath()); + final @Nullable File dataDir = Environment.getDataDirectory(); + if (dataDir != null) { + StatFs internalStorageStat = new StatFs(dataDir.getPath()); device.setStorageSize(getTotalInternalStorage(internalStorageStat)); device.setFreeStorage(getUnusedInternalStorage(internalStorageStat)); } - final @Nullable StatFs externalStorageStat = - getExternalStorageStat(internalStorageFile); - if (externalStorageStat != null) { - device.setExternalStorageSize(getTotalExternalStorage(externalStorageStat)); - device.setExternalFreeStorage(getUnusedExternalStorage(externalStorageStat)); + if (includeExternalStorage) { + final @Nullable File internalStorageFile = context.getExternalFilesDir(null); + final @Nullable StatFs externalStorageStat = + getExternalStorageStat(internalStorageFile); + if (externalStorageStat != null) { + device.setExternalStorageSize(getTotalExternalStorage(externalStorageStat)); + device.setExternalFreeStorage(getUnusedExternalStorage(externalStorageStat)); + } } }); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java index a6392d4895..3acd0a779e 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java @@ -86,6 +86,7 @@ final class ManifestMetadataReader { static final String ATTACH_VIEW_HIERARCHY = "io.sentry.attach-view-hierarchy"; static final String CLIENT_REPORTS_ENABLE = "io.sentry.send-client-reports"; static final String COLLECT_ADDITIONAL_CONTEXT = "io.sentry.additional-context"; + static final String COLLECT_EXTERNAL_STORAGE_CONTEXT = "io.sentry.external-storage-context"; static final String SEND_DEFAULT_PII = "io.sentry.send-default-pii"; @@ -338,6 +339,13 @@ static void applyMetadata( COLLECT_ADDITIONAL_CONTEXT, options.isCollectAdditionalContext())); + options.setCollectExternalStorageContext( + readBool( + metadata, + logger, + COLLECT_EXTERNAL_STORAGE_CONTEXT, + options.isCollectExternalStorageContext())); + if (options.getTracesSampleRate() == null) { final double tracesSampleRate = readDouble(metadata, logger, TRACES_SAMPLE_RATE); if (tracesSampleRate != -1) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index 221495172e..816eb03417 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -121,6 +121,9 @@ public final class SentryAndroidOptions extends SentryOptions { */ private boolean collectAdditionalContext = true; + /** Enables or disables collecting of external storage context. */ + private boolean collectExternalStorageContext = false; + /** * Controls how many seconds to wait for sending events in case there were Startup Crashes in the * previous run. Sentry SDKs normally send events from a background queue, but in the case of @@ -414,6 +417,14 @@ public void setCollectAdditionalContext(boolean collectAdditionalContext) { this.collectAdditionalContext = collectAdditionalContext; } + public boolean isCollectExternalStorageContext() { + return collectExternalStorageContext; + } + + public void setCollectExternalStorageContext(final boolean collectExternalStorageContext) { + this.collectExternalStorageContext = collectExternalStorageContext; + } + public boolean isEnableFramesTracking() { return enableFramesTracking; } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt index ed58f26867..06d284c5dd 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt @@ -1158,6 +1158,31 @@ class ManifestMetadataReaderTest { assertTrue(fixture.options.isCollectAdditionalContext) } + @Test + fun `applyMetadata reads collect external storage to options`() { + // Arrange + val bundle = bundleOf(ManifestMetadataReader.COLLECT_EXTERNAL_STORAGE_CONTEXT to true) + val context = fixture.getContext(metaData = bundle) + + // Act + ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider) + + // Assert + assertTrue(fixture.options.isCollectExternalStorageContext) + } + + @Test + fun `applyMetadata reads collect external storage and keep default value if not found`() { + // Arrange + val context = fixture.getContext() + + // Act + ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider) + + // Assert + assertFalse(fixture.options.isCollectExternalStorageContext) + } + @Test fun `applyMetadata reads send default pii and keep default value if not found`() { // Arrange