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

Process importance in app metadata #1973

Merged
2 commits merged into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bugsnag-android-core/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>CyclomaticComplexMethod:AppDataCollector.kt$AppDataCollector$@SuppressLint("SwitchIntDef") @Suppress("DEPRECATION") private fun getProcessImportance(): String?</ID>
<ID>CyclomaticComplexMethod:ConfigInternal.kt$ConfigInternal$fun getConfigDifferences(): Map&lt;String, Any></ID>
<ID>ImplicitDefaultLocale:DeliveryHeaders.kt$String.format("%02x", byte)</ID>
<ID>LongParameterList:App.kt$App$( /** * The architecture of the running application binary */ var binaryArch: String?, /** * The package name of the application */ var id: String?, /** * The release stage set in [Configuration.releaseStage] */ var releaseStage: String?, /** * The version of the application set in [Configuration.version] */ var version: String?, /** The revision ID from the manifest (React Native apps only) */ var codeBundleId: String?, /** * The unique identifier for the build of the application set in [Configuration.buildUuid] */ var buildUuid: String?, /** * The application type set in [Configuration#version] */ var type: String?, /** * The version code of the application set in [Configuration.versionCode] */ var versionCode: Number? )</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.app.ActivityManager
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Process
import androidx.test.core.app.ApplicationProvider
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
Expand Down Expand Up @@ -126,4 +127,84 @@ class AppDataCollectorTest {
val result = collector.getInstallerPackageName()
assertEquals("Test Installer name", result)
}

@Test
fun testGetProcessImportanceWithVersion29() = withBuildSdkInt(Build.VERSION_CODES.Q) {
val packageManager = mock(PackageManager::class.java)
`when`(packageManager.getApplicationLabel(any())).thenReturn("Test App name")
`when`(am.runningAppProcesses).thenReturn(
listOf(
ActivityManager.RunningAppProcessInfo().apply {
importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
}
)
)

val collector = AppDataCollector(
context,
packageManager,
client.immutableConfig,
client.sessionTracker,
am,
client.launchCrashTracker,
client.memoryTrimState
)

val result = collector.getAppDataMetadata()["processImportance"]
assertEquals("foreground service", result)
}

@Test
fun testGetProcessImportanceWithVersion14() = withBuildSdkInt(Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
val packageManager = mock(PackageManager::class.java)
`when`(packageManager.getApplicationLabel(any())).thenReturn("Test App name")
`when`(am.runningAppProcesses).thenReturn(
listOf(
ActivityManager.RunningAppProcessInfo().apply {
importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
pid = Process.myPid()
}
)
)

val collector = AppDataCollector(
context,
packageManager,
client.immutableConfig,
client.sessionTracker,
am,
client.launchCrashTracker,
client.memoryTrimState
)

val result = collector.getAppDataMetadata()["processImportance"]
assertEquals("foreground", result)
}

@Test
fun testGetProcessImportanceWithPid0() = withBuildSdkInt(Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
val packageManager = mock(PackageManager::class.java)
`when`(packageManager.getApplicationLabel(any())).thenReturn("Test App name")
`when`(am.runningAppProcesses).thenReturn(
listOf(
ActivityManager.RunningAppProcessInfo().apply {
importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
pid = 0
}
)
)

val collector = AppDataCollector(
context,
packageManager,
client.immutableConfig,
client.sessionTracker,
am,
client.launchCrashTracker,
client.memoryTrimState
)

val result = collector.getAppDataMetadata()["processImportance"]
assertEquals(null, result)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public void testAppDataCollection() {
public void testAppDataMetadata() {
client = generateClient();
Map<String, Object> app = client.getAppDataCollector().getAppDataMetadata();
assertEquals(10, app.size());
assertEquals(11, app.size());
assertEquals("Bugsnag Android Tests", app.get("name"));
assertEquals("com.bugsnag.android.core.test", app.get("processName"));
assertNotNull(app.get("memoryUsage"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,26 @@ package com.bugsnag.android

import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING_PRE_28
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE
import android.app.Application
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.Process
import android.os.SystemClock
import com.bugsnag.android.internal.ImmutableConfig

Expand Down Expand Up @@ -49,12 +64,57 @@ internal class AppDataCollector(
)
}

@SuppressLint("SwitchIntDef")
@Suppress("DEPRECATION")
private fun getProcessImportance(): String? {
try {
val appInfo = ActivityManager.RunningAppProcessInfo()
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
ActivityManager.getMyMemoryState(appInfo)
} else {
val expectedPid = Process.myPid()
activityManager?.runningAppProcesses
?.find { it.pid == expectedPid }
?.let {
appInfo.importance = it.importance
appInfo.pid = expectedPid
}
}

if (appInfo.pid == 0) {
return null
}

return when (appInfo.importance) {
IMPORTANCE_FOREGROUND -> "foreground"
IMPORTANCE_FOREGROUND_SERVICE -> "foreground service"
IMPORTANCE_TOP_SLEEPING -> "top sleeping"
IMPORTANCE_TOP_SLEEPING_PRE_28 -> "top sleeping"
IMPORTANCE_VISIBLE -> "visible"
IMPORTANCE_PERCEPTIBLE -> "perceptible"
IMPORTANCE_PERCEPTIBLE_PRE_26 -> "perceptible"
IMPORTANCE_CANT_SAVE_STATE -> "can't save state"
IMPORTANCE_CANT_SAVE_STATE_PRE_26 -> "can't save state"
IMPORTANCE_SERVICE -> "service"
IMPORTANCE_CACHED -> "cached/background"
IMPORTANCE_GONE -> "gone"
IMPORTANCE_EMPTY -> "empty"
REASON_PROVIDER_IN_USE -> "provider in use"
REASON_SERVICE_IN_USE -> "service in use"
else -> "unknown importance (${appInfo.importance})"
}
} catch (e: Exception) {
return null
}
}

fun getAppDataMetadata(): MutableMap<String, Any?> {
val map = HashMap<String, Any?>()
map["name"] = appName
map["activeScreen"] = sessionTracker.contextActivity
map["lowMemory"] = memoryTrimState.isLowMemory
map["memoryTrimLevel"] = memoryTrimState.trimLevelDescription
map["processImportance"] = getProcessImportance()

populateRuntimeMemoryMetadata(map)

Expand Down Expand Up @@ -128,6 +188,7 @@ internal class AppDataCollector(
packageManager != null && copy != null -> {
packageManager.getApplicationLabel(copy).toString()
}

else -> null
}
}
Expand Down Expand Up @@ -156,6 +217,7 @@ internal class AppDataCollector(
VERSION.SDK_INT >= VERSION_CODES.P -> {
Application.getProcessName()
}

else -> {
// see https://stackoverflow.com/questions/19631894
val clz = Class.forName("android.app.ActivityThread")
Expand All @@ -179,5 +241,7 @@ internal class AppDataCollector(
* good approximation for how long the app has been running.
*/
fun getDurationMs(): Long = SystemClock.elapsedRealtime() - startTimeMs

private const val IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170
}
}
1 change: 1 addition & 0 deletions bugsnag-plugin-android-exitinfo/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$@SuppressLint("SwitchIntDef") @Suppress("DEPRECATION") private fun importanceDescriptionOf(exitInfo: ApplicationExitInfo)</ID>
<ID>CyclomaticComplexMethod:ExitInfoCallback.kt$ExitInfoCallback$private fun exitReasonOf(exitInfo: ApplicationExitInfo)</ID>
<ID>LongParameterList:TombstoneParser.kt$TombstoneParser$( exitInfo: ApplicationExitInfo, listOpenFds: Boolean, includeLogcat: Boolean, threadConsumer: (BugsnagThread) -> Unit, fileDescriptorConsumer: (Int, String, String) -> Unit, logcatConsumer: (String) -> Unit )</ID>
<ID>MagicNumber:TraceParser.kt$TraceParser$16</ID>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
package com.bugsnag.android

import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE_PRE_26
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING_PRE_28
import android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE
import android.app.ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE
import android.app.ApplicationExitInfo
import android.content.Context
import android.os.Build
Expand Down Expand Up @@ -62,16 +77,24 @@ internal class ExitInfoCallback(
else -> "unknown reason (${exitInfo.reason})"
}

@SuppressLint("SwitchIntDef")
@Suppress("DEPRECATION")
private fun importanceDescriptionOf(exitInfo: ApplicationExitInfo) = when (exitInfo.importance) {
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND -> "foreground"
ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE -> "foreground service"
ActivityManager.RunningAppProcessInfo.IMPORTANCE_TOP_SLEEPING -> "top sleeping"
ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE -> "visible"
ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE -> "perceptible"
ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE -> "can't save state"
ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE -> "service"
ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED -> "cached"
ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE -> "gone"
IMPORTANCE_FOREGROUND -> "foreground"
IMPORTANCE_FOREGROUND_SERVICE -> "foreground service"
IMPORTANCE_TOP_SLEEPING -> "top sleeping"
IMPORTANCE_TOP_SLEEPING_PRE_28 -> "top sleeping"
IMPORTANCE_VISIBLE -> "visible"
IMPORTANCE_PERCEPTIBLE -> "perceptible"
IMPORTANCE_PERCEPTIBLE_PRE_26 -> "perceptible"
IMPORTANCE_CANT_SAVE_STATE -> "can't save state"
IMPORTANCE_CANT_SAVE_STATE_PRE_26 -> "can't save state"
IMPORTANCE_SERVICE -> "service"
IMPORTANCE_CACHED -> "cached/background"
IMPORTANCE_GONE -> "gone"
IMPORTANCE_EMPTY -> "empty"
REASON_PROVIDER_IN_USE -> "provider in use"
REASON_SERVICE_IN_USE -> "service in use"
else -> "unknown importance (${exitInfo.importance})"
}

Expand All @@ -86,6 +109,7 @@ internal class ExitInfoCallback(
allExitInfo.find { it.pid == pid }

internal companion object {
const val MAX_EXIT_INFO = 100
private const val MAX_EXIT_INFO = 100
private const val IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170
}
}
Loading