blob: 8bf2591825449e2aaa0772ab9e1eaf7cd41b4980 [file] [log] [blame]
Kevin Chynff168dc2019-09-16 16:04:38 -07001/*
2 * Copyright (C) 2019 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
17package com.android.systemui.biometrics;
18
Curtis Belmontebecc0a72020-03-16 16:13:56 -070019import android.app.AlertDialog;
20import android.app.admin.DevicePolicyManager;
Kevin Chynff168dc2019-09-16 16:04:38 -070021import android.content.Context;
Curtis Belmontebecc0a72020-03-16 16:13:56 -070022import android.content.pm.UserInfo;
Alex Johnston5179ac62020-01-07 14:23:26 +000023import android.graphics.drawable.Drawable;
Kevin Chynff168dc2019-09-16 16:04:38 -070024import android.hardware.biometrics.BiometricPrompt;
25import android.os.AsyncTask;
26import android.os.Bundle;
27import android.os.CountDownTimer;
28import android.os.Handler;
29import android.os.Looper;
30import android.os.SystemClock;
Curtis Belmontebecc0a72020-03-16 16:13:56 -070031import android.os.UserManager;
Kevin Chynff168dc2019-09-16 16:04:38 -070032import android.text.TextUtils;
33import android.util.AttributeSet;
34import android.view.View;
Curtis Belmontebecc0a72020-03-16 16:13:56 -070035import android.view.WindowManager;
Kevin Chynff168dc2019-09-16 16:04:38 -070036import android.view.accessibility.AccessibilityManager;
Alex Johnston5179ac62020-01-07 14:23:26 +000037import android.widget.ImageView;
Kevin Chynff168dc2019-09-16 16:04:38 -070038import android.widget.LinearLayout;
39import android.widget.TextView;
40
Curtis Belmontebecc0a72020-03-16 16:13:56 -070041import androidx.annotation.IntDef;
42import androidx.annotation.NonNull;
Curtis Belmonte4584d882020-02-11 13:12:36 -080043import androidx.annotation.Nullable;
Curtis Belmontebecc0a72020-03-16 16:13:56 -070044import androidx.annotation.StringRes;
Curtis Belmonte4584d882020-02-11 13:12:36 -080045
Kevin Chynff168dc2019-09-16 16:04:38 -070046import com.android.internal.widget.LockPatternUtils;
Kevin Chynff168dc2019-09-16 16:04:38 -070047import com.android.systemui.Interpolators;
48import com.android.systemui.R;
49
Curtis Belmontebecc0a72020-03-16 16:13:56 -070050import java.lang.annotation.Retention;
51import java.lang.annotation.RetentionPolicy;
52
Kevin Chynff168dc2019-09-16 16:04:38 -070053/**
Kevin Chyn8110ebb2019-10-02 11:16:31 -070054 * Abstract base class for Pin, Pattern, or Password authentication, for
Kevin Chync8cb6852020-03-10 18:29:15 -070055 * {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}}
Kevin Chynff168dc2019-09-16 16:04:38 -070056 */
Kevin Chyn8110ebb2019-10-02 11:16:31 -070057public abstract class AuthCredentialView extends LinearLayout {
Kevin Chyn8110ebb2019-10-02 11:16:31 -070058 private static final String TAG = "BiometricPrompt/AuthCredentialView";
Kevin Chynff168dc2019-09-16 16:04:38 -070059 private static final int ERROR_DURATION_MS = 3000;
60
Curtis Belmontebecc0a72020-03-16 16:13:56 -070061 static final int USER_TYPE_PRIMARY = 1;
62 static final int USER_TYPE_MANAGED_PROFILE = 2;
63 static final int USER_TYPE_SECONDARY = 3;
64 @Retention(RetentionPolicy.SOURCE)
65 @IntDef({USER_TYPE_PRIMARY, USER_TYPE_MANAGED_PROFILE, USER_TYPE_SECONDARY})
66 private @interface UserType {}
Kevin Chynff168dc2019-09-16 16:04:38 -070067
Kevin Chyn8110ebb2019-10-02 11:16:31 -070068 protected final Handler mHandler;
Curtis Belmontebecc0a72020-03-16 16:13:56 -070069 protected final LockPatternUtils mLockPatternUtils;
70
71 private final AccessibilityManager mAccessibilityManager;
72 private final UserManager mUserManager;
73 private final DevicePolicyManager mDevicePolicyManager;
Kevin Chyn8110ebb2019-10-02 11:16:31 -070074
Kevin Chynff168dc2019-09-16 16:04:38 -070075 private Bundle mBiometricPromptBundle;
Kevin Chyn8cbb4882019-09-19 16:49:02 -070076 private AuthPanelController mPanelController;
77 private boolean mShouldAnimatePanel;
Kevin Chyn86f1b8e2019-09-24 19:00:49 -070078 private boolean mShouldAnimateContents;
Kevin Chynff168dc2019-09-16 16:04:38 -070079
80 private TextView mTitleView;
81 private TextView mSubtitleView;
82 private TextView mDescriptionView;
Alex Johnston5179ac62020-01-07 14:23:26 +000083 private ImageView mIconView;
Kevin Chyn8110ebb2019-10-02 11:16:31 -070084 protected TextView mErrorView;
85
86 protected @Utils.CredentialType int mCredentialType;
Kevin Chyn8110ebb2019-10-02 11:16:31 -070087 protected AuthContainerView mContainerView;
88 protected Callback mCallback;
89 protected AsyncTask<?, ?, ?> mPendingLockCheck;
Kevin Chynbc3347f2020-02-20 16:45:59 -080090 protected int mUserId;
Kevin Chync8cb6852020-03-10 18:29:15 -070091 protected long mOperationId;
Kevin Chynb54bdfc2020-01-17 16:07:00 -080092 protected int mEffectiveUserId;
Kevin Chyn8110ebb2019-10-02 11:16:31 -070093 protected ErrorTimer mErrorTimer;
Kevin Chynff168dc2019-09-16 16:04:38 -070094
95 interface Callback {
Kevin Chync8cb6852020-03-10 18:29:15 -070096 void onCredentialMatched(byte[] attestation);
Kevin Chynff168dc2019-09-16 16:04:38 -070097 }
98
Kevin Chyn8110ebb2019-10-02 11:16:31 -070099 protected static class ErrorTimer extends CountDownTimer {
Kevin Chynff168dc2019-09-16 16:04:38 -0700100 private final TextView mErrorView;
101 private final Context mContext;
102
103 /**
104 * @param millisInFuture The number of millis in the future from the call
105 * to {@link #start()} until the countdown is done and {@link
106 * #onFinish()}
107 * is called.
108 * @param countDownInterval The interval along the way to receive
109 * {@link #onTick(long)} callbacks.
110 */
111 public ErrorTimer(Context context, long millisInFuture, long countDownInterval,
112 TextView errorView) {
113 super(millisInFuture, countDownInterval);
114 mErrorView = errorView;
115 mContext = context;
116 }
117
118 @Override
119 public void onTick(long millisUntilFinished) {
120 final int secondsCountdown = (int) (millisUntilFinished / 1000);
121 mErrorView.setText(mContext.getString(
122 R.string.biometric_dialog_credential_too_many_attempts, secondsCountdown));
123 }
124
125 @Override
126 public void onFinish() {
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700127 if (mErrorView != null) {
128 mErrorView.setText("");
129 }
Kevin Chynff168dc2019-09-16 16:04:38 -0700130 }
131 }
132
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700133 protected final Runnable mClearErrorRunnable = new Runnable() {
Kevin Chynff168dc2019-09-16 16:04:38 -0700134 @Override
135 public void run() {
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700136 if (mErrorView != null) {
137 mErrorView.setText("");
138 }
Kevin Chynff168dc2019-09-16 16:04:38 -0700139 }
140 };
141
142 public AuthCredentialView(Context context, AttributeSet attrs) {
143 super(context, attrs);
144
Kevin Chynff168dc2019-09-16 16:04:38 -0700145 mLockPatternUtils = new LockPatternUtils(mContext);
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700146 mHandler = new Handler(Looper.getMainLooper());
Kevin Chynff168dc2019-09-16 16:04:38 -0700147 mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700148 mUserManager = mContext.getSystemService(UserManager.class);
149 mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
Kevin Chynff168dc2019-09-16 16:04:38 -0700150 }
151
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700152 protected void showError(String error) {
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700153 if (mHandler != null) {
154 mHandler.removeCallbacks(mClearErrorRunnable);
155 mHandler.postDelayed(mClearErrorRunnable, ERROR_DURATION_MS);
156 }
157 if (mErrorView != null) {
158 mErrorView.setText(error);
159 }
Kevin Chynff168dc2019-09-16 16:04:38 -0700160 }
161
Curtis Belmonte4584d882020-02-11 13:12:36 -0800162 private void setTextOrHide(TextView view, CharSequence text) {
163 if (TextUtils.isEmpty(text)) {
Kevin Chynff168dc2019-09-16 16:04:38 -0700164 view.setVisibility(View.GONE);
165 } else {
Curtis Belmonte4584d882020-02-11 13:12:36 -0800166 view.setText(text);
Kevin Chynff168dc2019-09-16 16:04:38 -0700167 }
168
169 Utils.notifyAccessibilityContentChanged(mAccessibilityManager, this);
170 }
171
Curtis Belmonte4584d882020-02-11 13:12:36 -0800172 private void setText(TextView view, CharSequence text) {
173 view.setText(text);
Kevin Chynff168dc2019-09-16 16:04:38 -0700174 }
175
Kevin Chynbc3347f2020-02-20 16:45:59 -0800176 void setUserId(int userId) {
177 mUserId = userId;
178 }
179
Kevin Chync8cb6852020-03-10 18:29:15 -0700180 void setOperationId(long operationId) {
181 mOperationId = operationId;
182 }
183
Kevin Chynb54bdfc2020-01-17 16:07:00 -0800184 void setEffectiveUserId(int effectiveUserId) {
185 mEffectiveUserId = effectiveUserId;
186 }
187
188 void setCredentialType(@Utils.CredentialType int credentialType) {
189 mCredentialType = credentialType;
Kevin Chynff168dc2019-09-16 16:04:38 -0700190 }
191
192 void setCallback(Callback callback) {
193 mCallback = callback;
194 }
195
196 void setBiometricPromptBundle(Bundle bundle) {
197 mBiometricPromptBundle = bundle;
198 }
199
Kevin Chyn8cbb4882019-09-19 16:49:02 -0700200 void setPanelController(AuthPanelController panelController, boolean animatePanel) {
201 mPanelController = panelController;
202 mShouldAnimatePanel = animatePanel;
203 }
204
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700205 void setShouldAnimateContents(boolean animateContents) {
206 mShouldAnimateContents = animateContents;
207 }
208
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700209 void setContainerView(AuthContainerView containerView) {
210 mContainerView = containerView;
211 }
212
Kevin Chynff168dc2019-09-16 16:04:38 -0700213 @Override
214 protected void onAttachedToWindow() {
215 super.onAttachedToWindow();
216
Curtis Belmonteda9204a2020-04-03 11:51:10 -0700217 final CharSequence title = getTitle(mBiometricPromptBundle);
218 setText(mTitleView, title);
Curtis Belmonte4584d882020-02-11 13:12:36 -0800219 setTextOrHide(mSubtitleView, getSubtitle(mBiometricPromptBundle));
220 setTextOrHide(mDescriptionView, getDescription(mBiometricPromptBundle));
Curtis Belmonteda9204a2020-04-03 11:51:10 -0700221 announceForAccessibility(title);
Kevin Chynff168dc2019-09-16 16:04:38 -0700222
Alex Johnston5179ac62020-01-07 14:23:26 +0000223 final boolean isManagedProfile = Utils.isManagedProfile(mContext, mEffectiveUserId);
224 final Drawable image;
225 if (isManagedProfile) {
226 image = getResources().getDrawable(R.drawable.auth_dialog_enterprise,
227 mContext.getTheme());
228 } else {
229 image = getResources().getDrawable(R.drawable.auth_dialog_lock, mContext.getTheme());
230 }
231 mIconView.setImageDrawable(image);
232
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700233 // Only animate this if we're transitioning from a biometric view.
234 if (mShouldAnimateContents) {
235 setTranslationY(getResources()
236 .getDimension(R.dimen.biometric_dialog_credential_translation_offset));
237 setAlpha(0);
Kevin Chynff168dc2019-09-16 16:04:38 -0700238
Kevin Chyn86f1b8e2019-09-24 19:00:49 -0700239 postOnAnimation(() -> {
240 animate().translationY(0)
241 .setDuration(AuthDialog.ANIMATE_CREDENTIAL_INITIAL_DURATION_MS)
242 .alpha(1.f)
243 .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
244 .withLayer()
245 .start();
246 });
247 }
Kevin Chynff168dc2019-09-16 16:04:38 -0700248 }
249
250 @Override
251 protected void onDetachedFromWindow() {
252 super.onDetachedFromWindow();
253 if (mErrorTimer != null) {
254 mErrorTimer.cancel();
255 }
256 }
257
258 @Override
259 protected void onFinishInflate() {
260 super.onFinishInflate();
261 mTitleView = findViewById(R.id.title);
262 mSubtitleView = findViewById(R.id.subtitle);
263 mDescriptionView = findViewById(R.id.description);
Alex Johnston5179ac62020-01-07 14:23:26 +0000264 mIconView = findViewById(R.id.icon);
Kevin Chynff168dc2019-09-16 16:04:38 -0700265 mErrorView = findViewById(R.id.error);
Kevin Chynff168dc2019-09-16 16:04:38 -0700266 }
267
Kevin Chyn8cbb4882019-09-19 16:49:02 -0700268 @Override
269 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
270 super.onLayout(changed, left, top, right, bottom);
271
272 if (mShouldAnimatePanel) {
273 // Credential view is always full screen.
274 mPanelController.setUseFullScreen(true);
275 mPanelController.updateForContentDimensions(mPanelController.getContainerWidth(),
276 mPanelController.getContainerHeight(), 0 /* animateDurationMs */);
277 mShouldAnimatePanel = false;
278 }
279 }
280
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700281 protected void onErrorTimeoutFinish() {}
282
Kevin Chync8cb6852020-03-10 18:29:15 -0700283 protected void onCredentialVerified(byte[] attestation, int timeoutMs) {
284
285 final boolean matched = attestation != null;
286
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700287 if (matched) {
288 mClearErrorRunnable.run();
Kevin Chync8cb6852020-03-10 18:29:15 -0700289 mCallback.onCredentialMatched(attestation);
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700290 } else {
291 if (timeoutMs > 0) {
292 mHandler.removeCallbacks(mClearErrorRunnable);
Kevin Chynb54bdfc2020-01-17 16:07:00 -0800293 long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
294 mEffectiveUserId, timeoutMs);
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700295 mErrorTimer = new ErrorTimer(mContext,
296 deadline - SystemClock.elapsedRealtime(),
297 LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS,
298 mErrorView) {
299 @Override
300 public void onFinish() {
301 onErrorTimeoutFinish();
302 mClearErrorRunnable.run();
303 }
304 };
305 mErrorTimer.start();
306 } else {
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700307 final boolean didUpdateErrorText = reportFailedAttempt();
308 if (!didUpdateErrorText) {
309 final @StringRes int errorRes;
310 switch (mCredentialType) {
311 case Utils.CREDENTIAL_PIN:
312 errorRes = R.string.biometric_dialog_wrong_pin;
313 break;
314 case Utils.CREDENTIAL_PATTERN:
315 errorRes = R.string.biometric_dialog_wrong_pattern;
316 break;
317 case Utils.CREDENTIAL_PASSWORD:
318 default:
319 errorRes = R.string.biometric_dialog_wrong_password;
320 break;
321 }
322 showError(getResources().getString(errorRes));
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700323 }
Kevin Chyn8110ebb2019-10-02 11:16:31 -0700324 }
325 }
326 }
Curtis Belmonte4584d882020-02-11 13:12:36 -0800327
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700328 private boolean reportFailedAttempt() {
329 boolean result = updateErrorMessage(
330 mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId) + 1);
331 mLockPatternUtils.reportFailedPasswordAttempt(mEffectiveUserId);
332 return result;
333 }
334
335 private boolean updateErrorMessage(int numAttempts) {
336 // Don't show any message if there's no maximum number of attempts.
337 final int maxAttempts = mLockPatternUtils.getMaximumFailedPasswordsForWipe(
338 mEffectiveUserId);
339 if (maxAttempts <= 0 || numAttempts <= 0) {
340 return false;
341 }
342
343 // Update the on-screen error string.
344 if (mErrorView != null) {
345 final String message = getResources().getString(
346 R.string.biometric_dialog_credential_attempts_before_wipe,
347 numAttempts,
348 maxAttempts);
349 showError(message);
350 }
351
Curtis Belmonte1e6a3562020-03-24 18:07:08 -0700352 // Only show dialog if <=1 attempts are left before wiping.
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700353 final int remainingAttempts = maxAttempts - numAttempts;
Curtis Belmonte1e6a3562020-03-24 18:07:08 -0700354 if (remainingAttempts == 1) {
355 showLastAttemptBeforeWipeDialog();
356 } else if (remainingAttempts <= 0) {
357 showNowWipingDialog();
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700358 }
359 return true;
360 }
361
Curtis Belmonte1e6a3562020-03-24 18:07:08 -0700362 private void showLastAttemptBeforeWipeDialog() {
363 final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
364 .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title)
365 .setMessage(
366 getLastAttemptBeforeWipeMessageRes(getUserTypeForWipe(), mCredentialType))
367 .setPositiveButton(android.R.string.ok, null)
368 .create();
369 alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
370 alertDialog.show();
371 }
372
373 private void showNowWipingDialog() {
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700374 final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
375 .setMessage(getNowWipingMessageRes(getUserTypeForWipe()))
376 .setPositiveButton(R.string.biometric_dialog_now_wiping_dialog_dismiss, null)
Curtis Belmonte1e6a3562020-03-24 18:07:08 -0700377 .setOnDismissListener(
378 dialog -> mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR))
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700379 .create();
Curtis Belmonte1e6a3562020-03-24 18:07:08 -0700380 alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700381 alertDialog.show();
382 }
383
384 private @UserType int getUserTypeForWipe() {
385 final UserInfo userToBeWiped = mUserManager.getUserInfo(
386 mDevicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(mEffectiveUserId));
387 if (userToBeWiped == null || userToBeWiped.isPrimary()) {
388 return USER_TYPE_PRIMARY;
389 } else if (userToBeWiped.isManagedProfile()) {
390 return USER_TYPE_MANAGED_PROFILE;
391 } else {
392 return USER_TYPE_SECONDARY;
393 }
394 }
395
Curtis Belmonte1e6a3562020-03-24 18:07:08 -0700396 private static @StringRes int getLastAttemptBeforeWipeMessageRes(
397 @UserType int userType, @Utils.CredentialType int credentialType) {
398 switch (userType) {
399 case USER_TYPE_PRIMARY:
400 return getLastAttemptBeforeWipeDeviceMessageRes(credentialType);
401 case USER_TYPE_MANAGED_PROFILE:
402 return getLastAttemptBeforeWipeProfileMessageRes(credentialType);
403 case USER_TYPE_SECONDARY:
404 return getLastAttemptBeforeWipeUserMessageRes(credentialType);
405 default:
406 throw new IllegalArgumentException("Unrecognized user type:" + userType);
407 }
408 }
409
410 private static @StringRes int getLastAttemptBeforeWipeDeviceMessageRes(
411 @Utils.CredentialType int credentialType) {
412 switch (credentialType) {
413 case Utils.CREDENTIAL_PIN:
414 return R.string.biometric_dialog_last_pin_attempt_before_wipe_device;
415 case Utils.CREDENTIAL_PATTERN:
416 return R.string.biometric_dialog_last_pattern_attempt_before_wipe_device;
417 case Utils.CREDENTIAL_PASSWORD:
418 default:
419 return R.string.biometric_dialog_last_password_attempt_before_wipe_device;
420 }
421 }
422
423 private static @StringRes int getLastAttemptBeforeWipeProfileMessageRes(
424 @Utils.CredentialType int credentialType) {
425 switch (credentialType) {
426 case Utils.CREDENTIAL_PIN:
427 return R.string.biometric_dialog_last_pin_attempt_before_wipe_profile;
428 case Utils.CREDENTIAL_PATTERN:
429 return R.string.biometric_dialog_last_pattern_attempt_before_wipe_profile;
430 case Utils.CREDENTIAL_PASSWORD:
431 default:
432 return R.string.biometric_dialog_last_password_attempt_before_wipe_profile;
433 }
434 }
435
436 private static @StringRes int getLastAttemptBeforeWipeUserMessageRes(
437 @Utils.CredentialType int credentialType) {
438 switch (credentialType) {
439 case Utils.CREDENTIAL_PIN:
440 return R.string.biometric_dialog_last_pin_attempt_before_wipe_user;
441 case Utils.CREDENTIAL_PATTERN:
442 return R.string.biometric_dialog_last_pattern_attempt_before_wipe_user;
443 case Utils.CREDENTIAL_PASSWORD:
444 default:
445 return R.string.biometric_dialog_last_password_attempt_before_wipe_user;
446 }
447 }
448
Curtis Belmontebecc0a72020-03-16 16:13:56 -0700449 private static @StringRes int getNowWipingMessageRes(@UserType int userType) {
450 switch (userType) {
451 case USER_TYPE_PRIMARY:
452 return R.string.biometric_dialog_failed_attempts_now_wiping_device;
453 case USER_TYPE_MANAGED_PROFILE:
454 return R.string.biometric_dialog_failed_attempts_now_wiping_profile;
455 case USER_TYPE_SECONDARY:
456 return R.string.biometric_dialog_failed_attempts_now_wiping_user;
457 default:
458 throw new IllegalArgumentException("Unrecognized user type:" + userType);
459 }
460 }
461
Curtis Belmonte4584d882020-02-11 13:12:36 -0800462 @Nullable
463 private static CharSequence getTitle(@NonNull Bundle bundle) {
464 final CharSequence credentialTitle =
465 bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_TITLE);
466 return credentialTitle != null ? credentialTitle
467 : bundle.getCharSequence(BiometricPrompt.KEY_TITLE);
468 }
469
470 @Nullable
471 private static CharSequence getSubtitle(@NonNull Bundle bundle) {
472 final CharSequence credentialSubtitle =
473 bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_SUBTITLE);
474 return credentialSubtitle != null ? credentialSubtitle
475 : bundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
476 }
477
478 @Nullable
479 private static CharSequence getDescription(@NonNull Bundle bundle) {
480 final CharSequence credentialDescription =
481 bundle.getCharSequence(BiometricPrompt.KEY_DEVICE_CREDENTIAL_DESCRIPTION);
482 return credentialDescription != null ? credentialDescription
483 : bundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
484 }
Kevin Chynff168dc2019-09-16 16:04:38 -0700485}