2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.settings;
19 import android.annotation.Nullable;
20 import android.app.ActivityManager;
21 import android.app.ActivityManagerNative;
22 import android.app.ActivityOptions;
23 import android.app.AlertDialog;
24 import android.app.IActivityManager;
25 import android.app.admin.DevicePolicyManager;
26 import android.app.trust.TrustManager;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.DialogInterface.OnClickListener;
30 import android.content.Intent;
31 import android.content.IntentSender;
32 import android.graphics.Point;
33 import android.graphics.PorterDuff;
34 import android.graphics.drawable.ColorDrawable;
35 import android.graphics.drawable.Drawable;
36 import android.os.Bundle;
37 import android.os.Handler;
38 import android.os.RemoteException;
39 import android.os.UserManager;
40 import android.security.KeyStore;
41 import android.view.View;
42 import android.view.ViewGroup;
43 import android.widget.Button;
44 import android.widget.FrameLayout;
45 import android.widget.ImageView;
46 import android.widget.TextView;
48 import com.android.internal.widget.LockPatternUtils;
49 import com.android.settings.fingerprint.FingerprintUiHelper;
52 * Base fragment to be shared for PIN/Pattern/Password confirmation fragments.
54 public abstract class ConfirmDeviceCredentialBaseFragment extends OptionsMenuFragment
55 implements FingerprintUiHelper.Callback {
57 public static final String PACKAGE = "com.android.settings";
58 public static final String TITLE_TEXT = PACKAGE + ".ConfirmCredentials.title";
59 public static final String HEADER_TEXT = PACKAGE + ".ConfirmCredentials.header";
60 public static final String DETAILS_TEXT = PACKAGE + ".ConfirmCredentials.details";
61 public static final String ALLOW_FP_AUTHENTICATION =
62 PACKAGE + ".ConfirmCredentials.allowFpAuthentication";
63 public static final String DARK_THEME = PACKAGE + ".ConfirmCredentials.darkTheme";
64 public static final String SHOW_CANCEL_BUTTON =
65 PACKAGE + ".ConfirmCredentials.showCancelButton";
66 public static final String SHOW_WHEN_LOCKED =
67 PACKAGE + ".ConfirmCredentials.showWhenLocked";
69 private FingerprintUiHelper mFingerprintHelper;
70 protected boolean mIsStrongAuthRequired;
71 private boolean mAllowFpAuthentication;
72 protected boolean mReturnCredentials = false;
73 protected Button mCancelButton;
74 protected ImageView mFingerprintIcon;
75 protected int mEffectiveUserId;
76 protected int mUserId;
77 protected LockPatternUtils mLockPatternUtils;
78 protected TextView mErrorTextView;
79 protected final Handler mHandler = new Handler();
82 public void onCreate(@Nullable Bundle savedInstanceState) {
83 super.onCreate(savedInstanceState);
84 mAllowFpAuthentication = getActivity().getIntent().getBooleanExtra(
85 ALLOW_FP_AUTHENTICATION, false);
86 mReturnCredentials = getActivity().getIntent().getBooleanExtra(
87 ChooseLockSettingsHelper.EXTRA_KEY_RETURN_CREDENTIALS, false);
88 // Only take this argument into account if it belongs to the current profile.
89 Intent intent = getActivity().getIntent();
90 mUserId = Utils.getUserIdFromBundle(getActivity(), intent.getExtras());
91 final UserManager userManager = UserManager.get(getActivity());
92 mEffectiveUserId = userManager.getCredentialOwnerProfile(mUserId);
93 mLockPatternUtils = new LockPatternUtils(getActivity());
94 mIsStrongAuthRequired = isFingerprintDisallowedByStrongAuth();
95 mAllowFpAuthentication = mAllowFpAuthentication && !isFingerprintDisabledByAdmin()
96 && !mReturnCredentials && !mIsStrongAuthRequired;
100 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
101 super.onViewCreated(view, savedInstanceState);
102 mCancelButton = (Button) view.findViewById(R.id.cancelButton);
103 mFingerprintIcon = (ImageView) view.findViewById(R.id.fingerprintIcon);
104 mFingerprintHelper = new FingerprintUiHelper(
106 (TextView) view.findViewById(R.id.errorText), this, mEffectiveUserId);
107 boolean showCancelButton = getActivity().getIntent().getBooleanExtra(
108 SHOW_CANCEL_BUTTON, false);
109 mCancelButton.setVisibility(showCancelButton ? View.VISIBLE : View.GONE);
110 mCancelButton.setOnClickListener(new View.OnClickListener() {
112 public void onClick(View v) {
113 getActivity().finish();
116 int credentialOwnerUserId = Utils.getCredentialOwnerUserId(
118 Utils.getUserIdFromBundle(
120 getActivity().getIntent().getExtras()));
121 if (Utils.isManagedProfile(UserManager.get(getActivity()), credentialOwnerUserId)) {
122 setWorkChallengeBackground(view, credentialOwnerUserId);
126 private boolean isFingerprintDisabledByAdmin() {
127 DevicePolicyManager dpm = (DevicePolicyManager) getActivity().getSystemService(
128 Context.DEVICE_POLICY_SERVICE);
129 final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, mEffectiveUserId);
130 return (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) != 0;
133 // User could be locked while Effective user is unlocked even though the effective owns the
134 // credential. Otherwise, fingerprint can't unlock fbe/keystore through
135 // verifyTiedProfileChallenge. In such case, we also wanna show the user message that
136 // fingerprint is disabled due to device restart.
137 private boolean isFingerprintDisallowedByStrongAuth() {
138 return !(mLockPatternUtils.isFingerprintAllowedForUser(mEffectiveUserId)
139 && KeyStore.getInstance().state(mUserId) == KeyStore.State.UNLOCKED);
143 public void onResume() {
145 if (mAllowFpAuthentication) {
146 mFingerprintHelper.startListening();
148 if (isProfileChallenge()) {
149 updateErrorMessage(mLockPatternUtils.getCurrentFailedPasswordAttempts(
154 protected void setAccessibilityTitle(CharSequence supplementalText) {
155 Intent intent = getActivity().getIntent();
156 if (intent != null) {
157 CharSequence titleText = intent.getCharSequenceExtra(
158 ConfirmDeviceCredentialBaseFragment.TITLE_TEXT);
159 if (titleText == null || supplementalText == null) {
162 String accessibilityTitle =
163 new StringBuilder(titleText).append(",").append(supplementalText).toString();
164 getActivity().setTitle(Utils.createAccessibleSequence(titleText, accessibilityTitle));
169 public void onPause() {
171 if (mAllowFpAuthentication) {
172 mFingerprintHelper.stopListening();
177 public void onAuthenticated() {
178 // Check whether we are still active.
179 if (getActivity() != null && getActivity().isResumed()) {
180 TrustManager trustManager =
181 (TrustManager) getActivity().getSystemService(Context.TRUST_SERVICE);
182 trustManager.setDeviceLockedForUser(mEffectiveUserId, false);
183 authenticationSucceeded();
184 authenticationSucceeded();
185 checkForPendingIntent();
189 protected abstract void authenticationSucceeded();
192 public void onFingerprintIconVisibilityChanged(boolean visible) {
195 public void prepareEnterAnimation() {
198 public void startEnterAnimation() {
201 protected void checkForPendingIntent() {
202 int taskId = getActivity().getIntent().getIntExtra(Intent.EXTRA_TASK_ID, -1);
205 IActivityManager activityManager = ActivityManagerNative.getDefault();
206 final ActivityOptions options = ActivityOptions.makeBasic();
207 options.setLaunchStackId(ActivityManager.StackId.INVALID_STACK_ID);
208 activityManager.startActivityFromRecents(taskId, options.toBundle());
210 } catch (RemoteException e) {
214 IntentSender intentSender = getActivity().getIntent()
215 .getParcelableExtra(Intent.EXTRA_INTENT);
216 if (intentSender != null) {
218 getActivity().startIntentSenderForResult(intentSender, -1, null, 0, 0, 0);
219 } catch (IntentSender.SendIntentException e) {
225 private void setWorkChallengeBackground(View baseView, int userId) {
226 View mainContent = getActivity().findViewById(com.android.settings.R.id.main_content);
227 if (mainContent != null) {
228 // Remove the main content padding so that the background image is full screen.
229 mainContent.setPadding(0, 0, 0, 0);
232 DevicePolicyManager dpm = (DevicePolicyManager) getActivity().getSystemService(
233 Context.DEVICE_POLICY_SERVICE);
234 baseView.setBackground(new ColorDrawable(dpm.getOrganizationColorForUser(userId)));
235 ImageView imageView = (ImageView) baseView.findViewById(R.id.background_image);
236 if (imageView != null) {
237 Drawable image = getResources().getDrawable(R.drawable.work_challenge_background);
238 image.setColorFilter(
239 getResources().getColor(R.color.confirm_device_credential_transparent_black),
240 PorterDuff.Mode.DARKEN);
241 imageView.setImageDrawable(image);
242 Point screenSize = new Point();
243 getActivity().getWindowManager().getDefaultDisplay().getSize(screenSize);
244 imageView.setLayoutParams(new FrameLayout.LayoutParams(
245 ViewGroup.LayoutParams.MATCH_PARENT,
250 protected boolean isProfileChallenge() {
251 return Utils.isManagedProfile(UserManager.get(getContext()), mEffectiveUserId);
254 protected void reportSuccessfullAttempt() {
255 if (isProfileChallenge()) {
256 mLockPatternUtils.reportSuccessfulPasswordAttempt(mEffectiveUserId);
257 // Keyguard is responsible to disable StrongAuth for primary user. Disable StrongAuth
258 // for work challenge only here.
259 mLockPatternUtils.userPresent(mEffectiveUserId);
263 protected void reportFailedAttempt() {
264 if (isProfileChallenge()) {
265 // + 1 for this attempt.
267 mLockPatternUtils.getCurrentFailedPasswordAttempts(mEffectiveUserId) + 1);
268 mLockPatternUtils.reportFailedPasswordAttempt(mEffectiveUserId);
272 protected void updateErrorMessage(int numAttempts) {
273 final int maxAttempts =
274 mLockPatternUtils.getMaximumFailedPasswordsForWipe(mEffectiveUserId);
275 if (maxAttempts > 0 && numAttempts > 0) {
276 int remainingAttempts = maxAttempts - numAttempts;
277 if (remainingAttempts == 1) {
279 final String title = getActivity().getString(
280 R.string.lock_profile_wipe_warning_title);
281 final String message = getActivity().getString(getLastTryErrorMessage());
282 showDialog(title, message, android.R.string.ok, false /* dismiss */);
283 } else if (remainingAttempts <= 0) {
285 final String message = getActivity().getString(R.string.lock_profile_wipe_content);
286 showDialog(null, message, R.string.lock_profile_wipe_dismiss, true /* dismiss */);
288 if (mErrorTextView != null) {
289 final String message = getActivity().getString(R.string.lock_profile_wipe_attempts,
290 numAttempts, maxAttempts);
291 showError(message, 0);
296 protected abstract int getLastTryErrorMessage();
298 private final Runnable mResetErrorRunnable = new Runnable() {
301 mErrorTextView.setText("");
305 protected void showError(CharSequence msg, long timeout) {
306 mErrorTextView.setText(msg);
308 mHandler.removeCallbacks(mResetErrorRunnable);
310 mHandler.postDelayed(mResetErrorRunnable, timeout);
314 protected abstract void onShowError();
316 protected void showError(int msg, long timeout) {
317 showError(getText(msg), timeout);
320 private void showDialog(String title, String message, int buttonString, final boolean dismiss) {
321 final AlertDialog dialog = new AlertDialog.Builder(getActivity())
324 .setPositiveButton(buttonString, new OnClickListener() {
326 public void onClick(DialogInterface dialog, int which) {
328 getActivity().finish();