LeakCanaryProcess.kt
TLDR
This file contains the LeakCanaryProcess
object, which provides functionality to determine whether the current process is the LeakCanary analyzer process.
Methods
isInAnalyzerProcess(context: Context): Boolean
Checks whether the current process is the process running the heap analyzer, which is a different process than the normal app process. Returns true
if the process is the analyzer process, false
otherwise.
isInServiceProcess(context: Context, serviceClass: Class<out Service>): Boolean
Checks whether the provided service class is running in the same process as the current process. Returns true
if the service class is running in the same process, false
otherwise.
Classes
No classes found
package leakcanary
import android.app.ActivityManager
import android.app.Service
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import leakcanary.internal.RemoteLeakCanaryWorkerService
import shark.SharkLog
/**
* Used to determine whether the current process is the LeakCanary analyzer process. By depending
* on the `leakcanary-android-process` artifact instead of the `leakcanary-android`, LeakCanary
* will automatically run its analysis in a separate process.
*
* As such, you'll need to be careful to do any custom configuration of LeakCanary in both the main
* process and the analyzer process.
*/
object LeakCanaryProcess {
@Volatile private var isInAnalyzerProcess: Boolean? = null
/**
* Whether the current process is the process running the heap analyzer, which is
* a different process than the normal app process.
*/
fun isInAnalyzerProcess(context: Context): Boolean {
var isInAnalyzerProcess: Boolean? = isInAnalyzerProcess
// This only needs to be computed once per process.
if (isInAnalyzerProcess == null) {
isInAnalyzerProcess = isInServiceProcess(context, RemoteLeakCanaryWorkerService::class.java)
this.isInAnalyzerProcess = isInAnalyzerProcess
}
return isInAnalyzerProcess
}
@Suppress("ReturnCount")
private fun isInServiceProcess(
context: Context,
serviceClass: Class<out Service>
): Boolean {
val packageManager = context.packageManager
val packageInfo: PackageInfo
try {
packageInfo = packageManager.getPackageInfo(context.packageName, PackageManager.GET_SERVICES)
} catch (e: Exception) {
SharkLog.d(e) { "Could not get package info for ${context.packageName}" }
return false
}
val mainProcess = packageInfo.applicationInfo.processName
val component = ComponentName(context, serviceClass)
val serviceInfo: ServiceInfo
try {
serviceInfo =
packageManager.getServiceInfo(component, PackageManager.GET_DISABLED_COMPONENTS)
} catch (ignored: PackageManager.NameNotFoundException) {
// Service is disabled.
return false
}
if (serviceInfo.processName == null) {
SharkLog.d { "Did not expect service $serviceClass to have a null process name" }
return false
} else if (serviceInfo.processName == mainProcess) {
SharkLog.d { "Did not expect service $serviceClass to run in main process $mainProcess" }
// Technically we are in the service process, but we're not in the service dedicated process.
return false
}
val myPid = android.os.Process.myPid()
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
var myProcess: ActivityManager.RunningAppProcessInfo? = null
val runningProcesses: List<ActivityManager.RunningAppProcessInfo>?
try {
runningProcesses = activityManager.runningAppProcesses
} catch (exception: SecurityException) {
// https://github.com/square/leakcanary/issues/948
SharkLog.d { "Could not get running app processes $exception" }
return false
}
if (runningProcesses != null) {
for (process in runningProcesses) {
if (process.pid == myPid) {
myProcess = process
break
}
}
}
if (myProcess == null) {
SharkLog.d { "Could not find running process for $myPid" }
return false
}
return myProcess.processName == serviceInfo.processName
}
}