• 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;
18 
19 import android.app.admin.DevicePolicyManager;
20 import android.content.Intent;
21 import android.os.Bundle;
22 import android.os.UserHandle;
23 import android.os.UserManager;
24 import android.os.storage.StorageManager;
25 import android.view.View;
26 import android.widget.TextView;
27 
28 import com.android.internal.widget.LockPatternUtils;
29 import com.android.settings.R;
30 import com.android.settings.SetupWizardUtils;
31 import com.android.settings.password.ChooseLockGeneric;
32 import com.android.settings.password.ChooseLockGeneric.ChooseLockGenericFragment;
33 import com.android.settings.password.ChooseLockSettingsHelper;
34 import com.android.settings.password.SetupChooseLockGeneric;
35 
36 import com.google.android.setupcompat.template.FooterButton;
37 import com.google.android.setupcompat.util.WizardManagerHelper;
38 import com.google.android.setupdesign.span.LinkSpan;
39 
40 /**
41  * Abstract base class for the intro onboarding activity for biometric enrollment.
42  */
43 public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
44         implements LinkSpan.OnClickListener {
45 
46     private UserManager mUserManager;
47     private boolean mHasPassword;
48     private boolean mBiometricUnlockDisabledByAdmin;
49     private TextView mErrorText;
50 
51     /**
52      * @return true if the biometric is disabled by a device administrator
53      */
isDisabledByAdmin()54     protected abstract boolean isDisabledByAdmin();
55 
56     /**
57      * @return the layout resource
58      */
getLayoutResource()59     protected abstract int getLayoutResource();
60 
61     /**
62      * @return the header resource for if the biometric has been disabled by a device administrator
63      */
getHeaderResDisabledByAdmin()64     protected abstract int getHeaderResDisabledByAdmin();
65 
66     /**
67      * @return the default header resource
68      */
getHeaderResDefault()69     protected abstract int getHeaderResDefault();
70 
71     /**
72      * @return the description resource for if the biometric has been disabled by a device admin
73      */
getDescriptionResDisabledByAdmin()74     protected abstract int getDescriptionResDisabledByAdmin();
75 
76     /**
77      * @return the cancel button
78      */
getCancelButton()79     protected abstract FooterButton getCancelButton();
80 
81     /**
82      * @return the next button
83      */
getNextButton()84     protected abstract FooterButton getNextButton();
85 
86     /**
87      * @return the error TextView
88      */
getErrorTextView()89     protected abstract TextView getErrorTextView();
90 
91     /**
92      * @return 0 if there are no errors, otherwise returns the resource ID for the error string
93      * to be displayed.
94      */
checkMaxEnrolled()95     protected abstract int checkMaxEnrolled();
96 
97     /**
98      * @return the challenge generated by the biometric hardware
99      */
getChallenge()100     protected abstract long getChallenge();
101 
102     /**
103      * @return one of the ChooseLockSettingsHelper#EXTRA_KEY_FOR_* constants
104      */
getExtraKeyForBiometric()105     protected abstract String getExtraKeyForBiometric();
106 
107     /**
108      * @return the intent for proceeding to the next step of enrollment. For Fingerprint, this
109      * should lead to the "Find Sensor" activity. For Face, this should lead to the "Enrolling"
110      * activity.
111      */
getEnrollingIntent()112     protected abstract Intent getEnrollingIntent();
113 
114     /**
115      * @return the title to be shown on the ConfirmLock screen.
116      */
getConfirmLockTitleResId()117     protected abstract int getConfirmLockTitleResId();
118 
119     /**
120      * @param span
121      */
onClick(LinkSpan span)122     public abstract void onClick(LinkSpan span);
123 
124     @Override
onCreate(Bundle savedInstanceState)125     protected void onCreate(Bundle savedInstanceState) {
126         super.onCreate(savedInstanceState);
127 
128         Intent intent = getIntent();
129         if (intent.getStringExtra(WizardManagerHelper.EXTRA_THEME) == null) {
130             // Put the theme in the intent so it gets propagated to other activities in the flow
131             intent.putExtra(
132                     WizardManagerHelper.EXTRA_THEME,
133                     SetupWizardUtils.getThemeString(intent));
134         }
135 
136         mBiometricUnlockDisabledByAdmin = isDisabledByAdmin();
137 
138         setContentView(getLayoutResource());
139         if (mBiometricUnlockDisabledByAdmin) {
140             setHeaderText(getHeaderResDisabledByAdmin());
141         } else {
142             setHeaderText(getHeaderResDefault());
143         }
144 
145         mErrorText = getErrorTextView();
146 
147         mUserManager = UserManager.get(this);
148         updatePasswordQuality();
149 
150         if (!mHasPassword) {
151             // No password registered, launch into enrollment wizard.
152             launchChooseLock();
153         } else if (mToken == null) {
154             // It's possible to have a token but mLaunchedConfirmLock == false, since
155             // ChooseLockGeneric can pass us a token.
156             launchConfirmLock(getConfirmLockTitleResId(), getChallenge());
157         }
158     }
159 
160     @Override
onResume()161     protected void onResume() {
162         super.onResume();
163 
164         final int errorMsg = checkMaxEnrolled();
165         if (errorMsg == 0) {
166             mErrorText.setText(null);
167             mErrorText.setVisibility(View.GONE);
168             getNextButton().setVisibility(View.VISIBLE);
169         } else {
170             mErrorText.setText(errorMsg);
171             mErrorText.setVisibility(View.VISIBLE);
172             getNextButton().setText(getResources().getString(R.string.done));
173             getNextButton().setVisibility(View.VISIBLE);
174         }
175     }
176 
updatePasswordQuality()177     private void updatePasswordQuality() {
178         final int passwordQuality = new ChooseLockSettingsHelper(this).utils()
179                 .getActivePasswordQuality(mUserManager.getCredentialOwnerProfile(mUserId));
180         mHasPassword = passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
181     }
182 
183     @Override
onNextButtonClick(View view)184     protected void onNextButtonClick(View view) {
185         if (checkMaxEnrolled() == 0) {
186             // Lock thingy is already set up, launch directly to the next page
187             launchNextEnrollingActivity(mToken);
188         } else {
189             setResult(RESULT_FINISHED);
190             finish();
191         }
192     }
193 
launchChooseLock()194     private void launchChooseLock() {
195         Intent intent = getChooseLockIntent();
196         long challenge = getChallenge();
197         intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
198                 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
199         intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_DISABLED_PREFS, true);
200         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_HAS_CHALLENGE, true);
201         intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE, challenge);
202         intent.putExtra(getExtraKeyForBiometric(), true);
203         if (mUserId != UserHandle.USER_NULL) {
204             intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
205         }
206         startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST);
207     }
208 
launchNextEnrollingActivity(byte[] token)209     private void launchNextEnrollingActivity(byte[] token) {
210         Intent intent = getEnrollingIntent();
211         if (token != null) {
212             intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
213         }
214         if (mUserId != UserHandle.USER_NULL) {
215             intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
216         }
217         intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary);
218         startActivityForResult(intent, BIOMETRIC_FIND_SENSOR_REQUEST);
219     }
220 
getChooseLockIntent()221     protected Intent getChooseLockIntent() {
222         if (WizardManagerHelper.isAnySetupWizard(getIntent())) {
223             // Default to PIN lock in setup wizard
224             Intent intent = new Intent(this, SetupChooseLockGeneric.class);
225             if (StorageManager.isFileEncryptedNativeOrEmulated()) {
226                 intent.putExtra(
227                         LockPatternUtils.PASSWORD_TYPE_KEY,
228                         DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
229                 intent.putExtra(ChooseLockGenericFragment.EXTRA_SHOW_OPTIONS_BUTTON, true);
230             }
231             WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
232             return intent;
233         } else {
234             return new Intent(this, ChooseLockGeneric.class);
235         }
236     }
237 
238     @Override
onActivityResult(int requestCode, int resultCode, Intent data)239     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
240         if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST) {
241             if (resultCode == RESULT_FINISHED || resultCode == RESULT_SKIP) {
242                 setResult(resultCode, data);
243                 finish();
244                 return;
245             }
246         } else if (requestCode == CHOOSE_LOCK_GENERIC_REQUEST) {
247             if (resultCode == RESULT_FINISHED) {
248                 updatePasswordQuality();
249                 mToken = data.getByteArrayExtra(
250                         ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
251                 overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
252                 return;
253             } else {
254                 setResult(resultCode, data);
255                 finish();
256             }
257         } else if (requestCode == CONFIRM_REQUEST) {
258             if (resultCode == RESULT_OK && data != null) {
259                 mToken = data.getByteArrayExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN);
260                 overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
261             } else {
262                 setResult(resultCode, data);
263                 finish();
264             }
265         } else if (requestCode == LEARN_MORE_REQUEST) {
266             overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out);
267         }
268         super.onActivityResult(requestCode, resultCode, data);
269     }
270 
onCancelButtonClick(View view)271     protected void onCancelButtonClick(View view) {
272         finish();
273     }
274 
onSkipButtonClick(View view)275     protected void onSkipButtonClick(View view) {
276         setResult(RESULT_SKIP);
277         finish();
278     }
279 
280     @Override
initViews()281     protected void initViews() {
282         super.initViews();
283 
284         TextView description = (TextView) findViewById(R.id.sud_layout_description);
285         if (mBiometricUnlockDisabledByAdmin) {
286             description.setText(getDescriptionResDisabledByAdmin());
287         }
288     }
289 }
290