mirror of https://github.com/M66B/FairEmail.git
Bugsnag 5.12.0
This commit is contained in:
parent
d72090a8b8
commit
d2890c6808
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
*/
|
||||
|
|
|
@ -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)"
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue