diff --git a/CHANGELOG.md b/CHANGELOG.md index 332bb31773..1577b9ca09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,13 @@ - Allow setting SDK info (name & version) in manifest ([#2016](https://github.com/getsentry/sentry-java/pull/2016)) - Allow setting native Android SDK name during build ([#2035](https://github.com/getsentry/sentry-java/pull/2035)) +- Include application permissions in Android events ([#2018](https://github.com/getsentry/sentry-java/pull/2018)) - Automatically create transactions for UI events ([#1975](https://github.com/getsentry/sentry-java/pull/1975)) ### Changed - Update sentry-native to 0.4.17 ([#2033](https://github.com/getsentry/sentry-java/pull/2033)) +- Update Gradle to 7.4.2 and AGP to 7.2 ([#2042](https://github.com/getsentry/sentry-java/pull/2042)) ## 6.0.0-beta.3 diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 29adc89d42..27bb77b26c 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -8,7 +8,7 @@ object Config { val kotlinCompatibleLanguageVersion = "1.4" object BuildPlugins { - val androidGradle = "com.android.tools.build:gradle:7.1.2" + val androidGradle = "com.android.tools.build:gradle:7.2.0" val kotlinGradlePlugin = "gradle-plugin" val buildConfig = "com.github.gmazzo.buildconfig" val buildConfigVersion = "3.0.3" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2a..41d9927a4d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb87909..aa991fceae 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ContextUtils.java b/sentry-android-core/src/main/java/io/sentry/android/core/ContextUtils.java index 7e8b0c18b8..30d828013b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ContextUtils.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ContextUtils.java @@ -19,8 +19,19 @@ private ContextUtils() {} */ @Nullable static PackageInfo getPackageInfo(final @NotNull Context context, final @NotNull ILogger logger) { + return getPackageInfo(context, 0, logger); + } + + /** + * Return the Application's PackageInfo with the specified flags if possible, or null. + * + * @return the Application's PackageInfo if possible, or null + */ + @Nullable + static PackageInfo getPackageInfo( + final @NotNull Context context, final int flags, final @NotNull ILogger logger) { try { - return context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + return context.getPackageManager().getPackageInfo(context.getPackageName(), flags); } catch (Throwable e) { logger.log(SentryLevel.ERROR, "Error getting package info.", e); return null; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java index 469ea2f837..cbfcbef377 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java @@ -1,8 +1,10 @@ package io.sentry.android.core; import static android.content.Context.ACTIVITY_SERVICE; +import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.os.BatteryManager.EXTRA_TEMPERATURE; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; @@ -215,7 +217,8 @@ private void setThreads(final @NotNull SentryEvent event) { } private void setPackageInfo(final @NotNull SentryBaseEvent event, final @NotNull App app) { - final PackageInfo packageInfo = ContextUtils.getPackageInfo(context, logger); + final PackageInfo packageInfo = + ContextUtils.getPackageInfo(context, PackageManager.GET_PERMISSIONS, logger); if (packageInfo != null) { String versionCode = ContextUtils.getVersionCode(packageInfo); @@ -727,10 +730,33 @@ private boolean isExternalStorageMounted() { return os; } + @SuppressLint("NewApi") // we perform an if-check for that, but lint fails to recognize private void setAppPackageInfo(final @NotNull App app, final @NotNull PackageInfo packageInfo) { app.setAppIdentifier(packageInfo.packageName); app.setAppVersion(packageInfo.versionName); app.setAppBuild(ContextUtils.getVersionCode(packageInfo)); + + if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.JELLY_BEAN) { + final Map permissions = new HashMap<>(); + final String[] requestedPermissions = packageInfo.requestedPermissions; + final int[] requestedPermissionsFlags = packageInfo.requestedPermissionsFlags; + + if (requestedPermissions != null + && requestedPermissions.length > 0 + && requestedPermissionsFlags != null + && requestedPermissionsFlags.length > 0) { + for (int i = 0; i < requestedPermissions.length; i++) { + String permission = requestedPermissions[i]; + permission = permission.substring(permission.lastIndexOf('.') + 1); + + final boolean granted = + (requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) + == REQUESTED_PERMISSION_GRANTED; + permissions.put(permission, granted ? "granted" : "not_granted"); + } + } + app.setPermissions(permissions); + } } /** diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt index 0a76a6ade5..8812d088da 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt @@ -1,6 +1,7 @@ package io.sentry.android.core import android.content.Context +import android.os.Build import android.os.Looper import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -9,6 +10,7 @@ import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.verify +import com.nhaarman.mockitokotlin2.whenever import io.sentry.DiagnosticLogger import io.sentry.ILogger import io.sentry.SentryEvent @@ -44,7 +46,8 @@ class DefaultAndroidEventProcessorTest { private lateinit var context: Context private val className = "io.sentry.android.core.DefaultAndroidEventProcessor" - private val ctorTypes = arrayOf(Context::class.java, ILogger::class.java, BuildInfoProvider::class.java) + private val ctorTypes = + arrayOf(Context::class.java, ILogger::class.java, BuildInfoProvider::class.java) init { Locale.setDefault(Locale.US) @@ -104,11 +107,28 @@ class DefaultAndroidEventProcessorTest { @Test fun `When Event and hint is not Cached, data should be applied`() { + whenever(fixture.buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.M) val sut = fixture.getSut(context) assertNotNull(sut.process(SentryEvent(), null)) { assertNotNull(it.contexts.app) assertNotNull(it.dist) + + // assert adds permissions as unknown + val permissions = it.contexts.app!!.permissions + assertNotNull(permissions) + } + } + + @Test + fun `when Android version is below JELLY_BEAN, does not add permissions`() { + whenever(fixture.buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + val sut = fixture.getSut(context) + + assertNotNull(sut.process(SentryEvent(), null)) { + // assert adds permissions + val unknown = it.contexts.app!!.permissions + assertNull(unknown) } } @@ -252,17 +272,24 @@ class DefaultAndroidEventProcessorTest { sut.process(SentryEvent(), null) - verify((fixture.options.logger as DiagnosticLogger).logger, never())!!.log(eq(SentryLevel.ERROR), any(), any()) + verify( + (fixture.options.logger as DiagnosticLogger).logger, + never() + )!!.log(eq(SentryLevel.ERROR), any(), any()) } @Test fun `Processor won't throw exception when theres a hint`() { - val processor = DefaultAndroidEventProcessor(context, fixture.options.logger, fixture.buildInfo, mock()) + val processor = + DefaultAndroidEventProcessor(context, fixture.options.logger, fixture.buildInfo, mock()) val hintsMap = mutableMapOf(SENTRY_TYPE_CHECK_HINT to CachedEvent()) processor.process(SentryEvent(), hintsMap) - verify((fixture.options.logger as DiagnosticLogger).logger, never())!!.log(eq(SentryLevel.ERROR), any(), any()) + verify( + (fixture.options.logger as DiagnosticLogger).logger, + never() + )!!.log(eq(SentryLevel.ERROR), any(), any()) } @Test diff --git a/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml b/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml index ac39ccecc8..ee4b525765 100644 --- a/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml +++ b/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml @@ -10,6 +10,10 @@ + + + + @@ -45,6 +49,10 @@ android:name=".GesturesActivity" android:exported="false" /> + + diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java index 9a030a8591..5379ec4faa 100644 --- a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java @@ -178,6 +178,11 @@ protected void onCreate(Bundle savedInstanceState) { crashCount); }); + binding.openPermissionsActivity.setOnClickListener( + view -> { + startActivity(new Intent(this, PermissionsActivity.class)); + }); + setContentView(binding.getRoot()); } diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/PermissionsActivity.kt b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/PermissionsActivity.kt new file mode 100644 index 0000000000..dce7af4771 --- /dev/null +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/PermissionsActivity.kt @@ -0,0 +1,41 @@ +package io.sentry.samples.android + +import android.Manifest +import android.os.Bundle +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts.RequestPermission +import androidx.appcompat.app.AppCompatActivity +import io.sentry.samples.android.databinding.ActivityPermissionsBinding + +class PermissionsActivity : AppCompatActivity() { + + private lateinit var binding: ActivityPermissionsBinding + private val requestPermissionLauncher = + registerForActivityResult(RequestPermission()) { isGranted: Boolean -> + if (isGranted) { + Toast.makeText(this, "The permission was granted", Toast.LENGTH_LONG).show() + } else { + Toast.makeText(this, "The permission was denied", Toast.LENGTH_LONG).show() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityPermissionsBinding.inflate(layoutInflater) + + binding.cameraPermission.setOnClickListener { + requestPermissionLauncher.launch(Manifest.permission.CAMERA) + } + + binding.writePermission.setOnClickListener { + requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) + } + + binding.permissionsCrash.setOnClickListener { + throw RuntimeException("Permissions Activity Exception") + } + + setContentView(binding.root) + } +} diff --git a/sentry-samples/sentry-samples-android/src/main/res/layout/activity_main.xml b/sentry-samples/sentry-samples-android/src/main/res/layout/activity_main.xml index 411054c47f..3f0549fdf8 100644 --- a/sentry-samples/sentry-samples-android/src/main/res/layout/activity_main.xml +++ b/sentry-samples/sentry-samples-android/src/main/res/layout/activity_main.xml @@ -105,6 +105,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/test_timber_integration"/> + +