UnifiedNlp/service/src/main/kotlin/org/microg/nlp/service/AbstractBackendHelper.kt

120 lines
3.9 KiB
Kotlin

/*
* SPDX-FileCopyrightText: 2014, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.nlp.service
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.IBinder
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
fun <T> Array<out T>?.isNotNullOrEmpty(): Boolean {
return this != null && this.isNotEmpty()
}
abstract class AbstractBackendHelper(private val TAG: String, private val context: Context, protected val coroutineScope: CoroutineScope, val serviceIntent: Intent, val signatureDigest: String?) : ServiceConnection {
private var bound: Boolean = false
protected abstract suspend fun close()
protected abstract fun hasBackend(): Boolean
override fun onServiceConnected(name: ComponentName, service: IBinder) {
bound = true
Log.d(TAG, "Bound to: $name")
}
override fun onServiceDisconnected(name: ComponentName) {
bound = false
Log.d(TAG, "Unbound from: $name")
}
suspend fun unbind() {
if (bound) {
if (hasBackend()) {
try {
close()
} catch (e: Exception) {
Log.w(TAG, e)
}
}
try {
Log.d(TAG, "Unbinding from: $serviceIntent")
context.unbindService(this)
} catch (e: Exception) {
Log.w(TAG, e)
}
bound = false
}
}
fun bind() {
if (!bound) {
Log.d(TAG, "Binding to: $serviceIntent sig: $signatureDigest")
if (serviceIntent.getPackage() == null) {
Log.w(TAG, "Intent is not properly resolved, can't verify signature. Aborting.")
return
}
val computedDigest = firstSignatureDigest(context, serviceIntent.getPackage(), if (signatureDigest?.length == 40) "SHA-1" else "SHA-256")
if (signatureDigest != null && signatureDigest != computedDigest) {
Log.w(TAG, "Target signature does not match selected package ($signatureDigest != $computedDigest). Aborting.")
return
}
try {
context.bindService(serviceIntent, this, Context.BIND_AUTO_CREATE)
} catch (e: Exception) {
Log.w(TAG, e)
}
}
}
companion object {
@Suppress("DEPRECATION")
@SuppressLint("PackageManagerGetSignatures")
fun firstSignatureDigest(context: Context, packageName: String?, algorithm: String): String? {
val packageManager = context.packageManager
val info: PackageInfo?
try {
info = packageManager.getPackageInfo(packageName!!, PackageManager.GET_SIGNATURES)
} catch (e: PackageManager.NameNotFoundException) {
return null
}
if (info?.signatures.isNotNullOrEmpty()) {
for (sig in info.signatures) {
digest(sig.toByteArray(), algorithm)?.let { return it }
}
}
return null
}
private fun digest(bytes: ByteArray, algorithm: String): String? {
try {
val md = MessageDigest.getInstance(algorithm)
val digest = md.digest(bytes)
val sb = StringBuilder(2 * digest.size)
for (b in digest) {
sb.append(String.format("%02x", b))
}
return sb.toString()
} catch (e: NoSuchAlgorithmException) {
return null
}
}
}
}