/* * 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 Array?.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 } } } }