secant-android-wallet/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/process/ProcessNameCompat.kt

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
}
}