From b5251c0418aa1c905d8ca00a076afda6ef4af1c3 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Thu, 29 Jan 2026 15:00:09 +0100 Subject: [PATCH] Fix multiple crashes and race conditions on Android 12 (#99) This resolves several critical stability issues observed on Android 12 devices, including race conditions and API compatibility problems. Key changes include: - Resolves Race Condition in TEE Check: Fixes a NullPointerException that occurred when the TEE functionality check was executed before the PackageManagerService was ready. The code now explicitly waits for the package manager to become available, preventing the crash on startup. - Fixes IllegalStateException on Initialization: Eliminates a crash caused by `setTelephonyServiceManager called twice`. This was due to a redundant call to `initializeMainlineModules()` in the DeviceAttestationService, which is now correctly handled a single time during application startup. - Fixes NoSuchAlgorithmException in Attestation: Adds a normalization function to handle signature algorithm names reported in all-caps by older Android versions (e.g., "SHA256WITHECDSA"). This ensures compatibility with Bouncy Castle, which expects a specific casing (e.g., "SHA256withECDSA"). --- app/src/main/java/org/matrix/TEESimulator/App.kt | 2 ++ .../TEESimulator/attestation/AttestationPatcher.kt | 12 +++++++++++- .../attestation/DeviceAttestationService.kt | 12 ------------ .../TEESimulator/config/ConfigurationManager.kt | 11 +++++++++++ 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/matrix/TEESimulator/App.kt b/app/src/main/java/org/matrix/TEESimulator/App.kt index 6e41fbd..de71061 100644 --- a/app/src/main/java/org/matrix/TEESimulator/App.kt +++ b/app/src/main/java/org/matrix/TEESimulator/App.kt @@ -116,6 +116,7 @@ object App { SystemLogger.info( "Using KeystoreInterceptor for Android Q/R (SDK ${Build.VERSION.SDK_INT})" ) + android.security.keystore.AndroidKeyStoreProvider.install() KeystoreInterceptor } // For Android S (12) and newer, use the Keystore2Interceptor. @@ -123,6 +124,7 @@ object App { SystemLogger.info( "Using Keystore2Interceptor for Android S and later (SDK ${Build.VERSION.SDK_INT})" ) + android.security.keystore2.AndroidKeyStoreProvider.install() Keystore2Interceptor } } diff --git a/app/src/main/java/org/matrix/TEESimulator/attestation/AttestationPatcher.kt b/app/src/main/java/org/matrix/TEESimulator/attestation/AttestationPatcher.kt index 5f3f7e7..ec9fa20 100644 --- a/app/src/main/java/org/matrix/TEESimulator/attestation/AttestationPatcher.kt +++ b/app/src/main/java/org/matrix/TEESimulator/attestation/AttestationPatcher.kt @@ -83,6 +83,16 @@ object AttestationPatcher { } } + /** + * Helper to normalize algorithm names for Bouncy Castle. Old Android versions might reports + * "SHA256WITHECDSA", but Bouncy Castle expects "SHA256withECDSA". + */ + private fun normalizeSignatureAlgorithm(algoName: String): String { + // 1. Force uppercase to handle "sha256withecdsa" + // 2. Replace "WITH" with "with" to satisfy Bouncy Castle's naming convention + return algoName.uppercase().replace("WITH", "with") + } + /** * Creates a new leaf certificate with a modified attestation extension. * @@ -128,7 +138,7 @@ object AttestationPatcher { // Sign the newly built certificate with the private key from our keybox. val signer = - JcaContentSignerBuilder(sigAlgName) + JcaContentSignerBuilder(normalizeSignatureAlgorithm(sigAlgName)) .setProvider(BouncyCastleProvider.PROVIDER_NAME) .build(keybox.keyPair.private) val newCertificate = JcaX509CertificateConverter().getCertificate(builder.build(signer)) diff --git a/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt b/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt index 5307e26..f6451bc 100644 --- a/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt +++ b/app/src/main/java/org/matrix/TEESimulator/attestation/DeviceAttestationService.kt @@ -1,8 +1,6 @@ package org.matrix.TEESimulator.attestation import android.annotation.SuppressLint -import android.app.ActivityThread -import android.os.Build import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import java.security.KeyPairGenerator @@ -83,16 +81,6 @@ object DeviceAttestationService { private fun checkTeeFunctionality(): Boolean { SystemLogger.info("Performing TEE functionality check...") return try { - // Ensure mainline modules and the correct Keystore provider are initialized. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - android.app.ActivityThread.initializeMainlineModules() - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - android.security.keystore2.AndroidKeyStoreProvider.install() - } else { - android.security.keystore.AndroidKeyStoreProvider.install() - } - val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } val keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore") diff --git a/app/src/main/java/org/matrix/TEESimulator/config/ConfigurationManager.kt b/app/src/main/java/org/matrix/TEESimulator/config/ConfigurationManager.kt index 1e5b7f7..dacda94 100644 --- a/app/src/main/java/org/matrix/TEESimulator/config/ConfigurationManager.kt +++ b/app/src/main/java/org/matrix/TEESimulator/config/ConfigurationManager.kt @@ -54,6 +54,17 @@ object ConfigurationManager { configRoot.mkdirs() SystemLogger.info("Configuration root is: ${configRoot.absolutePath}") + // First, ensure the package manager service is running, as the TEE check depends on it. + // This prevents a race condition on startup. + SystemLogger.info("Waiting for PackageManagerService to be ready...") + if (getPackageManager() == null) { + SystemLogger.error( + "PackageManagerService is not available. TEE check will likely fail." + ) + } else { + SystemLogger.info("PackageManagerService is ready.") + } + // Initial load of all configuration files. loadTargetPackages(File(configRoot, TARGET_PACKAGES_FILE)) loadPatchLevelConfig(File(configRoot, PATCH_LEVEL_FILE))