blob: 0c6794c2ab8582ef6fb828a7a2203d458c07e283 [file] [log] [blame]
Kevin Chynaae4a152018-01-18 11:48:09 -08001/*
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
Kevin Chynf8688a02019-08-27 17:04:05 -070014 * limitations under the License.
Kevin Chynaae4a152018-01-18 11:48:09 -080015 */
16
Kevin Chyne9275662018-07-23 16:42:06 -070017package com.android.systemui.biometrics;
Kevin Chynaae4a152018-01-18 11:48:09 -080018
Ilya Matyukhin0f9da352019-10-03 14:10:01 -070019import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
20import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
Curtis Belmonte13eb5812019-10-22 14:17:30 -070021import static android.hardware.biometrics.BiometricManager.Authenticators;
Ilya Matyukhin0f9da352019-10-03 14:10:01 -070022
Kevin Chync8cb6852020-03-10 18:29:15 -070023import android.annotation.Nullable;
Kevin Chyn050315f2019-08-08 14:22:54 -070024import android.app.ActivityManager;
25import android.app.ActivityTaskManager;
26import android.app.IActivityTaskManager;
27import android.app.TaskStackListener;
Kevin Chyne181b8d2019-11-05 15:02:52 -080028import android.content.BroadcastReceiver;
Kevin Chyn42653e82018-01-19 14:15:46 -080029import android.content.Context;
Kevin Chyne181b8d2019-11-05 15:02:52 -080030import android.content.Intent;
31import android.content.IntentFilter;
Gus Prevasa7df7b22018-10-30 10:29:34 -040032import android.content.res.Configuration;
Kevin Chyn8429da22019-09-24 12:42:35 -070033import android.hardware.biometrics.BiometricConstants;
Vishwath Mohanecf00ce2018-04-05 10:28:24 -070034import android.hardware.biometrics.BiometricPrompt;
Kevin Chyn23289ef2018-11-28 16:32:36 -080035import android.hardware.biometrics.IBiometricServiceReceiverInternal;
Ilya Matyukhin0f9da352019-10-03 14:10:01 -070036import android.hardware.face.FaceManager;
37import android.hardware.fingerprint.FingerprintManager;
Kevin Chynaae4a152018-01-18 11:48:09 -080038import android.os.Bundle;
Kevin Chyn050315f2019-08-08 14:22:54 -070039import android.os.Handler;
40import android.os.Looper;
Kevin Chyn42653e82018-01-19 14:15:46 -080041import android.os.RemoteException;
Kevin Chynaae4a152018-01-18 11:48:09 -080042import android.util.Log;
Kevin Chyn42653e82018-01-19 14:15:46 -080043import android.view.WindowManager;
Kevin Chynaae4a152018-01-18 11:48:09 -080044
Ilya Matyukhin0f9da352019-10-03 14:10:01 -070045import com.android.internal.R;
Kevin Chyn050315f2019-08-08 14:22:54 -070046import com.android.internal.annotations.VisibleForTesting;
Kevin Chyn42653e82018-01-19 14:15:46 -080047import com.android.internal.os.SomeArgs;
Kevin Chynaae4a152018-01-18 11:48:09 -080048import com.android.systemui.SystemUI;
49import com.android.systemui.statusbar.CommandQueue;
50
Kevin Chyn050315f2019-08-08 14:22:54 -070051import java.util.List;
52
Dave Mankoffbcaca8a2019-10-31 18:04:08 -040053import javax.inject.Inject;
54import javax.inject.Singleton;
55
Kevin Chyne9275662018-07-23 16:42:06 -070056/**
Kevin Chync53d9812019-07-30 18:10:30 -070057 * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
58 * appropriate biometric UI (e.g. BiometricDialogView).
Kevin Chyne9275662018-07-23 16:42:06 -070059 */
Dave Mankoffbcaca8a2019-10-31 18:04:08 -040060@Singleton
Kevin Chynf8688a02019-08-27 17:04:05 -070061public class AuthController extends SystemUI implements CommandQueue.Callbacks,
62 AuthDialogCallback {
Kevin Chynfc468262019-08-20 17:17:11 -070063
Kevin Chynf8688a02019-08-27 17:04:05 -070064 private static final String TAG = "BiometricPrompt/AuthController";
Kevin Chyn42653e82018-01-19 14:15:46 -080065 private static final boolean DEBUG = true;
66
Dave Mankoffbcaca8a2019-10-31 18:04:08 -040067 private final CommandQueue mCommandQueue;
Kevin Chyn050315f2019-08-08 14:22:54 -070068 private final Injector mInjector;
69
Kevin Chync53d9812019-07-30 18:10:30 -070070 // TODO: These should just be saved from onSaveState
Gus Prevasa7df7b22018-10-30 10:29:34 -040071 private SomeArgs mCurrentDialogArgs;
Kevin Chyn050315f2019-08-08 14:22:54 -070072 @VisibleForTesting
Kevin Chynf8688a02019-08-27 17:04:05 -070073 AuthDialog mCurrentDialog;
Kevin Chync53d9812019-07-30 18:10:30 -070074
Kevin Chyn050315f2019-08-08 14:22:54 -070075 private Handler mHandler = new Handler(Looper.getMainLooper());
Kevin Chyn42653e82018-01-19 14:15:46 -080076 private WindowManager mWindowManager;
Kevin Chyn050315f2019-08-08 14:22:54 -070077 @VisibleForTesting
78 IActivityTaskManager mActivityTaskManager;
79 @VisibleForTesting
80 BiometricTaskStackListener mTaskStackListener;
81 @VisibleForTesting
82 IBiometricServiceReceiverInternal mReceiver;
83
84 public class BiometricTaskStackListener extends TaskStackListener {
85 @Override
86 public void onTaskStackChanged() {
87 mHandler.post(mTaskStackChangedRunnable);
88 }
89 }
90
Kevin Chyne181b8d2019-11-05 15:02:52 -080091 @VisibleForTesting
92 final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
93 @Override
94 public void onReceive(Context context, Intent intent) {
95 if (mCurrentDialog != null
96 && Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
97 Log.w(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received");
98 mCurrentDialog.dismissWithoutCallback(true /* animate */);
99 mCurrentDialog = null;
100
101 try {
102 if (mReceiver != null) {
Kevin Chync8cb6852020-03-10 18:29:15 -0700103 mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
104 null /* credentialAttestation */);
Kevin Chyne181b8d2019-11-05 15:02:52 -0800105 mReceiver = null;
106 }
107 } catch (RemoteException e) {
108 Log.e(TAG, "Remote exception", e);
109 }
110 }
111 }
112 };
113
Kevin Chyn050315f2019-08-08 14:22:54 -0700114 private final Runnable mTaskStackChangedRunnable = () -> {
115 if (mCurrentDialog != null) {
116 try {
117 final String clientPackage = mCurrentDialog.getOpPackageName();
118 Log.w(TAG, "Task stack changed, current client: " + clientPackage);
119 final List<ActivityManager.RunningTaskInfo> runningTasks =
120 mActivityTaskManager.getTasks(1);
121 if (!runningTasks.isEmpty()) {
122 final String topPackage = runningTasks.get(0).topActivity.getPackageName();
123 if (!topPackage.contentEquals(clientPackage)) {
124 Log.w(TAG, "Evicting client due to: " + topPackage);
125 mCurrentDialog.dismissWithoutCallback(true /* animate */);
126 mCurrentDialog = null;
Curtis Belmonteffa9d872019-10-24 12:55:01 -0700127 if (mReceiver != null) {
128 mReceiver.onDialogDismissed(
Kevin Chync8cb6852020-03-10 18:29:15 -0700129 BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
130 null /* credentialAttestation */);
Curtis Belmonteffa9d872019-10-24 12:55:01 -0700131 mReceiver = null;
132 }
Kevin Chyn050315f2019-08-08 14:22:54 -0700133 }
134 }
135 } catch (RemoteException e) {
136 Log.e(TAG, "Remote exception", e);
137 }
138 }
139 };
Kevin Chyn42653e82018-01-19 14:15:46 -0800140
Kevin Chync53d9812019-07-30 18:10:30 -0700141 @Override
142 public void onTryAgainPressed() {
Curtis Belmonteffa9d872019-10-24 12:55:01 -0700143 if (mReceiver == null) {
144 Log.e(TAG, "onTryAgainPressed: Receiver is null");
145 return;
146 }
Kevin Chync53d9812019-07-30 18:10:30 -0700147 try {
148 mReceiver.onTryAgainPressed();
149 } catch (RemoteException e) {
150 Log.e(TAG, "RemoteException when handling try again", e);
Kevin Chyn5906c172018-07-23 15:43:02 -0700151 }
152 }
153
Kevin Chync53d9812019-07-30 18:10:30 -0700154 @Override
Kevin Chynff168dc2019-09-16 16:04:38 -0700155 public void onDeviceCredentialPressed() {
Curtis Belmonteffa9d872019-10-24 12:55:01 -0700156 if (mReceiver == null) {
157 Log.e(TAG, "onDeviceCredentialPressed: Receiver is null");
158 return;
159 }
Kevin Chynff168dc2019-09-16 16:04:38 -0700160 try {
161 mReceiver.onDeviceCredentialPressed();
162 } catch (RemoteException e) {
163 Log.e(TAG, "RemoteException when handling credential button", e);
164 }
165 }
166
167 @Override
Kevin Chyn0a45b662020-03-27 10:15:50 -0700168 public void onSystemEvent(int event) {
169 if (mReceiver == null) {
170 Log.e(TAG, "onSystemEvent(" + event + "): Receiver is null");
171 return;
172 }
173 try {
174 mReceiver.onSystemEvent(event);
175 } catch (RemoteException e) {
176 Log.e(TAG, "RemoteException when sending system event", e);
177 }
178 }
179
180 @Override
Kevin Chync8cb6852020-03-10 18:29:15 -0700181 public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation) {
Kevin Chync53d9812019-07-30 18:10:30 -0700182 switch (reason) {
Kevin Chynf8688a02019-08-27 17:04:05 -0700183 case AuthDialogCallback.DISMISSED_USER_CANCELED:
Kevin Chync8cb6852020-03-10 18:29:15 -0700184 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
185 credentialAttestation);
Kevin Chync53d9812019-07-30 18:10:30 -0700186 break;
187
Kevin Chynf8688a02019-08-27 17:04:05 -0700188 case AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE:
Kevin Chync8cb6852020-03-10 18:29:15 -0700189 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE,
190 credentialAttestation);
Kevin Chync53d9812019-07-30 18:10:30 -0700191 break;
192
Kevin Chynf8688a02019-08-27 17:04:05 -0700193 case AuthDialogCallback.DISMISSED_BUTTON_POSITIVE:
Kevin Chync8cb6852020-03-10 18:29:15 -0700194 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
195 credentialAttestation);
Kevin Chync53d9812019-07-30 18:10:30 -0700196 break;
197
Kevin Chynff168dc2019-09-16 16:04:38 -0700198 case AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED:
199 sendResultAndCleanUp(
Kevin Chync8cb6852020-03-10 18:29:15 -0700200 BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED,
201 credentialAttestation);
Kevin Chync53d9812019-07-30 18:10:30 -0700202 break;
203
Kevin Chynf8688a02019-08-27 17:04:05 -0700204 case AuthDialogCallback.DISMISSED_ERROR:
Kevin Chync8cb6852020-03-10 18:29:15 -0700205 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR,
206 credentialAttestation);
Kevin Chync53d9812019-07-30 18:10:30 -0700207 break;
Kevin Chyn050315f2019-08-08 14:22:54 -0700208
Kevin Chynf8688a02019-08-27 17:04:05 -0700209 case AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER:
Kevin Chync8cb6852020-03-10 18:29:15 -0700210 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED,
211 credentialAttestation);
Kevin Chyn050315f2019-08-08 14:22:54 -0700212 break;
213
Kevin Chynff168dc2019-09-16 16:04:38 -0700214 case AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED:
Kevin Chync8cb6852020-03-10 18:29:15 -0700215 sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED,
216 credentialAttestation);
Kevin Chynff168dc2019-09-16 16:04:38 -0700217 break;
218
Kevin Chync53d9812019-07-30 18:10:30 -0700219 default:
220 Log.e(TAG, "Unhandled reason: " + reason);
221 break;
Kevin Chync94b7db2019-05-15 17:28:16 -0700222 }
Kevin Chync53d9812019-07-30 18:10:30 -0700223 }
224
Kevin Chync8cb6852020-03-10 18:29:15 -0700225 private void sendResultAndCleanUp(@DismissedReason int reason,
226 @Nullable byte[] credentialAttestation) {
Kevin Chync53d9812019-07-30 18:10:30 -0700227 if (mReceiver == null) {
Curtis Belmonteffa9d872019-10-24 12:55:01 -0700228 Log.e(TAG, "sendResultAndCleanUp: Receiver is null");
Kevin Chync53d9812019-07-30 18:10:30 -0700229 return;
230 }
231 try {
Kevin Chync8cb6852020-03-10 18:29:15 -0700232 mReceiver.onDialogDismissed(reason, credentialAttestation);
Kevin Chync53d9812019-07-30 18:10:30 -0700233 } catch (RemoteException e) {
234 Log.w(TAG, "Remote exception", e);
235 }
Kevin Chyn050315f2019-08-08 14:22:54 -0700236 onDialogDismissed(reason);
237 }
238
239 public static class Injector {
240 IActivityTaskManager getActivityTaskManager() {
241 return ActivityTaskManager.getService();
242 }
243 }
244
Dave Mankoffbcaca8a2019-10-31 18:04:08 -0400245 @Inject
246 public AuthController(Context context, CommandQueue commandQueue) {
247 this(context, commandQueue, new Injector());
Kevin Chyn050315f2019-08-08 14:22:54 -0700248 }
249
250 @VisibleForTesting
Dave Mankoffbcaca8a2019-10-31 18:04:08 -0400251 AuthController(Context context, CommandQueue commandQueue, Injector injector) {
Dave Mankoffa5d8a392019-10-10 12:21:09 -0400252 super(context);
Dave Mankoffbcaca8a2019-10-31 18:04:08 -0400253 mCommandQueue = commandQueue;
Kevin Chyn050315f2019-08-08 14:22:54 -0700254 mInjector = injector;
Kevin Chyne181b8d2019-11-05 15:02:52 -0800255
256 IntentFilter filter = new IntentFilter();
257 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
258
259 context.registerReceiver(mBroadcastReceiver, filter);
Kevin Chync53d9812019-07-30 18:10:30 -0700260 }
Kevin Chync94b7db2019-05-15 17:28:16 -0700261
Kevin Chynaae4a152018-01-18 11:48:09 -0800262 @Override
263 public void start() {
Kevin Chyn4e6417d2020-02-20 12:10:21 -0800264 mCommandQueue.addCallback(this);
265 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
266 mActivityTaskManager = mInjector.getActivityTaskManager();
Kevin Chyn050315f2019-08-08 14:22:54 -0700267
Kevin Chyn4e6417d2020-02-20 12:10:21 -0800268 try {
269 mTaskStackListener = new BiometricTaskStackListener();
270 mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
271 } catch (RemoteException e) {
272 Log.w(TAG, "Unable to register task stack listener", e);
Gus Prevasa7df7b22018-10-30 10:29:34 -0400273 }
274 }
275
Kevin Chynaae4a152018-01-18 11:48:09 -0800276 @Override
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700277 public void showAuthenticationDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
Kevin Chync8cb6852020-03-10 18:29:15 -0700278 int biometricModality, boolean requireConfirmation, int userId, String opPackageName,
279 long operationId) {
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700280 final int authenticators = Utils.getAuthenticators(bundle);
281
Kevin Chyn158fefb2019-01-03 18:59:05 -0800282 if (DEBUG) {
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700283 Log.d(TAG, "showAuthenticationDialog, authenticators: " + authenticators
284 + ", biometricModality: " + biometricModality
Kevin Chync8cb6852020-03-10 18:29:15 -0700285 + ", requireConfirmation: " + requireConfirmation
286 + ", operationId: " + operationId);
Kevin Chyn158fefb2019-01-03 18:59:05 -0800287 }
Kevin Chyn42653e82018-01-19 14:15:46 -0800288 SomeArgs args = SomeArgs.obtain();
289 args.arg1 = bundle;
290 args.arg2 = receiver;
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700291 args.argi1 = biometricModality;
Kevin Chyn6cf54e82018-09-18 19:13:27 -0700292 args.arg3 = requireConfirmation;
Kevin Chyn1b9f8df2018-11-12 19:04:55 -0800293 args.argi2 = userId;
Kevin Chyn050315f2019-08-08 14:22:54 -0700294 args.arg4 = opPackageName;
Kevin Chync8cb6852020-03-10 18:29:15 -0700295 args.arg5 = operationId;
Kevin Chync53d9812019-07-30 18:10:30 -0700296
297 boolean skipAnimation = false;
298 if (mCurrentDialog != null) {
299 Log.w(TAG, "mCurrentDialog: " + mCurrentDialog);
300 skipAnimation = true;
301 }
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700302
303 showDialog(args, skipAnimation, null /* savedState */);
Kevin Chynaae4a152018-01-18 11:48:09 -0800304 }
305
306 @Override
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700307 public void onBiometricAuthenticated() {
308 mCurrentDialog.onAuthenticationSucceeded();
Kevin Chyn42653e82018-01-19 14:15:46 -0800309 }
310
Kevin Chync53d9812019-07-30 18:10:30 -0700311 @Override
312 public void onBiometricHelp(String message) {
313 if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
314
315 mCurrentDialog.onHelp(message);
Kevin Chyn42653e82018-01-19 14:15:46 -0800316 }
317
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700318 private String getErrorString(int modality, int error, int vendorCode) {
319 switch (modality) {
320 case TYPE_FACE:
321 return FaceManager.getErrorString(mContext, error, vendorCode);
Kevin Chyn8429da22019-09-24 12:42:35 -0700322
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700323 case TYPE_FINGERPRINT:
324 return FingerprintManager.getErrorString(mContext, error, vendorCode);
325
326 default:
327 return "";
328 }
329 }
330
331 @Override
332 public void onBiometricError(int modality, int error, int vendorCode) {
333 if (DEBUG) {
334 Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode));
335 }
336
337 final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
338 || (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
339
340 // TODO(b/141025588): Create separate methods for handling hard and soft errors.
341 final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED
342 || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT);
343
Kevin Chyn8429da22019-09-24 12:42:35 -0700344 if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) {
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700345 if (DEBUG) Log.d(TAG, "onBiometricError, lockout");
Kevin Chyn8429da22019-09-24 12:42:35 -0700346 mCurrentDialog.animateToCredentialUI();
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700347 } else if (isSoftError) {
348 final String errorMessage = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED)
349 ? mContext.getString(R.string.biometric_not_recognized)
350 : getErrorString(modality, error, vendorCode);
351 if (DEBUG) Log.d(TAG, "onBiometricError, soft error: " + errorMessage);
352 mCurrentDialog.onAuthenticationFailed(errorMessage);
Kevin Chyn8429da22019-09-24 12:42:35 -0700353 } else {
Ilya Matyukhin0f9da352019-10-03 14:10:01 -0700354 final String errorMessage = getErrorString(modality, error, vendorCode);
355 if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage);
356 mCurrentDialog.onError(errorMessage);
Kevin Chyn8429da22019-09-24 12:42:35 -0700357 }
Kevin Chyn42653e82018-01-19 14:15:46 -0800358 }
359
Kevin Chync53d9812019-07-30 18:10:30 -0700360 @Override
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700361 public void hideAuthenticationDialog() {
Kevin Chyna847a032020-01-17 14:17:03 -0800362 if (DEBUG) Log.d(TAG, "hideAuthenticationDialog: " + mCurrentDialog);
363
364 if (mCurrentDialog == null) {
365 // Could be possible if the caller canceled authentication after credential success
366 // but before the client was notified.
367 return;
368 }
Kevin Chync53d9812019-07-30 18:10:30 -0700369
Kevin Chyn050315f2019-08-08 14:22:54 -0700370 mCurrentDialog.dismissFromSystemServer();
Kevin Chyn22910722019-12-13 16:57:51 -0800371
372 // BiometricService will have already sent the callback to the client in this case.
373 // This avoids a round trip to SystemUI. So, just dismiss the dialog and we're done.
374 mCurrentDialog = null;
Kevin Chync53d9812019-07-30 18:10:30 -0700375 }
376
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700377 private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
Kevin Chync53d9812019-07-30 18:10:30 -0700378 mCurrentDialogArgs = args;
379 final int type = args.argi1;
Kevin Chyn050315f2019-08-08 14:22:54 -0700380 final Bundle biometricPromptBundle = (Bundle) args.arg1;
381 final boolean requireConfirmation = (boolean) args.arg3;
382 final int userId = args.argi2;
383 final String opPackageName = (String) args.arg4;
Kevin Chync8cb6852020-03-10 18:29:15 -0700384 final long operationId = (long) args.arg5;
Kevin Chync53d9812019-07-30 18:10:30 -0700385
386 // Create a new dialog but do not replace the current one yet.
Kevin Chynf8688a02019-08-27 17:04:05 -0700387 final AuthDialog newDialog = buildDialog(
Kevin Chyn050315f2019-08-08 14:22:54 -0700388 biometricPromptBundle,
389 requireConfirmation,
390 userId,
391 type,
Kevin Chynfc468262019-08-20 17:17:11 -0700392 opPackageName,
Kevin Chync8cb6852020-03-10 18:29:15 -0700393 skipAnimation,
394 operationId);
Kevin Chync53d9812019-07-30 18:10:30 -0700395
396 if (newDialog == null) {
397 Log.e(TAG, "Unsupported type: " + type);
Kevin Chyn42653e82018-01-19 14:15:46 -0800398 return;
399 }
Kevin Chync53d9812019-07-30 18:10:30 -0700400
401 if (DEBUG) {
Kevin Chynbc3347f2020-02-20 16:45:59 -0800402 Log.d(TAG, "userId: " + userId
Kevin Chync53d9812019-07-30 18:10:30 -0700403 + " savedState: " + savedState
404 + " mCurrentDialog: " + mCurrentDialog
405 + " newDialog: " + newDialog
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700406 + " type: " + type);
Kevin Chync53d9812019-07-30 18:10:30 -0700407 }
408
Kevin Chyn9cf89912019-08-30 13:33:58 -0700409 if (mCurrentDialog != null) {
Kevin Chync53d9812019-07-30 18:10:30 -0700410 // If somehow we're asked to show a dialog, the old one doesn't need to be animated
411 // away. This can happen if the app cancels and re-starts auth during configuration
412 // change. This is ugly because we also have to do things on onConfigurationChanged
413 // here.
414 mCurrentDialog.dismissWithoutCallback(false /* animate */);
415 }
416
417 mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
418 mCurrentDialog = newDialog;
Kevin Chyn9cf89912019-08-30 13:33:58 -0700419 mCurrentDialog.show(mWindowManager, savedState);
Kevin Chync53d9812019-07-30 18:10:30 -0700420 }
421
Kevin Chyn050315f2019-08-08 14:22:54 -0700422 private void onDialogDismissed(@DismissedReason int reason) {
423 if (DEBUG) Log.d(TAG, "onDialogDismissed: " + reason);
Kevin Chync53d9812019-07-30 18:10:30 -0700424 if (mCurrentDialog == null) {
425 Log.w(TAG, "Dialog already dismissed");
Kevin Chyn42653e82018-01-19 14:15:46 -0800426 }
427 mReceiver = null;
Kevin Chync53d9812019-07-30 18:10:30 -0700428 mCurrentDialog = null;
Kevin Chyn23289ef2018-11-28 16:32:36 -0800429 }
430
Gus Prevasa7df7b22018-10-30 10:29:34 -0400431 @Override
432 protected void onConfigurationChanged(Configuration newConfig) {
Kevin Chyn02129b12018-11-01 16:47:12 -0700433 super.onConfigurationChanged(newConfig);
Kevin Chyne1912712019-01-04 14:22:34 -0800434
435 // Save the state of the current dialog (buttons showing, etc)
Kevin Chyne1912712019-01-04 14:22:34 -0800436 if (mCurrentDialog != null) {
Kevin Chync53d9812019-07-30 18:10:30 -0700437 final Bundle savedState = new Bundle();
Kevin Chyne1912712019-01-04 14:22:34 -0800438 mCurrentDialog.onSaveState(savedState);
Kevin Chync53d9812019-07-30 18:10:30 -0700439 mCurrentDialog.dismissWithoutCallback(false /* animate */);
440 mCurrentDialog = null;
Kevin Chyne1912712019-01-04 14:22:34 -0800441
Kevin Chyn27da7182019-09-11 12:17:55 -0700442 // Only show the dialog if necessary. If it was animating out, the dialog is supposed
443 // to send its pending callback immediately.
444 if (savedState.getInt(AuthDialog.KEY_CONTAINER_STATE)
445 != AuthContainerView.STATE_ANIMATING_OUT) {
Kevin Chyn8cbb4882019-09-19 16:49:02 -0700446 final boolean credentialShowing =
447 savedState.getBoolean(AuthDialog.KEY_CREDENTIAL_SHOWING);
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700448 if (credentialShowing) {
449 // TODO: Clean this up
450 Bundle bundle = (Bundle) mCurrentDialogArgs.arg1;
451 bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
Curtis Belmonte13eb5812019-10-22 14:17:30 -0700452 Authenticators.DEVICE_CREDENTIAL);
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700453 }
Kevin Chyn8cbb4882019-09-19 16:49:02 -0700454
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700455 showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
Kevin Chyn27da7182019-09-11 12:17:55 -0700456 }
Gus Prevasa7df7b22018-10-30 10:29:34 -0400457 }
Kevin Chync53d9812019-07-30 18:10:30 -0700458 }
Kevin Chyne1912712019-01-04 14:22:34 -0800459
Kevin Chynf8688a02019-08-27 17:04:05 -0700460 protected AuthDialog buildDialog(Bundle biometricPromptBundle, boolean requireConfirmation,
Kevin Chync8cb6852020-03-10 18:29:15 -0700461 int userId, int type, String opPackageName, boolean skipIntro, long operationId) {
Kevin Chynd837ced2019-09-11 16:09:43 -0700462 return new AuthContainerView.Builder(mContext)
463 .setCallback(this)
464 .setBiometricPromptBundle(biometricPromptBundle)
465 .setRequireConfirmation(requireConfirmation)
466 .setUserId(userId)
467 .setOpPackageName(opPackageName)
468 .setSkipIntro(skipIntro)
Kevin Chync8cb6852020-03-10 18:29:15 -0700469 .setOperationId(operationId)
Kevin Chynd837ced2019-09-11 16:09:43 -0700470 .build(type);
Gus Prevasa7df7b22018-10-30 10:29:34 -0400471 }
Kevin Chynaae4a152018-01-18 11:48:09 -0800472}