Bugsnag 5.12.0

This commit is contained in:
M66B 2021-09-11 20:36:58 +02:00
parent d72090a8b8
commit d2890c6808
12 changed files with 151 additions and 60 deletions

View File

@ -299,7 +299,7 @@ dependencies {
def dnsjava_version = "2.1.9"
def openpgp_version = "12.0"
def badge_version = "1.1.22"
def bugsnag_version = "5.11.0"
def bugsnag_version = "5.12.0"
def biweekly_version = "0.6.6"
def relinker_version = "1.4.3"
def markwon_version = "4.6.2"

View File

@ -20,8 +20,9 @@ internal class AppDataCollector(
private val sessionTracker: SessionTracker,
private val activityManager: ActivityManager?,
private val launchCrashTracker: LaunchCrashTracker,
private val logger: Logger
private val memoryTrimState: MemoryTrimState
) {
var codeBundleId: String? = null
private val packageName: String = appContext.packageName
@ -51,8 +52,10 @@ internal class AppDataCollector(
val map = HashMap<String, Any?>()
map["name"] = appName
map["activeScreen"] = sessionTracker.contextActivity
map["memoryUsage"] = getMemoryUsage()
map["lowMemory"] = isLowMemory()
map["lowMemory"] = memoryTrimState.isLowMemory
map["memoryTrimLevel"] = memoryTrimState.trimLevelDescription
populateRuntimeMemoryMetadata(map)
bgWorkRestricted?.let {
map["backgroundWorkRestricted"] = bgWorkRestricted
@ -63,13 +66,14 @@ internal class AppDataCollector(
return map
}
/**
* Get the actual memory used by the VM (which may not be the total used
* by the app in the case of NDK usage).
*/
private fun getMemoryUsage(): Long {
private fun populateRuntimeMemoryMetadata(map: MutableMap<String, Any?>) {
val runtime = Runtime.getRuntime()
return runtime.totalMemory() - runtime.freeMemory()
val totalMemory = runtime.totalMemory()
val freeMemory = runtime.freeMemory()
map["memoryUsage"] = totalMemory - freeMemory
map["totalMemory"] = totalMemory
map["freeMemory"] = freeMemory
map["memoryLimit"] = runtime.maxMemory()
}
/**
@ -86,22 +90,6 @@ internal class AppDataCollector(
}
}
/**
* Check if the device is currently running low on memory.
*/
private fun isLowMemory(): Boolean? {
try {
if (activityManager != null) {
val memInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memInfo)
return memInfo.lowMemory
}
} catch (exception: Exception) {
logger.w("Could not check lowMemory status")
}
return null
}
fun setBinaryArch(binaryArch: String) {
this.binaryArch = binaryArch
}

View File

@ -62,6 +62,9 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
@NonNull
final BreadcrumbState breadcrumbState;
@NonNull
final MemoryTrimState memoryTrimState = new MemoryTrimState();
@NonNull
protected final EventStore eventStore;
@ -162,7 +165,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
DataCollectionModule dataCollectionModule = new DataCollectionModule(contextModule,
configModule, systemServiceModule, trackerModule,
bgTaskService, connectivity, storageModule.getDeviceId());
bgTaskService, connectivity, storageModule.getDeviceId(), memoryTrimState);
dataCollectionModule.resolveDependencies(bgTaskService, TaskType.IO);
appDataCollector = dataCollectionModule.getAppDataCollector();
deviceDataCollector = dataCollectionModule.getDeviceDataCollector();
@ -337,10 +340,21 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
clientObservable.postOrientationChange(newOrientation);
return null;
}
}, new Function1<Boolean, Unit>() {
}, new Function2<Boolean, Integer, Unit>() {
@Override
public Unit invoke(Boolean isLowMemory) {
clientObservable.postMemoryTrimEvent(isLowMemory);
public Unit invoke(Boolean isLowMemory, Integer memoryTrimLevel) {
memoryTrimState.setLowMemory(Boolean.TRUE.equals(isLowMemory));
if (memoryTrimState.updateMemoryTrimLevel(memoryTrimLevel)) {
leaveAutoBreadcrumb(
"Trim Memory",
BreadcrumbType.STATE,
Collections.<String, Object>singletonMap(
"trimLevel", memoryTrimState.getTrimLevelDescription()
)
);
}
memoryTrimState.emitObservableEvent();
return null;
}
}
@ -383,6 +397,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
contextState.addObserver(observer);
deliveryDelegate.addObserver(observer);
launchCrashTracker.addObserver(observer);
memoryTrimState.addObserver(observer);
}
void removeObserver(StateObserver observer) {
@ -394,6 +409,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
contextState.removeObserver(observer);
deliveryDelegate.removeObserver(observer);
launchCrashTracker.removeObserver(observer);
memoryTrimState.removeObserver(observer);
}
/**
@ -403,6 +419,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
metadataState.emitObservableEvent();
contextState.emitObservableEvent();
userState.emitObservableEvent();
memoryTrimState.emitObservableEvent();
}
/**

View File

@ -1,13 +1,13 @@
package com.bugsnag.android
import android.content.ComponentCallbacks
import android.content.ComponentCallbacks2
import android.content.res.Configuration
internal class ClientComponentCallbacks(
private val deviceDataCollector: DeviceDataCollector,
private val cb: (oldOrientation: String?, newOrientation: String?) -> Unit,
val callback: (Boolean) -> Unit
) : ComponentCallbacks {
val memoryCallback: (Boolean, Int?) -> Unit
) : ComponentCallbacks2 {
override fun onConfigurationChanged(newConfig: Configuration) {
val oldOrientation = deviceDataCollector.getOrientationAsString()
@ -18,7 +18,11 @@ internal class ClientComponentCallbacks(
}
}
override fun onTrimMemory(level: Int) {
memoryCallback(level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE, level)
}
override fun onLowMemory() {
callback(true)
memoryCallback(true, null)
}
}

View File

@ -8,10 +8,6 @@ internal class ClientObservable : BaseObservable() {
updateState { StateEvent.UpdateOrientation(orientation) }
}
fun postMemoryTrimEvent(isLowMemory: Boolean) {
updateState { StateEvent.UpdateMemoryTrimEvent(isLowMemory) }
}
fun postNdkInstall(
conf: ImmutableConfig,
lastRunInfoPath: String,

View File

@ -17,7 +17,8 @@ internal class DataCollectionModule(
trackerModule: TrackerModule,
bgTaskService: BackgroundTaskService,
connectivity: Connectivity,
deviceId: String?
deviceId: String?,
memoryTrimState: MemoryTrimState
) : DependencyModule() {
private val ctx = contextModule.ctx
@ -34,7 +35,7 @@ internal class DataCollectionModule(
trackerModule.sessionTracker,
systemServiceModule.activityManager,
trackerModule.launchCrashTracker,
logger
memoryTrimState
)
}

View File

@ -66,7 +66,7 @@ internal class DefaultDelivery(
return DeliveryStatus.UNDELIVERED
} catch (exception: IOException) {
logger.w("IOException encountered in request", exception)
return DeliveryStatus.UNDELIVERED
return DeliveryStatus.FAILURE
} catch (exception: Exception) {
logger.w("Unexpected error delivering payload", exception)
return DeliveryStatus.FAILURE

View File

@ -1,6 +1,7 @@
package com.bugsnag.android
import android.annotation.SuppressLint
import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
@ -8,6 +9,7 @@ import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.content.res.Resources
import android.os.BatteryManager
import android.os.Build
import android.provider.Settings
import java.io.File
import java.util.Date
@ -19,6 +21,7 @@ import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.atomic.AtomicInteger
import kotlin.math.max
import kotlin.math.min
import android.os.Process as AndroidProcess
internal class DeviceDataCollector(
private val connectivity: Connectivity,
@ -41,6 +44,7 @@ internal class DeviceDataCollector(
private val cpuAbi = getCpuAbi()
private val runtimeVersions: MutableMap<String, Any>
private val rootedFuture: Future<Boolean>?
private val totalMemoryFuture: Future<Long?>? = retrieveTotalDeviceMemory()
private var orientation = AtomicInteger(resources.configuration.orientation)
init {
@ -68,7 +72,7 @@ internal class DeviceDataCollector(
checkIsRooted(),
deviceId,
locale,
calculateTotalMemory(),
totalMemoryFuture.runCatching { this?.get() }.getOrNull(),
runtimeVersions.toMutableMap()
)
@ -77,7 +81,7 @@ internal class DeviceDataCollector(
checkIsRooted(),
deviceId,
locale,
calculateTotalMemory(),
totalMemoryFuture.runCatching { this?.get() }.getOrNull(),
runtimeVersions.toMutableMap(),
calculateFreeDisk(),
calculateFreeMemory(),
@ -216,31 +220,60 @@ internal class DeviceDataCollector(
}
/**
* Get the amount of memory remaining that the VM can allocate
* Get the amount of memory remaining on the device
*/
private fun calculateFreeMemory(): Long {
val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory()
private fun calculateFreeMemory(): Long? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
val freeMemory = appContext.getActivityManager()
?.let { am -> ActivityManager.MemoryInfo().also { am.getMemoryInfo(it) } }
?.availMem
return if (maxMemory != Long.MAX_VALUE) {
maxMemory - runtime.totalMemory() + runtime.freeMemory()
} else {
runtime.freeMemory()
if (freeMemory != null) {
return freeMemory
}
}
return runCatching {
@Suppress("PrivateApi")
AndroidProcess::class.java.getDeclaredMethod("getFreeMemory").invoke(null) as Long?
}.getOrNull()
}
/**
* Get the total memory available on the current Android device, in bytes
* Attempt to retrieve the total amount of memory available on the device
*/
private fun calculateTotalMemory(): Long {
val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory()
return when {
maxMemory != Long.MAX_VALUE -> maxMemory
else -> runtime.totalMemory()
private fun retrieveTotalDeviceMemory(): Future<Long?>? {
return try {
bgTaskService.submitTask(
TaskType.DEFAULT,
Callable {
calculateTotalMemory()
}
)
} catch (exc: RejectedExecutionException) {
logger.w("Failed to lookup available device memory", exc)
null
}
}
private fun calculateTotalMemory(): Long? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
val totalMemory = appContext.getActivityManager()
?.let { am -> ActivityManager.MemoryInfo().also { am.getMemoryInfo(it) } }
?.totalMem
if (totalMemory != null) {
return totalMemory
}
}
// we try falling back to a reflective API
return runCatching {
@Suppress("PrivateApi")
AndroidProcess::class.java.getDeclaredMethod("getTotalMemory").invoke(null) as Long?
}.getOrNull()
}
/**
* Get the current device orientation, eg. "landscape"
*/

View File

@ -0,0 +1,41 @@
package com.bugsnag.android
import android.content.ComponentCallbacks2
internal class MemoryTrimState : BaseObservable() {
var isLowMemory: Boolean = false
var memoryTrimLevel: Int? = null
val trimLevelDescription: String get() = descriptionFor(memoryTrimLevel)
fun updateMemoryTrimLevel(newTrimLevel: Int?): Boolean {
if (memoryTrimLevel == newTrimLevel) {
return false
}
memoryTrimLevel = newTrimLevel
return true
}
fun emitObservableEvent() {
updateState {
StateEvent.UpdateMemoryTrimEvent(
isLowMemory,
memoryTrimLevel,
trimLevelDescription
)
}
}
private fun descriptionFor(memoryTrimLevel: Int?) = when (memoryTrimLevel) {
null -> "None"
ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> "Complete"
ComponentCallbacks2.TRIM_MEMORY_MODERATE -> "Moderate"
ComponentCallbacks2.TRIM_MEMORY_BACKGROUND -> "Background"
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> "UI hidden"
ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> "Running critical"
ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> "Running low"
ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE -> "Running moderate"
else -> "Unknown ($memoryTrimLevel)"
}
}

View File

@ -429,4 +429,11 @@ public class NativeInterface {
public static void setAutoDetectAnrs(boolean autoDetectAnrs) {
getClient().setAutoDetectAnrs(autoDetectAnrs);
}
/**
* Marks the launch period as complete
*/
public static void markLaunchCompleted() {
getClient().markLaunchCompleted();
}
}

View File

@ -7,7 +7,7 @@ import java.io.IOException
*/
class Notifier @JvmOverloads constructor(
var name: String = "Android Bugsnag Notifier",
var version: String = "5.11.0",
var version: String = "5.12.0",
var url: String = "https://bugsnag.com"
) : JsonStream.Streamable {

View File

@ -62,5 +62,9 @@ sealed class StateEvent { // JvmField allows direct field access optimizations
class UpdateUser(@JvmField val user: User) : StateEvent()
class UpdateMemoryTrimEvent(@JvmField val isLowMemory: Boolean) : StateEvent()
class UpdateMemoryTrimEvent(
@JvmField val isLowMemory: Boolean,
@JvmField val memoryTrimLevel: Int? = null,
@JvmField val memoryTrimLevelDescription: String = "None"
) : StateEvent()
}