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