• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.settings.biometrics.fingerprint;
18 
19 import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED;
20 
21 import android.app.admin.DevicePolicyManager;
22 import android.app.settings.SettingsEnums;
23 import android.content.ActivityNotFoundException;
24 import android.content.Intent;
25 import android.hardware.biometrics.BiometricAuthenticator;
26 import android.hardware.fingerprint.FingerprintManager;
27 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
28 import android.os.Bundle;
29 import android.text.Html;
30 import android.text.method.LinkMovementMethod;
31 import android.util.Log;
32 import android.view.View;
33 import android.widget.ImageView;
34 import android.widget.ScrollView;
35 import android.widget.TextView;
36 
37 import androidx.annotation.NonNull;
38 import androidx.annotation.Nullable;
39 import androidx.annotation.StringRes;
40 
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.settings.R;
43 import com.android.settings.Utils;
44 import com.android.settings.biometrics.BiometricEnrollIntroduction;
45 import com.android.settings.biometrics.BiometricUtils;
46 import com.android.settings.biometrics.MultiBiometricEnrollHelper;
47 import com.android.settings.password.ChooseLockSettingsHelper;
48 import com.android.settingslib.HelpUtils;
49 import com.android.settingslib.RestrictedLockUtilsInternal;
50 
51 import com.google.android.setupcompat.template.FooterButton;
52 import com.google.android.setupcompat.util.WizardManagerHelper;
53 import com.google.android.setupdesign.span.LinkSpan;
54 
55 import java.util.List;
56 
57 public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
58 
59     private static final String TAG = "FingerprintIntro";
60 
61     @VisibleForTesting
62     private FingerprintManager mFingerprintManager;
63     @Nullable private FooterButton mPrimaryFooterButton;
64     @Nullable private FooterButton mSecondaryFooterButton;
65 
66     private DevicePolicyManager mDevicePolicyManager;
67     private boolean mCanAssumeUdfps;
68 
69     @Override
onCreate(Bundle savedInstanceState)70     protected void onCreate(Bundle savedInstanceState) {
71         mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
72         if (mFingerprintManager == null) {
73             Log.e(TAG, "Null FingerprintManager");
74             finish();
75             return;
76         }
77 
78         super.onCreate(savedInstanceState);
79         final FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class);
80         final List<FingerprintSensorPropertiesInternal> props =
81                 fingerprintManager.getSensorPropertiesInternal();
82         mCanAssumeUdfps = props != null && props.size() == 1 && props.get(0).isAnyUdfpsType();
83 
84         mDevicePolicyManager = getSystemService(DevicePolicyManager.class);
85 
86         final ImageView iconFingerprint = findViewById(R.id.icon_fingerprint);
87         final ImageView iconDeviceLocked = findViewById(R.id.icon_device_locked);
88         final ImageView iconTrashCan = findViewById(R.id.icon_trash_can);
89         final ImageView iconInfo = findViewById(R.id.icon_info);
90         final ImageView iconShield = findViewById(R.id.icon_shield);
91         final ImageView iconLink = findViewById(R.id.icon_link);
92         iconFingerprint.getDrawable().setColorFilter(getIconColorFilter());
93         iconDeviceLocked.getDrawable().setColorFilter(getIconColorFilter());
94         iconTrashCan.getDrawable().setColorFilter(getIconColorFilter());
95         iconInfo.getDrawable().setColorFilter(getIconColorFilter());
96         iconShield.getDrawable().setColorFilter(getIconColorFilter());
97         iconLink.getDrawable().setColorFilter(getIconColorFilter());
98 
99         final TextView footerMessage2 = findViewById(R.id.footer_message_2);
100         final TextView footerMessage3 = findViewById(R.id.footer_message_3);
101         final TextView footerMessage4 = findViewById(R.id.footer_message_4);
102         final TextView footerMessage5 = findViewById(R.id.footer_message_5);
103         final TextView footerMessage6 = findViewById(R.id.footer_message_6);
104         footerMessage2.setText(getFooterMessage2());
105         footerMessage3.setText(getFooterMessage3());
106         footerMessage4.setText(getFooterMessage4());
107         footerMessage5.setText(getFooterMessage5());
108         footerMessage6.setText(getFooterMessage6());
109 
110         final TextView footerLink = findViewById(R.id.footer_learn_more);
111         footerLink.setMovementMethod(LinkMovementMethod.getInstance());
112         footerLink.setText(Html.fromHtml(getString(getFooterLearnMore()),
113                 Html.FROM_HTML_MODE_LEGACY));
114 
115         if (mCanAssumeUdfps) {
116             footerMessage6.setVisibility(View.VISIBLE);
117             iconShield.setVisibility(View.VISIBLE);
118         } else {
119             footerMessage6.setVisibility(View.GONE);
120             iconShield.setVisibility(View.GONE);
121         }
122 
123         final TextView footerTitle1 = findViewById(R.id.footer_title_1);
124         final TextView footerTitle2 = findViewById(R.id.footer_title_2);
125         footerTitle1.setText(getFooterTitle1());
126         footerTitle2.setText(getFooterTitle2());
127 
128         final ScrollView scrollView = findViewById(R.id.sud_scroll_view);
129         scrollView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
130     }
131 
132     @Override
onActivityResult(int requestCode, int resultCode, Intent data)133     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
134         super.onActivityResult(requestCode, resultCode, data);
135     }
136 
137     @Override
onCancelButtonClick(View view)138     protected void onCancelButtonClick(View view) {
139         if (!BiometricUtils.tryStartingNextBiometricEnroll(
140                 this, ENROLL_NEXT_BIOMETRIC_REQUEST, "cancel")) {
141             super.onCancelButtonClick(view);
142         }
143     }
144 
145     @Override
onSkipButtonClick(View view)146     protected void onSkipButtonClick(View view) {
147         if (!BiometricUtils.tryStartingNextBiometricEnroll(
148                 this, ENROLL_NEXT_BIOMETRIC_REQUEST, "skipped")) {
149             super.onSkipButtonClick(view);
150         }
151     }
152 
153     @Override
onFinishedEnrolling(@ullable Intent data)154     protected void onFinishedEnrolling(@Nullable Intent data) {
155         if (!BiometricUtils.tryStartingNextBiometricEnroll(
156                 this, ENROLL_NEXT_BIOMETRIC_REQUEST, "finished")) {
157             super.onFinishedEnrolling(data);
158         }
159     }
160 
161     @StringRes
getNegativeButtonTextId()162     int getNegativeButtonTextId() {
163         return R.string.security_settings_fingerprint_enroll_introduction_no_thanks;
164     }
165 
166     @StringRes
getFooterTitle1()167     protected int getFooterTitle1() {
168         return R.string.security_settings_fingerprint_enroll_introduction_footer_title_1;
169     }
170 
171     @StringRes
getFooterTitle2()172     protected int getFooterTitle2() {
173         return R.string.security_settings_fingerprint_enroll_introduction_footer_title_2;
174     }
175 
176     @StringRes
getFooterMessage2()177     protected int getFooterMessage2() {
178         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_2;
179     }
180 
181     @StringRes
getFooterMessage3()182     protected int getFooterMessage3() {
183         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_3;
184     }
185 
186     @StringRes
getFooterMessage4()187     protected int getFooterMessage4() {
188         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_4;
189     }
190 
191     @StringRes
getFooterMessage5()192     protected int getFooterMessage5() {
193         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_5;
194     }
195 
196     @StringRes
getFooterMessage6()197     protected int getFooterMessage6() {
198         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_6;
199     }
200 
201     @StringRes
getFooterLearnMore()202     protected int getFooterLearnMore() {
203         return R.string.security_settings_fingerprint_v2_enroll_introduction_message_learn_more;
204     }
205 
206     @Override
isDisabledByAdmin()207     protected boolean isDisabledByAdmin() {
208         return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
209                 this, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, mUserId) != null;
210     }
211 
212     @Override
getLayoutResource()213     protected int getLayoutResource() {
214         return R.layout.fingerprint_enroll_introduction;
215     }
216 
217     @Override
getHeaderResDisabledByAdmin()218     protected int getHeaderResDisabledByAdmin() {
219         return R.string.security_settings_fingerprint_enroll_introduction_title_unlock_disabled;
220     }
221 
222     @Override
getHeaderResDefault()223     protected int getHeaderResDefault() {
224         return R.string.security_settings_fingerprint_enroll_introduction_title;
225     }
226 
227     @Override
getDescriptionDisabledByAdmin()228     protected String getDescriptionDisabledByAdmin() {
229         return mDevicePolicyManager.getResources().getString(
230                 FINGERPRINT_UNLOCK_DISABLED,
231                 () -> getString(R.string.security_settings_fingerprint_enroll_introduction_message_unlock_disabled));
232     }
233 
234     @Override
getCancelButton()235     protected FooterButton getCancelButton() {
236         if (mFooterBarMixin != null) {
237             return mFooterBarMixin.getSecondaryButton();
238         }
239         return null;
240     }
241 
242     @Override
getNextButton()243     protected FooterButton getNextButton() {
244         if (mFooterBarMixin != null) {
245             return mFooterBarMixin.getPrimaryButton();
246         }
247         return null;
248     }
249 
250     @Override
getErrorTextView()251     protected TextView getErrorTextView() {
252         return findViewById(R.id.error_text);
253     }
254 
isFromSetupWizardSuggestAction(@ullable Intent intent)255     private boolean isFromSetupWizardSuggestAction(@Nullable Intent intent) {
256         return intent != null && intent.getBooleanExtra(
257                 WizardManagerHelper.EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW, false);
258     }
259 
260     @Override
checkMaxEnrolled()261     protected int checkMaxEnrolled() {
262         final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
263         final boolean isDeferredSetupWizard =
264                 WizardManagerHelper.isDeferredSetupWizard(getIntent());
265         final boolean isPortalSetupWizard =
266                 WizardManagerHelper.isPortalSetupWizard(getIntent());
267         final boolean isFromSetupWizardSuggestAction = isFromSetupWizardSuggestAction(getIntent());
268         if (mFingerprintManager != null) {
269             final List<FingerprintSensorPropertiesInternal> props =
270                     mFingerprintManager.getSensorPropertiesInternal();
271             // This will need to be updated for devices with multiple fingerprint sensors
272             final int max = props.get(0).maxEnrollmentsPerUser;
273             final int numEnrolledFingerprints =
274                     mFingerprintManager.getEnrolledFingerprints(mUserId).size();
275             final int maxFingerprintsEnrollableIfSUW =
276                     getApplicationContext()
277                             .getResources()
278                             .getInteger(R.integer.suw_max_fingerprints_enrollable);
279             if (isSetupWizard && !isDeferredSetupWizard && !isPortalSetupWizard
280                     && !isFromSetupWizardSuggestAction) {
281                 if (numEnrolledFingerprints >= maxFingerprintsEnrollableIfSUW) {
282                     return R.string.fingerprint_intro_error_max;
283                 } else {
284                     return 0;
285                 }
286             } else if (numEnrolledFingerprints >= max) {
287                 return R.string.fingerprint_intro_error_max;
288             } else {
289                 return 0;
290             }
291         } else {
292             return R.string.fingerprint_intro_error_unknown;
293         }
294     }
295 
296     @Override
getChallenge(GenerateChallengeCallback callback)297     protected void getChallenge(GenerateChallengeCallback callback) {
298         mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
299         if (mFingerprintManager == null) {
300             callback.onChallengeGenerated(0, 0, 0L);
301             return;
302         }
303         mFingerprintManager.generateChallenge(mUserId, callback::onChallengeGenerated);
304     }
305 
306     @Override
getExtraKeyForBiometric()307     protected String getExtraKeyForBiometric() {
308         return ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT;
309     }
310 
311     @Override
getEnrollingIntent()312     protected Intent getEnrollingIntent() {
313         final Intent intent = new Intent(this, FingerprintEnrollFindSensor.class);
314         BiometricUtils.copyMultiBiometricExtras(getIntent(), intent);
315         if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
316             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE,
317                     BiometricUtils.getGatekeeperPasswordHandle(getIntent()));
318         }
319         return intent;
320     }
321 
322     @Override
getConfirmLockTitleResId()323     protected int getConfirmLockTitleResId() {
324         return R.string.security_settings_fingerprint_preference_title;
325     }
326 
327     @Override
getMetricsCategory()328     public int getMetricsCategory() {
329         return SettingsEnums.FINGERPRINT_ENROLL_INTRO;
330     }
331 
332     @Override
onClick(LinkSpan span)333     public void onClick(LinkSpan span) {
334         if ("url".equals(span.getLink())) {
335             String url = getString(R.string.help_url_fingerprint);
336             Intent intent = HelpUtils.getHelpIntent(this, url, getClass().getName());
337             if (intent == null) {
338                 Log.w(TAG, "Null help intent.");
339                 return;
340             }
341             try {
342                 // This needs to be startActivityForResult even though we do not care about the
343                 // actual result because the help app needs to know about who invoked it.
344                 startActivityForResult(intent, LEARN_MORE_REQUEST);
345             } catch (ActivityNotFoundException e) {
346                 Log.w(TAG, "Activity was not found for intent, " + e);
347             }
348         }
349     }
350 
351     @Override
getModality()352     public @BiometricAuthenticator.Modality int getModality() {
353         return BiometricAuthenticator.TYPE_FINGERPRINT;
354     }
355 
356     @Override
357     @NonNull
getPrimaryFooterButton()358     protected FooterButton getPrimaryFooterButton() {
359         if (mPrimaryFooterButton == null) {
360             mPrimaryFooterButton = new FooterButton.Builder(this)
361                     .setText(R.string.security_settings_fingerprint_enroll_introduction_agree)
362                     .setListener(this::onNextButtonClick)
363                     .setButtonType(FooterButton.ButtonType.OPT_IN)
364                     .setTheme(R.style.SudGlifButton_Primary)
365                     .build();
366         }
367         return mPrimaryFooterButton;
368     }
369 
370     @Override
371     @NonNull
getSecondaryFooterButton()372     protected FooterButton getSecondaryFooterButton() {
373         if (mSecondaryFooterButton == null) {
374             mSecondaryFooterButton = new FooterButton.Builder(this)
375                     .setText(getNegativeButtonTextId())
376                     .setListener(this::onSkipButtonClick)
377                     .setButtonType(FooterButton.ButtonType.NEXT)
378                     .setTheme(R.style.SudGlifButton_Primary)
379                     .build();
380         }
381         return mSecondaryFooterButton;
382     }
383 
384     @Override
385     @StringRes
getAgreeButtonTextRes()386     protected int getAgreeButtonTextRes() {
387         return R.string.security_settings_fingerprint_enroll_introduction_agree;
388     }
389 
390     @Override
391     @StringRes
getMoreButtonTextRes()392     protected int getMoreButtonTextRes() {
393         return R.string.security_settings_face_enroll_introduction_more;
394     }
395 
396     @NonNull
setSkipPendingEnroll(@ullable Intent data)397     protected static Intent setSkipPendingEnroll(@Nullable Intent data) {
398         if (data == null) {
399             data = new Intent();
400         }
401         data.putExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, true);
402         return data;
403     }
404 }
405