diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2de4310..058bc97 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
package="com.edison.fingerface">
+
FingerprintManager.CryptoObject(cipher)
+ mac != null -> FingerprintManager.CryptoObject(mac)
+ signature != null -> FingerprintManager.CryptoObject(signature)
+ else -> null
+ }
+
+ override fun getCryptoObject(): FingerprintManager.CryptoObject? {
+ return result.cryptoObject?.toLegacy()
+ }
+}
diff --git a/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.java b/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.java
deleted file mode 100644
index fd028d7..0000000
--- a/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package android.hardware.fingerprint;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.hardware.biometrics.BiometricPrompt;
-import android.os.CancellationSignal;
-import android.os.Handler;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyPermanentlyInvalidatedException;
-import android.security.keystore.KeyProperties;
-import android.util.Log;
-
-import javax.crypto.Cipher;
-import javax.crypto.KeyGenerator;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import java.io.IOException;
-import java.security.*;
-import java.security.cert.CertificateException;
-
-public class MyFingerprintManager extends FingerprintManager {
-
- private Context mContext;
-
- private KeyGenerator keyGenerator;
- private KeyStore keyStore;
- private Cipher cipher;
-
- private String KEY_NAME = "MyFingerprintManager";
-
- public void SetContext(Context context) {
- mContext = context;
- }
-
- @Override
- public boolean isHardwareDetected() {
-
- Log.d("XposedHandler", "isHardwareDetected");
- return true;
- }
-
- @Override
- public boolean hasEnrolledFingerprints() {
- Log.d("XposedHandler", "hasEnrolledFingerprints");
- return true;
- }
-
- @Override
- public void authenticate(final CryptoObject crypto, CancellationSignal cancel,
- int flags, final AuthenticationCallback callback, Handler handler) {
-
- BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(mContext).setTitle("FaceID").setNegativeButton(mContext.getString(android.R.string.cancel), mContext.getMainExecutor(), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- }).build();
-
- BiometricPrompt.CryptoObject c = null;
-
- if (crypto == null) {
-
- generateKey();
- initCipher();
- c = new BiometricPrompt.CryptoObject(cipher);
-
- } else if (crypto.getCipher() != null) {
- c = new BiometricPrompt.CryptoObject(crypto.getCipher());
- } else if (crypto.getMac() != null) {
- c = new BiometricPrompt.CryptoObject(crypto.getMac());
- } else if (crypto.getSignature() != null) {
- c = new BiometricPrompt.CryptoObject(crypto.getSignature());
- }
-
- biometricPrompt.authenticate(c, cancel, mContext.getMainExecutor(), new BiometricPrompt.AuthenticationCallback() {
- @Override
- public void onAuthenticationError(int errorCode, CharSequence errString) {
- callback.onAuthenticationError(errorCode, errString);
- }
-
- @Override
- public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
- callback.onAuthenticationHelp(helpCode, helpString);
- }
-
- @Override
- public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
-
- BiometricPrompt.CryptoObject BioCrypto = result.getCryptoObject();
-
- CryptoObject c = null;
-
- if (BioCrypto.getCipher() != null) {
- c = new CryptoObject(BioCrypto.getCipher());
- } else if (BioCrypto.getMac() != null) {
- c = new CryptoObject(BioCrypto.getMac());
- } else if (BioCrypto.getSignature() != null) {
- c = new CryptoObject(BioCrypto.getSignature());
- }
-
- AuthenticationResult fingerprintResult = new AuthenticationResult();
-
- if (crypto != null) {
- fingerprintResult = new MyAuthenticationResult();
- ((MyAuthenticationResult) fingerprintResult).setCryptoObject(c);
- }
-
- callback.onAuthenticationSucceeded(fingerprintResult);
- }
-
- @Override
- public void onAuthenticationFailed() {
- callback.onAuthenticationFailed();
- }
- });
- }
-
-
- private void generateKey() {
- try {
-
- keyStore = KeyStore.getInstance("AndroidKeyStore");
- keyStore.load(null);
-
- keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
- keyGenerator.init(new
- KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
- .setUserAuthenticationRequired(true)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
- .build());
-
- keyGenerator.generateKey();
-
- } catch (KeyStoreException
- | NoSuchAlgorithmException
- | NoSuchProviderException
- | InvalidAlgorithmParameterException
- | CertificateException
- | IOException exc) {
- exc.printStackTrace();
- }
- }
-
- private boolean initCipher() {
- try {
- cipher = Cipher.getInstance(
- KeyProperties.KEY_ALGORITHM_AES + "/"
- + KeyProperties.BLOCK_MODE_CBC + "/"
- + KeyProperties.ENCRYPTION_PADDING_PKCS7);
-
- } catch (NoSuchAlgorithmException |
- NoSuchPaddingException e) {
- throw new RuntimeException("Failed to get Cipher", e);
- }
-
- try {
- keyStore.load(null);
- SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
- null);
- cipher.init(Cipher.ENCRYPT_MODE, key);
- return true;
-
-
- } catch (KeyPermanentlyInvalidatedException e) {
- return false;
-
- } catch (KeyStoreException | CertificateException
- | UnrecoverableKeyException | IOException
- | NoSuchAlgorithmException | InvalidKeyException e) {
-
- throw new RuntimeException("Failed to init Cipher", e);
- }
- }
-}
diff --git a/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.kt b/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.kt
new file mode 100644
index 0000000..f444b8b
--- /dev/null
+++ b/app/src/main/java/android/hardware/fingerprint/MyFingerprintManager.kt
@@ -0,0 +1,75 @@
+@file:Suppress("DEPRECATION")
+
+package android.hardware.fingerprint
+
+import android.content.Context
+import android.content.DialogInterface
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.BiometricPrompt
+import android.os.CancellationSignal
+import android.os.Handler
+import android.util.Log
+import com.edison.fingerface.HandlerExecutor
+
+class MyFingerprintManager(private val context: Context) : FingerprintManager() {
+
+ private val manager = context.getSystemService(BiometricManager::class.java)!!
+
+ override fun isHardwareDetected(): Boolean {
+ Log.d(TAG, "isHardwareDetected")
+ return when (manager.canAuthenticate()) {
+ BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,
+ BiometricManager.BIOMETRIC_SUCCESS -> true
+ BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> false
+ else -> false
+ }
+ }
+
+ override fun hasEnrolledFingerprints(): Boolean {
+ Log.d(TAG, "hasEnrolledFingerprints")
+ return when (manager.canAuthenticate()) {
+ BiometricManager.BIOMETRIC_SUCCESS -> true
+ BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED,
+ BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> false
+ else -> false
+ }
+ }
+
+ private fun CryptoObject.toBio() = when {
+ cipher != null -> BiometricPrompt.CryptoObject(cipher)
+ mac != null -> BiometricPrompt.CryptoObject(mac)
+ signature != null -> BiometricPrompt.CryptoObject(signature)
+ else -> null
+ }
+
+ override fun authenticate(
+ crypto: CryptoObject?, cancel: CancellationSignal?,
+ flags: Int, legacyCallback: AuthenticationCallback, handler: Handler?
+ ) {
+ Log.d(TAG, "authenticate")
+
+ val bioCrypto = crypto?.toBio()
+ val cancelSignal = cancel ?: CancellationSignal()
+ val executor = handler?.let { HandlerExecutor(it) } ?: context.mainExecutor
+ val callback = MyAuthenticationCallback(legacyCallback)
+
+ val biometricPrompt = BiometricPrompt.Builder(context).setTitle("Fingerface")
+ .setNegativeButton(
+ context.getString(android.R.string.cancel),
+ executor,
+ DialogInterface.OnClickListener { dialog, _ -> dialog.dismiss() })
+ .build()
+
+ if (bioCrypto == null) {
+ biometricPrompt.authenticate(cancelSignal, executor, callback)
+ } else {
+ biometricPrompt.authenticate(bioCrypto, cancelSignal, executor, callback)
+ }
+ }
+
+ companion object {
+ const val TAG = "Fingerface"
+ }
+}
diff --git a/app/src/main/java/com/edison/fingerface/HandlerExecutor.kt b/app/src/main/java/com/edison/fingerface/HandlerExecutor.kt
new file mode 100644
index 0000000..2ed0ba9
--- /dev/null
+++ b/app/src/main/java/com/edison/fingerface/HandlerExecutor.kt
@@ -0,0 +1,13 @@
+package com.edison.fingerface
+
+import android.os.Handler
+import java.util.concurrent.Executor
+import java.util.concurrent.RejectedExecutionException
+
+class HandlerExecutor(private val handler: Handler) : Executor {
+ override fun execute(command: Runnable) {
+ if (!handler.post(command)) {
+ throw RejectedExecutionException("$handler is shutting down");
+ }
+ }
+}
diff --git a/app/src/main/java/com/edison/fingerface/ModFingerface.kt b/app/src/main/java/com/edison/fingerface/ModFingerface.kt
deleted file mode 100644
index 34287dc..0000000
--- a/app/src/main/java/com/edison/fingerface/ModFingerface.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.edison.fingerface
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.content.res.XModuleResources
-import android.hardware.fingerprint.FingerprintManager
-import android.util.Log
-import de.robv.android.xposed.XC_MethodHook
-import de.robv.android.xposed.XSharedPreferences
-import de.robv.android.xposed.XposedBridge
-import android.hardware.fingerprint.MyFingerprintManager
-
-
-class ModFingerface {
- companion object {
- @JvmStatic
- fun initAndroid(prefs: XSharedPreferences, classLoader: ClassLoader, modRes: XModuleResources) {
- val getSystemServiceHook = object : XC_MethodHook() {
- override fun afterHookedMethod(param: MethodHookParam?) {
- if (param!!.args[0].toString() == Context.FINGERPRINT_SERVICE) {
- Log.d("XposedHandler", "Get Fingerpring service, call from:${param!!.method.name}, thisObj:${param!!.result}")
- val mgr = MyFingerprintManager()
- mgr.SetContext(param!!.thisObject as Context)
- param!!.result = mgr
- }
- }
- }
-
- XposedBridge.hookAllMethods(
- Class.forName("android.app.ContextImpl"),
- "getSystemService",
- getSystemServiceHook
- )
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/edison/fingerface/ModPackageManager.kt b/app/src/main/java/com/edison/fingerface/ModPackageManager.kt
deleted file mode 100644
index cc7999b..0000000
--- a/app/src/main/java/com/edison/fingerface/ModPackageManager.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.edison.fingerface
-
-import android.content.Context
-import android.content.pm.PackageManager
-import android.content.res.XModuleResources
-import android.hardware.fingerprint.FingerprintManager
-import android.util.Log
-import de.robv.android.xposed.XC_MethodHook
-import de.robv.android.xposed.XSharedPreferences
-import de.robv.android.xposed.XposedBridge
-import android.hardware.fingerprint.MyFingerprintManager
-import de.robv.android.xposed.XposedHelpers
-import android.R.attr.classLoader
-
-
-class ModPackageManagerService {
- companion object {
- @JvmStatic
- fun initAndroid(prefs: XSharedPreferences, classLoader: ClassLoader, modRes: XModuleResources) {
-
- val hasSystemFeatureHook = object : XC_MethodHook() {
-
- override fun afterHookedMethod(param: MethodHookParam?) {
- if (param!!.args[0].toString() == PackageManager.FEATURE_FINGERPRINT) {
- param!!.result = true
- }
- }
- }
-
- XposedHelpers.findAndHookMethod("android.app.ContextImpl", classLoader,
- "getPackageManager", object : XC_MethodHook() {
- @Throws(Throwable::class)
- override fun afterHookedMethod(param: MethodHookParam?) {
- if (param!!.result is PackageManager) {
- val clazz = param.result.javaClass
- val packageMethod = clazz.getDeclaredMethod("hasSystemFeature", String::class.java)
-
- XposedBridge.hookMethod(packageMethod, hasSystemFeatureHook)
-
- }
- }
- })
-
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/edison/fingerface/XposedHandler.java b/app/src/main/java/com/edison/fingerface/XposedHandler.java
deleted file mode 100644
index 6d006f8..0000000
--- a/app/src/main/java/com/edison/fingerface/XposedHandler.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.edison.fingerface;
-
-import android.app.AndroidAppHelper;
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PermissionInfo;
-import android.content.res.XModuleResources;
-import android.content.res.XResources;
-import android.util.Log;
-import android.util.Pair;
-import de.robv.android.xposed.*;
-import de.robv.android.xposed.callbacks.XC_InitPackageResources;
-import de.robv.android.xposed.callbacks.XC_LoadPackage;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class XposedHandler implements IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources {
- public static final String PACKAGE_NAME = XposedHandler.class.getPackage().getName();
- private static final String TAG = "Fingerface";
-
- private static XSharedPreferences prefs;
- public static XModuleResources modRes;
- private static String MODULE_PATH = null;
-
- public static List> packageResources = new ArrayList<>();
-
- @Override
- public void initZygote(StartupParam startupParam) throws Throwable {
- MODULE_PATH = startupParam.modulePath;
- modRes = XModuleResources.createInstance(MODULE_PATH, null);
- prefs = new XSharedPreferences(PACKAGE_NAME);
-
- }
-
- @Override
- public void handleInitPackageResources(XC_InitPackageResources.InitPackageResourcesParam resparam) throws Throwable {
-
- if (resparam.packageName.equals("android")) {
- return;
- }
-
- XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res);
-
- }
-
- @Override
- public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
-
- if (!lpparam.processName.equals("android") && !lpparam.processName.equals("com.android.systemui")) {
- ModFingerface.initAndroid(prefs, lpparam.classLoader, modRes);
- ModPackageManagerService.initAndroid(prefs, lpparam.classLoader, modRes);
- }
- }
-
-}
diff --git a/app/src/main/java/com/edison/fingerface/XposedHandler.kt b/app/src/main/java/com/edison/fingerface/XposedHandler.kt
new file mode 100644
index 0000000..d4aa42c
--- /dev/null
+++ b/app/src/main/java/com/edison/fingerface/XposedHandler.kt
@@ -0,0 +1,62 @@
+package com.edison.fingerface
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.XModuleResources
+import android.hardware.fingerprint.MyFingerprintManager
+import de.robv.android.xposed.*
+import de.robv.android.xposed.callbacks.XC_LoadPackage
+
+class XposedHandler : IXposedHookZygoteInit, IXposedHookLoadPackage {
+
+ private lateinit var modulePath: String
+ private lateinit var modRes: XModuleResources
+ private lateinit var prefs: XSharedPreferences
+
+ override fun initZygote(startupParam: IXposedHookZygoteInit.StartupParam) {
+ modulePath = startupParam.modulePath
+ modRes = XModuleResources.createInstance(modulePath, null)
+ prefs = XSharedPreferences(BuildConfig.APPLICATION_ID)
+
+ }
+
+ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
+ if (lpparam.processName != "android" && lpparam.processName != "com.android.systemui") {
+ hookFingerprintService(lpparam.classLoader)
+ hookPackageManager(lpparam.classLoader)
+ }
+ }
+
+ private fun hookFingerprintService(cl: ClassLoader) {
+ val getSystemServiceHook = object : XC_MethodHook() {
+ override fun afterHookedMethod(param: MethodHookParam) {
+ if (param.args[0].toString() == Context.FINGERPRINT_SERVICE) {
+ param.result = MyFingerprintManager(param.thisObject as Context)
+ }
+ }
+ }
+
+ XposedHelpers.findAndHookMethod(
+ "android.app.ContextImpl", cl,
+ "getSystemService", String::class.java,
+ getSystemServiceHook
+ )
+ }
+
+ private fun hookPackageManager(cl: ClassLoader) {
+ val hasSystemFeatureHook = object : XC_MethodHook() {
+ override fun afterHookedMethod(param: MethodHookParam) {
+ if (param.args[0].toString() == PackageManager.FEATURE_FINGERPRINT) {
+ param.result = true
+ }
+ }
+ }
+
+ XposedHelpers.findAndHookMethod(
+ "android.app.ApplicationPackageManager", cl,
+ "hasSystemFeature", String::class.java,
+ hasSystemFeatureHook
+ )
+ }
+
+}