1 /* 2 * Copyright (C) 2021 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.settings.biometrics.face; 18 19 import static android.app.admin.DevicePolicyResources.Strings.Settings.FACE_UNLOCK_DISABLED; 20 21 import static com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException; 22 23 import android.app.admin.DevicePolicyManager; 24 import android.app.settings.SettingsEnums; 25 import android.content.Intent; 26 import android.content.res.Configuration; 27 import android.hardware.SensorPrivacyManager; 28 import android.hardware.biometrics.BiometricAuthenticator; 29 import android.hardware.biometrics.SensorProperties; 30 import android.hardware.face.FaceManager; 31 import android.hardware.face.FaceSensorPropertiesInternal; 32 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; 33 import android.os.Bundle; 34 import android.os.UserHandle; 35 import android.text.Html; 36 import android.text.method.LinkMovementMethod; 37 import android.util.Log; 38 import android.view.View; 39 import android.widget.ImageView; 40 import android.widget.LinearLayout; 41 import android.widget.ScrollView; 42 import android.widget.TextView; 43 44 import androidx.annotation.NonNull; 45 import androidx.annotation.Nullable; 46 import androidx.annotation.StringRes; 47 import androidx.annotation.VisibleForTesting; 48 49 import com.android.settings.R; 50 import com.android.settings.Settings; 51 import com.android.settings.Utils; 52 import com.android.settings.biometrics.BiometricEnrollActivity; 53 import com.android.settings.biometrics.BiometricEnrollIntroduction; 54 import com.android.settings.biometrics.BiometricUtils; 55 import com.android.settings.biometrics.MultiBiometricEnrollHelper; 56 import com.android.settings.password.ChooseLockSettingsHelper; 57 import com.android.settings.password.SetupSkipDialog; 58 import com.android.settings.utils.SensorPrivacyManagerHelper; 59 import com.android.settingslib.RestrictedLockUtilsInternal; 60 import com.android.systemui.unfold.compat.ScreenSizeFoldProvider; 61 import com.android.systemui.unfold.updates.FoldProvider; 62 63 import com.google.android.setupcompat.template.FooterButton; 64 import com.google.android.setupcompat.util.WizardManagerHelper; 65 import com.google.android.setupdesign.span.LinkSpan; 66 67 import java.util.List; 68 69 /** 70 * Provides introductory info about face unlock and prompts the user to agree before starting face 71 * enrollment. 72 */ 73 public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { 74 private static final String TAG = "FaceEnrollIntroduction"; 75 76 private FaceManager mFaceManager; 77 @Nullable private FooterButton mPrimaryFooterButton; 78 @Nullable private FooterButton mSecondaryFooterButton; 79 @Nullable private SensorPrivacyManager mSensorPrivacyManager; 80 private boolean mIsFaceStrong; 81 82 @Override onCancelButtonClick(View view)83 protected void onCancelButtonClick(View view) { 84 if (!BiometricUtils.tryStartingNextBiometricEnroll(this, ENROLL_NEXT_BIOMETRIC_REQUEST, 85 "cancel")) { 86 super.onCancelButtonClick(view); 87 } 88 } 89 90 @Override onSkipButtonClick(View view)91 protected void onSkipButtonClick(View view) { 92 if (!BiometricUtils.tryStartingNextBiometricEnroll(this, ENROLL_NEXT_BIOMETRIC_REQUEST, 93 "skip")) { 94 super.onSkipButtonClick(view); 95 } 96 } 97 98 @Override onEnrollmentSkipped(@ullable Intent data)99 protected void onEnrollmentSkipped(@Nullable Intent data) { 100 if (!BiometricUtils.tryStartingNextBiometricEnroll(this, ENROLL_NEXT_BIOMETRIC_REQUEST, 101 "skipped")) { 102 super.onEnrollmentSkipped(data); 103 } 104 } 105 106 @Override onFinishedEnrolling(@ullable Intent data)107 protected void onFinishedEnrolling(@Nullable Intent data) { 108 if (!BiometricUtils.tryStartingNextBiometricEnroll(this, ENROLL_NEXT_BIOMETRIC_REQUEST, 109 "finished")) { 110 super.onFinishedEnrolling(data); 111 } 112 } 113 114 @Override shouldFinishWhenBackgrounded()115 protected boolean shouldFinishWhenBackgrounded() { 116 return super.shouldFinishWhenBackgrounded() && !BiometricUtils.isPostureGuidanceShowing( 117 mDevicePostureState, mLaunchedPostureGuidance); 118 } 119 120 @Override onCreate(Bundle savedInstanceState)121 protected void onCreate(Bundle savedInstanceState) { 122 mFaceManager = getFaceManager(); 123 124 super.onCreate(savedInstanceState); 125 126 if (savedInstanceState == null 127 && !WizardManagerHelper.isAnySetupWizard(getIntent()) 128 && !getIntent().getBooleanExtra(EXTRA_FROM_SETTINGS_SUMMARY, false) 129 && maxFacesEnrolled()) { 130 // from tips && maxEnrolled 131 Log.d(TAG, "launch face settings"); 132 launchFaceSettingsActivity(); 133 finish(); 134 } 135 136 // Wait super::onCreated() then return because SuperNotCalledExceptio will be thrown 137 // if we don't wait for it. 138 if (isFinishing()) { 139 return; 140 } 141 142 // Apply extracted theme color to icons. 143 final ImageView iconGlasses = findViewById(R.id.icon_glasses); 144 final ImageView iconLooking = findViewById(R.id.icon_looking); 145 iconGlasses.getBackground().setColorFilter(getIconColorFilter()); 146 iconLooking.getBackground().setColorFilter(getIconColorFilter()); 147 148 // Set text for views with multiple variations. 149 final TextView infoMessageGlasses = findViewById(R.id.info_message_glasses); 150 final TextView infoMessageLooking = findViewById(R.id.info_message_looking); 151 final TextView howMessage = findViewById(R.id.how_message); 152 final TextView inControlTitle = findViewById(R.id.title_in_control); 153 final TextView inControlMessage = findViewById(R.id.message_in_control); 154 final TextView lessSecure = findViewById(R.id.info_message_less_secure); 155 infoMessageGlasses.setText(getInfoMessageGlasses()); 156 infoMessageLooking.setText(getInfoMessageLooking()); 157 inControlTitle.setText(getInControlTitle()); 158 howMessage.setText(getHowMessage()); 159 inControlMessage.setText(Html.fromHtml(getString(getInControlMessage()), 160 Html.FROM_HTML_MODE_LEGACY)); 161 inControlMessage.setMovementMethod(LinkMovementMethod.getInstance()); 162 lessSecure.setText(getLessSecureMessage()); 163 164 final ScrollView scrollView = 165 findViewById(com.google.android.setupdesign.R.id.sud_scroll_view); 166 scrollView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); 167 168 // Set up and show the "require eyes" info section if necessary. 169 if (getResources().getBoolean(R.bool.config_face_intro_show_require_eyes)) { 170 final LinearLayout infoRowRequireEyes = findViewById(R.id.info_row_require_eyes); 171 final ImageView iconRequireEyes = findViewById(R.id.icon_require_eyes); 172 final TextView infoMessageRequireEyes = findViewById(R.id.info_message_require_eyes); 173 infoRowRequireEyes.setVisibility(View.VISIBLE); 174 iconRequireEyes.getBackground().setColorFilter(getIconColorFilter()); 175 infoMessageRequireEyes.setText(getInfoMessageRequireEyes()); 176 } 177 178 179 if (mFaceManager != null) { 180 mFaceManager.addAuthenticatorsRegisteredCallback( 181 new IFaceAuthenticatorsRegisteredCallback.Stub() { 182 @Override 183 public void onAllAuthenticatorsRegistered( 184 @NonNull List<FaceSensorPropertiesInternal> sensors) { 185 if (sensors.isEmpty()) { 186 Log.e(TAG, "No sensors"); 187 return; 188 } 189 190 boolean isFaceStrong = sensors.get(0).sensorStrength 191 == SensorProperties.STRENGTH_STRONG; 192 mIsFaceStrong = isFaceStrong; 193 onFaceStrengthChanged(); 194 } 195 }); 196 } 197 198 // This path is an entry point for SetNewPasswordController, e.g. 199 // adb shell am start -a android.app.action.SET_NEW_PASSWORD 200 if (mToken == null && BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) { 201 if (generateChallengeOnCreate()) { 202 mFooterBarMixin.getPrimaryButton().setEnabled(false); 203 // We either block on generateChallenge, or need to gray out the "next" button until 204 // the challenge is ready. Let's just do this for now. 205 mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> { 206 if (isFinishing()) { 207 // Do nothing if activity is finishing 208 Log.w(TAG, "activity finished before challenge callback launched."); 209 return; 210 } 211 212 try { 213 mToken = requestGatekeeperHat(challenge); 214 mSensorId = sensorId; 215 mChallenge = challenge; 216 mFooterBarMixin.getPrimaryButton().setEnabled(true); 217 } catch (GatekeeperCredentialNotMatchException e) { 218 // Let BiometricEnrollBase#onCreate() to trigger confirmLock() 219 getIntent().removeExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE); 220 recreate(); 221 } 222 }); 223 } 224 } 225 226 mSensorPrivacyManager = getApplicationContext() 227 .getSystemService(SensorPrivacyManager.class); 228 final SensorPrivacyManagerHelper helper = SensorPrivacyManagerHelper 229 .getInstance(getApplicationContext()); 230 final boolean cameraPrivacyEnabled = helper 231 .isSensorBlocked(SensorPrivacyManagerHelper.SENSOR_CAMERA); 232 Log.v(TAG, "cameraPrivacyEnabled : " + cameraPrivacyEnabled); 233 } 234 launchFaceSettingsActivity()235 private void launchFaceSettingsActivity() { 236 final Intent intent = new Intent(this, Settings.FaceSettingsInternalActivity.class); 237 final byte[] token = getIntent().getByteArrayExtra( 238 ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN); 239 if (token != null) { 240 intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token); 241 } 242 final int userId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId()); 243 if (userId != UserHandle.USER_NULL) { 244 intent.putExtra(Intent.EXTRA_USER_ID, userId); 245 } 246 BiometricUtils.copyMultiBiometricExtras(getIntent(), intent); 247 intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true); 248 intent.putExtra(EXTRA_KEY_CHALLENGE, getIntent().getLongExtra(EXTRA_KEY_CHALLENGE, -1L)); 249 intent.putExtra(EXTRA_KEY_SENSOR_ID, getIntent().getIntExtra(EXTRA_KEY_SENSOR_ID, -1)); 250 startActivity(intent); 251 } 252 253 @VisibleForTesting 254 @Nullable getFaceManager()255 protected FaceManager getFaceManager() { 256 return Utils.getFaceManagerOrNull(this); 257 } 258 259 @VisibleForTesting 260 @Nullable getPostureGuidanceIntent()261 protected Intent getPostureGuidanceIntent() { 262 return mPostureGuidanceIntent; 263 } 264 265 @VisibleForTesting 266 @Nullable getPostureCallback()267 protected FoldProvider.FoldCallback getPostureCallback() { 268 return mFoldCallback; 269 } 270 271 @VisibleForTesting 272 @BiometricUtils.DevicePostureInt getDevicePostureState()273 protected int getDevicePostureState() { 274 return mDevicePostureState; 275 } 276 277 @VisibleForTesting 278 @Nullable requestGatekeeperHat(long challenge)279 protected byte[] requestGatekeeperHat(long challenge) { 280 return BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, challenge); 281 } 282 283 @Override onConfigurationChanged(@onNull Configuration newConfig)284 public void onConfigurationChanged(@NonNull Configuration newConfig) { 285 super.onConfigurationChanged(newConfig); 286 if (mScreenSizeFoldProvider != null && getPostureCallback() != null) { 287 mScreenSizeFoldProvider.onConfigurationChange(newConfig); 288 } 289 } 290 291 @Override onStart()292 protected void onStart() { 293 super.onStart(); 294 listenFoldEventForPostureGuidance(); 295 } 296 listenFoldEventForPostureGuidance()297 private void listenFoldEventForPostureGuidance() { 298 if (maxFacesEnrolled()) { 299 Log.d(TAG, "Device has enrolled face, do not show posture guidance"); 300 return; 301 } 302 303 if (getPostureGuidanceIntent() == null) { 304 Log.d(TAG, "Device do not support posture guidance"); 305 return; 306 } 307 308 BiometricUtils.setDevicePosturesAllowEnroll( 309 getResources().getInteger(R.integer.config_face_enroll_supported_posture)); 310 311 if (getPostureCallback() == null) { 312 mFoldCallback = isFolded -> { 313 mDevicePostureState = isFolded ? BiometricUtils.DEVICE_POSTURE_CLOSED 314 : BiometricUtils.DEVICE_POSTURE_OPENED; 315 if (BiometricUtils.shouldShowPostureGuidance(mDevicePostureState, 316 mLaunchedPostureGuidance) && !mNextLaunched) { 317 launchPostureGuidance(); 318 } 319 }; 320 } 321 322 if (mScreenSizeFoldProvider == null) { 323 mScreenSizeFoldProvider = new ScreenSizeFoldProvider(getApplicationContext()); 324 mScreenSizeFoldProvider.registerCallback(mFoldCallback, getMainExecutor()); 325 } 326 } 327 328 @Override onActivityResult(int requestCode, int resultCode, Intent data)329 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 330 if (requestCode == REQUEST_POSTURE_GUIDANCE) { 331 mLaunchedPostureGuidance = false; 332 if (resultCode == RESULT_CANCELED || resultCode == RESULT_SKIP) { 333 onSkipButtonClick(getCurrentFocus()); 334 } 335 return; 336 } 337 338 // If user has skipped or finished enrolling, don't restart enrollment. 339 final boolean isEnrollRequest = requestCode == BIOMETRIC_FIND_SENSOR_REQUEST 340 || requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST; 341 final boolean isResultSkipOrFinished = resultCode == RESULT_SKIP 342 || resultCode == SetupSkipDialog.RESULT_SKIP || resultCode == RESULT_FINISHED; 343 boolean hasEnrolledFace = false; 344 if (data != null) { 345 hasEnrolledFace = data.getBooleanExtra(EXTRA_FINISHED_ENROLL_FACE, false); 346 } 347 348 if (resultCode == RESULT_CANCELED) { 349 if (hasEnrolledFace || !BiometricUtils.isPostureAllowEnrollment(mDevicePostureState)) { 350 setResult(resultCode, data); 351 finish(); 352 return; 353 } 354 } 355 356 if (isEnrollRequest && isResultSkipOrFinished || hasEnrolledFace) { 357 data = setSkipPendingEnroll(data); 358 } 359 super.onActivityResult(requestCode, resultCode, data); 360 } 361 generateChallengeOnCreate()362 protected boolean generateChallengeOnCreate() { 363 return true; 364 } 365 366 @StringRes getInfoMessageGlasses()367 protected int getInfoMessageGlasses() { 368 return R.string.security_settings_face_enroll_introduction_info_glasses; 369 } 370 371 @StringRes getInfoMessageLooking()372 protected int getInfoMessageLooking() { 373 return isPrivateProfile() 374 ? R.string.private_space_face_enroll_introduction_info_looking 375 : R.string.security_settings_face_enroll_introduction_info_looking; 376 } 377 378 @StringRes getInfoMessageRequireEyes()379 protected int getInfoMessageRequireEyes() { 380 return R.string.security_settings_face_enroll_introduction_info_gaze; 381 } 382 383 @StringRes getHowMessage()384 protected int getHowMessage() { 385 return R.string.security_settings_face_enroll_introduction_how_message; 386 } 387 388 @StringRes getInControlTitle()389 protected int getInControlTitle() { 390 return R.string.security_settings_face_enroll_introduction_control_title; 391 } 392 393 @StringRes getInControlMessage()394 protected int getInControlMessage() { 395 return R.string.security_settings_face_enroll_introduction_control_message; 396 } 397 398 @StringRes getLessSecureMessage()399 protected int getLessSecureMessage() { 400 return isPrivateProfile() 401 ? R.string.private_space_face_enroll_introduction_info_less_secure 402 : R.string.security_settings_face_enroll_introduction_info_less_secure; 403 404 } 405 406 @Override isDisabledByAdmin()407 protected boolean isDisabledByAdmin() { 408 return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled( 409 this, DevicePolicyManager.KEYGUARD_DISABLE_FACE, mUserId) != null; 410 } 411 412 @Override getLayoutResource()413 protected int getLayoutResource() { 414 return R.layout.face_enroll_introduction; 415 } 416 417 @Override getHeaderResDisabledByAdmin()418 protected int getHeaderResDisabledByAdmin() { 419 return R.string.security_settings_face_enroll_introduction_title_unlock_disabled; 420 } 421 422 @Override getHeaderResDefault()423 protected int getHeaderResDefault() { 424 if (isPrivateProfile()) { 425 return R.string.private_space_face_enroll_introduction_title; 426 } 427 return R.string.security_settings_face_enroll_introduction_title; 428 } 429 430 @Override getDescriptionDisabledByAdmin()431 protected String getDescriptionDisabledByAdmin() { 432 DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class); 433 return devicePolicyManager.getResources().getString( 434 FACE_UNLOCK_DISABLED, 435 () -> getString(R.string.security_settings_face_enroll_introduction_message_unlock_disabled)); 436 } 437 438 @Override getCancelButton()439 protected FooterButton getCancelButton() { 440 if (mFooterBarMixin != null) { 441 return mFooterBarMixin.getSecondaryButton(); 442 } 443 return null; 444 } 445 446 @Override getNextButton()447 protected FooterButton getNextButton() { 448 if (mFooterBarMixin != null) { 449 return mFooterBarMixin.getPrimaryButton(); 450 } 451 return null; 452 } 453 454 @Override getErrorTextView()455 protected TextView getErrorTextView() { 456 return findViewById(R.id.error_text); 457 } 458 maxFacesEnrolled()459 private boolean maxFacesEnrolled() { 460 if (mFaceManager != null) { 461 // This will need to be updated for devices with multiple face sensors. 462 final int numEnrolledFaces = mFaceManager.getEnrolledFaces(mUserId).size(); 463 final int maxFacesEnrollable = getApplicationContext().getResources() 464 .getInteger(R.integer.suw_max_faces_enrollable); 465 return numEnrolledFaces >= maxFacesEnrollable; 466 } else { 467 return false; 468 } 469 } 470 471 //TODO: Refactor this to something that conveys it is used for getting a string ID. 472 @Override checkMaxEnrolled()473 protected int checkMaxEnrolled() { 474 if (mFaceManager != null) { 475 if (maxFacesEnrolled()) { 476 return R.string.face_intro_error_max; 477 } 478 } else { 479 return R.string.face_intro_error_unknown; 480 } 481 return 0; 482 } 483 484 @Override getChallenge(GenerateChallengeCallback callback)485 protected void getChallenge(GenerateChallengeCallback callback) { 486 mFaceManager = Utils.getFaceManagerOrNull(this); 487 if (mFaceManager == null) { 488 callback.onChallengeGenerated(0, 0, 0L); 489 return; 490 } 491 mFaceManager.generateChallenge(mUserId, callback::onChallengeGenerated); 492 } 493 494 @Override getExtraKeyForBiometric()495 protected String getExtraKeyForBiometric() { 496 return ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE; 497 } 498 499 @Override getEnrollingIntent()500 protected Intent getEnrollingIntent() { 501 Intent intent = new Intent(this, FaceEnrollEducation.class); 502 WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); 503 intent.putExtra(BiometricUtils.EXTRA_ENROLL_REASON, 504 getIntent().getIntExtra(BiometricUtils.EXTRA_ENROLL_REASON, -1)); 505 506 return intent; 507 } 508 509 @Override getConfirmLockTitleResId()510 protected int getConfirmLockTitleResId() { 511 return R.string.security_settings_face_preference_title; 512 } 513 514 @Override getMetricsCategory()515 public int getMetricsCategory() { 516 return SettingsEnums.FACE_ENROLL_INTRO; 517 } 518 519 @Override onClick(LinkSpan span)520 public void onClick(LinkSpan span) { 521 // TODO(b/110906762) 522 } 523 524 @Override getModality()525 public @BiometricAuthenticator.Modality int getModality() { 526 return BiometricAuthenticator.TYPE_FACE; 527 } 528 529 @Override onNextButtonClick(View view)530 protected void onNextButtonClick(View view) { 531 final boolean parentelConsentRequired = 532 getIntent() 533 .getBooleanExtra(BiometricEnrollActivity.EXTRA_REQUIRE_PARENTAL_CONSENT, false); 534 final boolean cameraPrivacyEnabled = SensorPrivacyManagerHelper 535 .getInstance(getApplicationContext()) 536 .isSensorBlocked(SensorPrivacyManagerHelper.SENSOR_CAMERA); 537 final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent()); 538 final boolean isSettingUp = isSetupWizard || (parentelConsentRequired 539 && !WizardManagerHelper.isUserSetupComplete(this)); 540 if (cameraPrivacyEnabled && !isSettingUp) { 541 if (mSensorPrivacyManager == null) { 542 mSensorPrivacyManager = getApplicationContext() 543 .getSystemService(SensorPrivacyManager.class); 544 } 545 mSensorPrivacyManager.showSensorUseDialog(SensorPrivacyManager.Sensors.CAMERA); 546 } else { 547 super.onNextButtonClick(view); 548 } 549 } 550 551 @Override 552 @NonNull getPrimaryFooterButton()553 protected FooterButton getPrimaryFooterButton() { 554 if (mPrimaryFooterButton == null) { 555 mPrimaryFooterButton = new FooterButton.Builder(this) 556 .setText(R.string.security_settings_face_enroll_introduction_agree) 557 .setButtonType(FooterButton.ButtonType.OPT_IN) 558 .setListener(this::onNextButtonClick) 559 .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) 560 .build(); 561 } 562 return mPrimaryFooterButton; 563 } 564 565 @Override 566 @NonNull getSecondaryFooterButton()567 protected FooterButton getSecondaryFooterButton() { 568 if (mSecondaryFooterButton == null) { 569 mSecondaryFooterButton = new FooterButton.Builder(this) 570 .setText(R.string.security_settings_face_enroll_introduction_no_thanks) 571 .setListener(this::onSkipButtonClick) 572 .setButtonType(FooterButton.ButtonType.NEXT) 573 .setTheme(com.google.android.setupdesign.R.style.SudGlifButton_Primary) 574 .build(); 575 } 576 return mSecondaryFooterButton; 577 } 578 579 @Override 580 @StringRes getAgreeButtonTextRes()581 protected int getAgreeButtonTextRes() { 582 return R.string.security_settings_face_enroll_introduction_agree; 583 } 584 585 @Override 586 @StringRes getMoreButtonTextRes()587 protected int getMoreButtonTextRes() { 588 return R.string.security_settings_face_enroll_introduction_more; 589 } 590 591 @Override updateDescriptionText()592 protected void updateDescriptionText() { 593 if (isPrivateProfile()) { 594 setDescriptionText(getString( 595 R.string.private_space_face_enroll_introduction_message)); 596 } else if (mIsFaceStrong) { 597 setDescriptionText(getString( 598 R.string.security_settings_face_enroll_introduction_message_class3)); 599 } 600 super.updateDescriptionText(); 601 } 602 603 @NonNull setSkipPendingEnroll(@ullable Intent data)604 protected static Intent setSkipPendingEnroll(@Nullable Intent data) { 605 if (data == null) { 606 data = new Intent(); 607 } 608 data.putExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, true); 609 return data; 610 } 611 isFaceStrong()612 protected boolean isFaceStrong() { 613 return mIsFaceStrong; 614 } 615 onFaceStrengthChanged()616 private void onFaceStrengthChanged() { 617 // Set up and show the "less secure" info section if necessary. 618 if (!mIsFaceStrong && getResources().getBoolean( 619 R.bool.config_face_intro_show_less_secure)) { 620 final LinearLayout infoRowLessSecure = findViewById(R.id.info_row_less_secure); 621 final ImageView iconLessSecure = findViewById(R.id.icon_less_secure); 622 infoRowLessSecure.setVisibility(View.VISIBLE); 623 iconLessSecure.getBackground().setColorFilter(getIconColorFilter()); 624 } 625 updateDescriptionText(); 626 } 627 isPrivateProfile()628 private boolean isPrivateProfile() { 629 return Utils.isPrivateProfile(mUserId, getApplicationContext()); 630 } 631 } 632