diff options
author | jhenrique09 <jhenrique09.mcz@hotmail.com> | 2021-03-06 15:27:11 -0300 |
---|---|---|
committer | alk3pInjection <webmaster@raspii.tech> | 2021-09-27 21:17:05 +0800 |
commit | d2469b4fe8cf9d428606b1bec1acd8e93e9df085 (patch) | |
tree | 3c8ef7681ea50ae0730046b514d7f9eea79f1f10 | |
parent | f55e2c7961f41f3e2e661c2ca882ab2882285002 (diff) |
[ArrowOS][11.0] base: Allow using face as auth method for apps [1/2]
commit 60756fa71bf50986b44b8cceea81f515a9df088f (HEAD)
Author: jhenrique09 <jhenrique09.mcz@hotmail.com>
Date: Sun Mar 7 17:16:03 2021 -0300
fixup - [1/2] Allow using face as auth method for apps
Change-Id: If9fd734b93fc2a507d41984d1997ddbabf26f3ec
Change-Id: I008370cefe9584c86e058b718e2ef79518b95eb6
12 files changed, 195 insertions, 15 deletions
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl index e57abd548057..c58ba0efcc13 100644 --- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl +++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl @@ -42,6 +42,8 @@ oneway interface IBiometricServiceReceiverInternal { void onTryAgainPressed(); // Notifies that the user has pressed the "use password" button on SystemUI void onDeviceCredentialPressed(); + // Notifies that the user has pressed the "use face" button on SystemUI + void onUseFacePressed(); // Notifies the client that an internal event, e.g. back button has occurred. void onSystemEvent(int event); } diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml index a87c7b3fa927..b80aa480ccb2 100644 --- a/packages/SystemUI/res/layout/auth_biometric_contents.xml +++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml @@ -92,6 +92,16 @@ android:maxWidth="@dimen/biometric_dialog_button_positive_max_width" android:text="@string/biometric_dialog_confirm" android:visibility="gone"/> + <!-- Use Face Button --> + <Button android:id="@+id/button_use_face" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@*android:style/Widget.DeviceDefault.Button.Colored" + android:layout_gravity="center_vertical" + android:ellipsize="end" + android:maxLines="2" + android:maxWidth="@dimen/biometric_dialog_button_positive_max_width" + android:text="@string/biometric_dialog_button_use_face"/> <!-- Try Again Button --> <Button android:id="@+id/button_try_again" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values/cm_strings.xml b/packages/SystemUI/res/values/cm_strings.xml index b746e57aba93..3be01462e604 100644 --- a/packages/SystemUI/res/values/cm_strings.xml +++ b/packages/SystemUI/res/values/cm_strings.xml @@ -139,4 +139,7 @@ <string name="quick_settings_reboot_label">Reboot</string> <string name="quick_settings_reboot_recovery_label">Recovery</string> <string name="quick_settings_poweroff_label">Power off</string> + + <!-- Biometric Dialog - Use Face --> + <string name="biometric_dialog_button_use_face">Use Face</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index dfcf59decc04..5b34f520857d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -103,6 +103,7 @@ public abstract class AuthBiometricView extends LinearLayout { int ACTION_BUTTON_TRY_AGAIN = 4; int ACTION_ERROR = 5; int ACTION_USE_DEVICE_CREDENTIAL = 6; + int ACTION_USE_FACE = 7; /** * When an action has occurred. The caller will only invoke this when the callback should @@ -128,6 +129,10 @@ public abstract class AuthBiometricView extends LinearLayout { return mBiometricView.findViewById(R.id.button_try_again); } + public Button getUseFaceButton() { + return mBiometricView.findViewById(R.id.button_use_face); + } + public TextView getTitleView() { return mBiometricView.findViewById(R.id.title); } @@ -178,6 +183,7 @@ public abstract class AuthBiometricView extends LinearLayout { @VisibleForTesting Button mNegativeButton; @VisibleForTesting Button mPositiveButton; @VisibleForTesting Button mTryAgainButton; + Button mUseFaceButton; // Measurements when biometric view is showing text, buttons, etc. private int mMediumHeight; @@ -602,6 +608,7 @@ public abstract class AuthBiometricView extends LinearLayout { mNegativeButton = mInjector.getNegativeButton(); mPositiveButton = mInjector.getPositiveButton(); mTryAgainButton = mInjector.getTryAgainButton(); + mUseFaceButton = mInjector.getUseFaceButton(); mNegativeButton.setOnClickListener((view) -> { if (mState == STATE_PENDING_CONFIRMATION) { @@ -625,6 +632,18 @@ public abstract class AuthBiometricView extends LinearLayout { mTryAgainButton.setVisibility(View.GONE); Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this); }); + + mUseFaceButton.setOnClickListener((view) -> { + mCallback.onAction(Callback.ACTION_USE_FACE); + }); + + if (this instanceof AuthBiometricFingerprintView) { + if (!Utils.canAuthenticateWithFace(mContext, mUserId)){ + mUseFaceButton.setVisibility(View.GONE); + } + } else if (this instanceof AuthBiometricFaceView) { + mUseFaceButton.setVisibility(View.GONE); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 363fd7d9b16d..7fcc12675134 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -235,6 +235,9 @@ public class AuthContainerView extends LinearLayout addCredentialView(false /* animatePanel */, true /* animateContents */); }, mInjector.getAnimateCredentialStartDelayMs()); break; + case AuthBiometricView.Callback.ACTION_USE_FACE: + mConfig.mCallback.onUseFacePressed(); + break; default: Log.e(TAG, "Unhandled action: " + action); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 9f0ea3ee46ff..2dc9a1ab23fb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -165,6 +165,19 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } @Override + public void onUseFacePressed() { + if (mReceiver == null) { + Log.e(TAG, "onUseFacePressed: Receiver is null"); + return; + } + try { + mReceiver.onUseFacePressed(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException when handling credential button", e); + } + } + + @Override public void onSystemEvent(int event) { if (mReceiver == null) { Log.e(TAG, "onSystemEvent(" + event + "): Receiver is null"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java index d3bd4fbd921c..0978b62da89a 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java @@ -60,6 +60,11 @@ public interface AuthDialogCallback { void onDeviceCredentialPressed(); /** + * Invoked when the "use face" button is clicked + */ + void onUseFacePressed(); + + /** * See {@link android.hardware.biometrics.BiometricPrompt.Builder * #setReceiveSystemEvents(boolean)} * @param event diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java index 7d237c45c7b3..7f4ca5164804 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java @@ -23,8 +23,10 @@ import android.annotation.IntDef; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.hardware.biometrics.BiometricPrompt; +import android.hardware.face.FaceManager; import android.os.Bundle; import android.os.UserManager; +import android.provider.Settings; import android.util.DisplayMetrics; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; @@ -35,6 +37,8 @@ import com.android.internal.widget.LockPatternUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import com.android.internal.util.custom.faceunlock.FaceUnlockUtils; + public class Utils { public static final int CREDENTIAL_PIN = 1; @@ -77,6 +81,20 @@ public class Utils { return (authenticators & Authenticators.BIOMETRIC_WEAK) != 0; } + static boolean canAuthenticateWithFace(Context context, int userId) { + if (!FaceUnlockUtils.isFaceUnlockSupported()){ + return false; + } + boolean enabledForApps = Settings.Secure.getIntForUser( + context.getContentResolver(), + Settings.Secure.FACE_UNLOCK_APP_ENABLED, 1, userId) != 0; + if (!enabledForApps){ + return false; + } + FaceManager faceManager = (FaceManager) context.getSystemService(FaceManager.class); + return faceManager != null && faceManager.hasEnrolledTemplates(userId); + } + static int getAuthenticators(Bundle biometricPromptBundle) { return biometricPromptBundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED); } diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 131267924179..a7501181316b 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -100,7 +100,7 @@ public class AuthService extends SystemService { */ @VisibleForTesting public String[] getConfiguration(Context context) { - return context.getResources().getStringArray(R.array.config_biometric_sensors); + return Utils.getConfiguration(context); } /** diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 1217a507a8dc..f8883385e5ca 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -114,6 +114,7 @@ public class BiometricService extends SystemService { private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12; private static final int MSG_ON_SYSTEM_EVENT = 13; private static final int MSG_CLIENT_DIED = 14; + private static final int MSG_ON_USE_FACE_PRESSED = 15; /** * Authentication either just called and we have not transitioned to the CALLED state, or @@ -183,8 +184,8 @@ public class BiometricService extends SystemService { final int mCallingUserId; // Continue authentication with the same modality/modalities after "try again" is // pressed - final int mModality; - final boolean mRequireConfirmation; + int mModality; + boolean mRequireConfirmation; // The current state, which can be either idle, called, or started int mState = STATE_AUTH_IDLE; @@ -241,6 +242,15 @@ public class BiometricService extends SystemService { return false; } + void clearCookie() { + if (mModalitiesWaiting != null) { + mModalitiesWaiting.clear(); + } + if (mModalitiesMatched != null) { + mModalitiesMatched.clear(); + } + } + boolean isAllowDeviceCredential() { return Utils.isCredentialRequested(mBundle); } @@ -384,6 +394,11 @@ public class BiometricService extends SystemService { break; } + case MSG_ON_USE_FACE_PRESSED: { + handleOnUseFacePressed(); + break; + } + case MSG_ON_SYSTEM_EVENT: { handleOnSystemEvent((int) msg.obj); break; @@ -668,6 +683,11 @@ public class BiometricService extends SystemService { } @Override + public void onUseFacePressed() { + mHandler.sendEmptyMessage(MSG_ON_USE_FACE_PRESSED); + } + + @Override public void onSystemEvent(int event) { mHandler.obtainMessage(MSG_ON_SYSTEM_EVENT, event).sendToTarget(); } @@ -980,7 +1000,7 @@ public class BiometricService extends SystemService { */ @VisibleForTesting public String[] getConfiguration(Context context) { - return context.getResources().getStringArray(R.array.config_biometric_sensors); + return Utils.getConfiguration(context); } } @@ -1434,7 +1454,13 @@ public class BiometricService extends SystemService { if (mCurrentAuthSession.isAllowDeviceCredential() && errorLockout) { // SystemUI handles transition from biometric to device credential. mCurrentAuthSession.mState = STATE_SHOWING_DEVICE_CREDENTIAL; - mStatusBarService.onBiometricError(modality, error, vendorCode); + mHandler.postDelayed(() -> { + try{ + mStatusBarService.onBiometricError(modality, error, vendorCode); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + }, 500); } else if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { mStatusBarService.hideAuthenticationDialog(); // TODO: If multiple authenticators are simultaneously running, this will @@ -1444,7 +1470,13 @@ public class BiometricService extends SystemService { mCurrentAuthSession = null; } else { mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI; - mStatusBarService.onBiometricError(modality, error, vendorCode); + mHandler.postDelayed(() -> { + try{ + mStatusBarService.onBiometricError(modality, error, vendorCode); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + }, 500); } } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) { // In the "try again" state, we should forward canceled errors to @@ -1613,6 +1645,58 @@ public class BiometricService extends SystemService { mCurrentAuthSession.mModality); } + private void handleOnUseFacePressed() { + Slog.d(TAG, "onUseFacePressed"); + if (mCurrentAuthSession == null) { + Slog.e(TAG, "Auth session null"); + return; + } + + // Cancel authentication. Skip the token/package check since we are cancelling + // from system server. The interface is permission protected so this is fine. + AuthSession currentAuthSession = mCurrentAuthSession; + mCurrentAuthSession = null; + cancelInternal(null /* token */, null /* package */, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.getCallingUserId(), + false /* fromClient */, TYPE_FINGERPRINT); + + // Re-authenticate with face modality + switchToFaceModality(currentAuthSession); + } + + private void switchToFaceModality(AuthSession authSession) { + Slog.d(TAG, "switchToFaceModality"); + // Generate random cookies to pass to the services that should prepare to start + // authenticating. Store the cookie here and wait for all services to "ack" + // with the cookie. Once all cookies are received, we can show the prompt + // and let the services start authenticating. The cookie should be non-zero. + final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; + Slog.d(TAG, "switchToFaceModality, cookie=" + cookie); + + // Use custom session + mPendingAuthSession = authSession; + mPendingAuthSession.clearCookie(); + mPendingAuthSession.mModality = TYPE_FACE; + mPendingAuthSession.mRequireConfirmation = true; + mPendingAuthSession.mModalitiesWaiting.put(TYPE_FACE, cookie); + try { + mPendingAuthSession.mState = STATE_AUTH_CALLED; + for (AuthenticatorWrapper authenticator : mAuthenticators) { + // TODO(b/141025588): use ids instead of modalities to avoid ambiguity. + if (authenticator.modality == TYPE_FACE) { + authenticator.impl.prepareForAuthentication(true, + authSession.mToken, authSession.mSessionId, authSession.mUserId, + mInternalReceiver, authSession.mOpPackageName, cookie, + authSession.mCallingUid, authSession.mCallingPid, + authSession.mCallingUserId); + break; + } + } + } catch (RemoteException e) { + Slog.e(TAG, "Unable to start authentication", e); + } + } + private void handleOnDeviceCredentialPressed() { Slog.d(TAG, "onDeviceCredentialPressed"); if (mCurrentAuthSession == null) { @@ -1792,9 +1876,8 @@ public class BiometricService extends SystemService { boolean requireConfirmation = bundle.getBoolean( BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */); if ((modality & TYPE_FACE) != 0) { - // Check if the user has forced confirmation to be required in Settings. - requireConfirmation = requireConfirmation - || mSettingObserver.getFaceAlwaysRequireConfirmation(userId); + // Always force confirmation if Face + requireConfirmation = true; } // Generate random cookies to pass to the services that should prepare to start // authenticating. Store the cookie here and wait for all services to "ack" @@ -1880,10 +1963,13 @@ public class BiometricService extends SystemService { void cancelInternal(IBinder token, String opPackageName, int callingUid, int callingPid, int callingUserId, boolean fromClient) { - if (mCurrentAuthSession == null) { - Slog.w(TAG, "Skipping cancelInternal"); - return; - } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED + cancelInternal(token, opPackageName, callingUid, callingPid, callingUserId, fromClient, -1); + } + + void cancelInternal(IBinder token, String opPackageName, int callingUid, int callingPid, + int callingUserId, boolean fromClient, int modality) { + if (mCurrentAuthSession != null + && mCurrentAuthSession.mState != STATE_AUTH_STARTED && mCurrentAuthSession.mState != STATE_CLIENT_DIED_CANCELLING) { Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState); return; @@ -1892,7 +1978,8 @@ public class BiometricService extends SystemService { // TODO: For multiple modalities, send a single ERROR_CANCELED only when all // drivers have canceled authentication. for (AuthenticatorWrapper authenticator : mAuthenticators) { - if ((authenticator.modality & mCurrentAuthSession.mModality) != 0) { + if (modality == authenticator.modality || + (mCurrentAuthSession != null && (authenticator.modality & mCurrentAuthSession.mModality) != 0)) { try { authenticator.impl.cancelAuthenticationFromService(token, opPackageName, callingUid, callingPid, callingUserId, fromClient); diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index c661f452820d..1a81c26d8eae 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -43,8 +43,12 @@ import android.util.Slog; import com.android.internal.R; import com.android.internal.widget.LockPatternUtils; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import com.android.server.biometrics.face.CustomFaceService; + public class Utils { private static final String TAG = "BiometricUtils"; @@ -320,4 +324,14 @@ public class Utils { Slog.d(TAG, "isEncrypted: " + isEncrypted + " isLockdown: " + isLockDown); return isEncrypted || isLockDown; } + + public static String[] getConfiguration(Context context) { + ArrayList<String> sensors = new ArrayList(); + Collections.addAll(sensors, context.getResources().getStringArray( + R.array.config_biometric_sensors)); + if (CustomFaceService.isSupported()){ + Collections.addAll(sensors, CustomFaceService.getConfiguration()); + } + return sensors.toArray(new String[0]); + } } diff --git a/services/core/java/com/android/server/biometrics/face/CustomFaceService.java b/services/core/java/com/android/server/biometrics/face/CustomFaceService.java index 1a79610e33db..51f8eda40612 100644 --- a/services/core/java/com/android/server/biometrics/face/CustomFaceService.java +++ b/services/core/java/com/android/server/biometrics/face/CustomFaceService.java @@ -28,6 +28,8 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.face.Face; import android.os.Binder; import android.os.Handler; @@ -384,7 +386,11 @@ public class CustomFaceService { return info != null && info.serviceInfo.isEnabled(); } - public boolean isSupported() { + public static String getConfiguration() { + return HAL_DEVICE_ID + ":" + BiometricAuthenticator.TYPE_FACE + ":" + Authenticators.BIOMETRIC_STRONG; + } + + public static boolean isSupported() { return FaceUnlockUtils.isFaceUnlockSupported(); } |