86 lines
3.0 KiB
Kotlin
86 lines
3.0 KiB
Kotlin
package co.electriccoin.zcash.spackle.process
|
|
|
|
import android.app.ActivityManager
|
|
import android.app.Application
|
|
import android.content.Context
|
|
import android.os.Build
|
|
import android.os.Process
|
|
import androidx.annotation.RequiresApi
|
|
import androidx.annotation.VisibleForTesting
|
|
import co.electriccoin.zcash.spackle.AndroidApiVersion
|
|
import co.electriccoin.zcash.spackle.process.ProcessNameCompat.getProcessName
|
|
|
|
/**
|
|
* Provides a reliable way of determining Android process name. For highest reliability and performance,
|
|
* [getProcessName] should only be called once the start of the Application.onCreate() callback has occurred
|
|
* which will mean that the process name content provider has been initialized.
|
|
*
|
|
* Note that if you wish to add another process, consider adding an empty subclass of
|
|
* [AbstractProcessNameContentProvider] in that process, as the ContentProvider has a more reliable
|
|
* way to get process name on older Android versions.
|
|
*/
|
|
object ProcessNameCompat {
|
|
|
|
// GuardedBy intrinsicLock
|
|
private var processName: String? = null
|
|
|
|
private val intrinsicLock = Any()
|
|
|
|
fun getProcessName(context: Context): String {
|
|
synchronized(intrinsicLock) {
|
|
processName?.let {
|
|
return it
|
|
}
|
|
|
|
val foundProcessName = searchForProcessName(context)
|
|
if (null == foundProcessName) {
|
|
// This should be exceedingly rare
|
|
error("Unable to determine process name")
|
|
} else {
|
|
processName = foundProcessName
|
|
return foundProcessName
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Not a public API; should only be called by [AbstractProcessNameContentProvider].
|
|
*/
|
|
internal fun setProcessName(newProcessName: String) {
|
|
processName = newProcessName
|
|
}
|
|
|
|
/**
|
|
* @param context Application context.
|
|
* @return Name of the current process. May return null if a failure occurs, which is possible
|
|
* due to some race conditions in Android.
|
|
*/
|
|
private fun searchForProcessName(context: Context): String? {
|
|
return if (AndroidApiVersion.isAtLeastT) {
|
|
getProcessNameTPlus()
|
|
} else if (AndroidApiVersion.isAtLeastP) {
|
|
getProcessNamePPlus()
|
|
} else {
|
|
searchForProcessNameLegacy(context)
|
|
}
|
|
}
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
|
|
private fun getProcessNameTPlus() = Process.myProcessName()
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.P)
|
|
private fun getProcessNamePPlus() = Application.getProcessName()
|
|
|
|
/**
|
|
* @param context Application context.
|
|
* @return Name of the current process. May return null if a failure occurs, which is possible
|
|
* due to some race conditions in older versions of Android.
|
|
*/
|
|
@VisibleForTesting
|
|
internal fun searchForProcessNameLegacy(context: Context): String? {
|
|
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
|
|
|
return activityManager.runningAppProcesses?.find { Process.myPid() == it.pid }?.processName
|
|
}
|
|
}
|