1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.fingerprint; 18 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.hardware.biometrics.BiometricPrompt; 22 import android.hardware.biometrics.IBiometricPromptReceiver; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.Message; 26 import android.os.RemoteException; 27 import android.util.Log; 28 import android.view.WindowManager; 29 30 import com.android.internal.os.SomeArgs; 31 import com.android.systemui.SystemUI; 32 import com.android.systemui.statusbar.CommandQueue; 33 34 public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Callbacks { 35 private static final String TAG = "FingerprintDialogImpl"; 36 private static final boolean DEBUG = true; 37 38 protected static final int MSG_SHOW_DIALOG = 1; 39 protected static final int MSG_FINGERPRINT_AUTHENTICATED = 2; 40 protected static final int MSG_FINGERPRINT_HELP = 3; 41 protected static final int MSG_FINGERPRINT_ERROR = 4; 42 protected static final int MSG_HIDE_DIALOG = 5; 43 protected static final int MSG_BUTTON_NEGATIVE = 6; 44 protected static final int MSG_USER_CANCELED = 7; 45 protected static final int MSG_BUTTON_POSITIVE = 8; 46 protected static final int MSG_CLEAR_MESSAGE = 9; 47 48 49 private FingerprintDialogView mDialogView; 50 private WindowManager mWindowManager; 51 private IBiometricPromptReceiver mReceiver; 52 private boolean mDialogShowing; 53 54 private Handler mHandler = new Handler() { 55 @Override 56 public void handleMessage(Message msg) { 57 switch(msg.what) { 58 case MSG_SHOW_DIALOG: 59 handleShowDialog((SomeArgs) msg.obj); 60 break; 61 case MSG_FINGERPRINT_AUTHENTICATED: 62 handleFingerprintAuthenticated(); 63 break; 64 case MSG_FINGERPRINT_HELP: 65 handleFingerprintHelp((String) msg.obj); 66 break; 67 case MSG_FINGERPRINT_ERROR: 68 handleFingerprintError((String) msg.obj); 69 break; 70 case MSG_HIDE_DIALOG: 71 handleHideDialog((Boolean) msg.obj); 72 break; 73 case MSG_BUTTON_NEGATIVE: 74 handleButtonNegative(); 75 break; 76 case MSG_USER_CANCELED: 77 handleUserCanceled(); 78 break; 79 case MSG_BUTTON_POSITIVE: 80 handleButtonPositive(); 81 break; 82 case MSG_CLEAR_MESSAGE: 83 handleClearMessage(); 84 break; 85 } 86 } 87 }; 88 89 @Override start()90 public void start() { 91 if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { 92 return; 93 } 94 getComponent(CommandQueue.class).addCallbacks(this); 95 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 96 mDialogView = new FingerprintDialogView(mContext, mHandler); 97 } 98 99 @Override showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver)100 public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) { 101 if (DEBUG) Log.d(TAG, "showFingerprintDialog"); 102 // Remove these messages as they are part of the previous client 103 mHandler.removeMessages(MSG_FINGERPRINT_ERROR); 104 mHandler.removeMessages(MSG_FINGERPRINT_HELP); 105 mHandler.removeMessages(MSG_FINGERPRINT_AUTHENTICATED); 106 SomeArgs args = SomeArgs.obtain(); 107 args.arg1 = bundle; 108 args.arg2 = receiver; 109 mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget(); 110 } 111 112 @Override onFingerprintAuthenticated()113 public void onFingerprintAuthenticated() { 114 if (DEBUG) Log.d(TAG, "onFingerprintAuthenticated"); 115 mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget(); 116 } 117 118 @Override onFingerprintHelp(String message)119 public void onFingerprintHelp(String message) { 120 if (DEBUG) Log.d(TAG, "onFingerprintHelp: " + message); 121 mHandler.obtainMessage(MSG_FINGERPRINT_HELP, message).sendToTarget(); 122 } 123 124 @Override onFingerprintError(String error)125 public void onFingerprintError(String error) { 126 if (DEBUG) Log.d(TAG, "onFingerprintError: " + error); 127 mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, error).sendToTarget(); 128 } 129 130 @Override hideFingerprintDialog()131 public void hideFingerprintDialog() { 132 if (DEBUG) Log.d(TAG, "hideFingerprintDialog"); 133 mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget(); 134 } 135 handleShowDialog(SomeArgs args)136 private void handleShowDialog(SomeArgs args) { 137 if (DEBUG) Log.d(TAG, "handleShowDialog, isAnimatingAway: " 138 + mDialogView.isAnimatingAway()); 139 if (mDialogView.isAnimatingAway()) { 140 mDialogView.forceRemove(); 141 } else if (mDialogShowing) { 142 Log.w(TAG, "Dialog already showing"); 143 return; 144 } 145 mReceiver = (IBiometricPromptReceiver) args.arg2; 146 mDialogView.setBundle((Bundle)args.arg1); 147 mWindowManager.addView(mDialogView, mDialogView.getLayoutParams()); 148 mDialogShowing = true; 149 } 150 handleFingerprintAuthenticated()151 private void handleFingerprintAuthenticated() { 152 if (DEBUG) Log.d(TAG, "handleFingerprintAuthenticated"); 153 mDialogView.announceForAccessibility( 154 mContext.getResources().getText( 155 com.android.internal.R.string.fingerprint_authenticated)); 156 handleHideDialog(false /* userCanceled */); 157 } 158 handleFingerprintHelp(String message)159 private void handleFingerprintHelp(String message) { 160 if (DEBUG) Log.d(TAG, "handleFingerprintHelp: " + message); 161 mDialogView.showHelpMessage(message); 162 } 163 handleFingerprintError(String error)164 private void handleFingerprintError(String error) { 165 if (DEBUG) Log.d(TAG, "handleFingerprintError: " + error); 166 if (!mDialogShowing) { 167 if (DEBUG) Log.d(TAG, "Dialog already dismissed"); 168 return; 169 } 170 mDialogView.showErrorMessage(error); 171 } 172 handleHideDialog(boolean userCanceled)173 private void handleHideDialog(boolean userCanceled) { 174 if (DEBUG) Log.d(TAG, "handleHideDialog, userCanceled: " + userCanceled); 175 if (!mDialogShowing) { 176 // This can happen if there's a race and we get called from both 177 // onAuthenticated and onError, etc. 178 Log.w(TAG, "Dialog already dismissed, userCanceled: " + userCanceled); 179 return; 180 } 181 if (userCanceled) { 182 try { 183 mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL); 184 } catch (RemoteException e) { 185 Log.e(TAG, "RemoteException when hiding dialog", e); 186 } 187 } 188 mReceiver = null; 189 mDialogShowing = false; 190 mDialogView.startDismiss(); 191 } 192 handleButtonNegative()193 private void handleButtonNegative() { 194 if (mReceiver == null) { 195 Log.e(TAG, "Receiver is null"); 196 return; 197 } 198 try { 199 mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE); 200 } catch (RemoteException e) { 201 Log.e(TAG, "Remote exception when handling negative button", e); 202 } 203 handleHideDialog(false /* userCanceled */); 204 } 205 handleButtonPositive()206 private void handleButtonPositive() { 207 if (mReceiver == null) { 208 Log.e(TAG, "Receiver is null"); 209 return; 210 } 211 try { 212 mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_POSITIVE); 213 } catch (RemoteException e) { 214 Log.e(TAG, "Remote exception when handling positive button", e); 215 } 216 handleHideDialog(false /* userCanceled */); 217 } 218 handleClearMessage()219 private void handleClearMessage() { 220 mDialogView.resetMessage(); 221 } 222 handleUserCanceled()223 private void handleUserCanceled() { 224 handleHideDialog(true /* userCanceled */); 225 } 226 } 227