[go: nahoru, domu]

1/*
2 * Copyright (C) 2014 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 */
16package com.android.keyguard;
17
18import android.app.Activity;
19import android.app.AlertDialog;
20import android.app.admin.DevicePolicyManager;
21import android.content.Context;
22import android.os.UserHandle;
23import android.util.AttributeSet;
24import android.util.Log;
25import android.util.Slog;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.WindowManager;
29import android.widget.FrameLayout;
30
31import com.android.internal.widget.LockPatternUtils;
32import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
33
34public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
35    private static final boolean DEBUG = KeyguardConstants.DEBUG;
36    private static final String TAG = "KeyguardSecurityView";
37
38    private static final int USER_TYPE_PRIMARY = 1;
39    private static final int USER_TYPE_WORK_PROFILE = 2;
40    private static final int USER_TYPE_SECONDARY_USER = 3;
41
42    private KeyguardSecurityModel mSecurityModel;
43    private LockPatternUtils mLockPatternUtils;
44
45    private KeyguardSecurityViewFlipper mSecurityViewFlipper;
46    private boolean mIsVerifyUnlockOnly;
47    private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
48    private SecurityCallback mSecurityCallback;
49
50    private final KeyguardUpdateMonitor mUpdateMonitor;
51
52    // Used to notify the container when something interesting happens.
53    public interface SecurityCallback {
54        public boolean dismiss(boolean authenticated);
55        public void userActivity();
56        public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
57
58        /**
59         * @param strongAuth wheher the user has authenticated with strong authentication like
60         *                   pattern, password or PIN but not by trust agents or fingerprint
61         */
62        public void finish(boolean strongAuth);
63        public void reset();
64    }
65
66    public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
67        this(context, attrs, 0);
68    }
69
70    public KeyguardSecurityContainer(Context context) {
71        this(context, null, 0);
72    }
73
74    public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
75        super(context, attrs, defStyle);
76        mSecurityModel = new KeyguardSecurityModel(context);
77        mLockPatternUtils = new LockPatternUtils(context);
78        mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
79    }
80
81    public void setSecurityCallback(SecurityCallback callback) {
82        mSecurityCallback = callback;
83    }
84
85    @Override
86    public void onResume(int reason) {
87        if (mCurrentSecuritySelection != SecurityMode.None) {
88            getSecurityView(mCurrentSecuritySelection).onResume(reason);
89        }
90    }
91
92    @Override
93    public void onPause() {
94        if (mCurrentSecuritySelection != SecurityMode.None) {
95            getSecurityView(mCurrentSecuritySelection).onPause();
96        }
97    }
98
99    public void startAppearAnimation() {
100        if (mCurrentSecuritySelection != SecurityMode.None) {
101            getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
102        }
103    }
104
105    public boolean startDisappearAnimation(Runnable onFinishRunnable) {
106        if (mCurrentSecuritySelection != SecurityMode.None) {
107            return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
108                    onFinishRunnable);
109        }
110        return false;
111    }
112
113    public void announceCurrentSecurityMethod() {
114        View v = (View) getSecurityView(mCurrentSecuritySelection);
115        if (v != null) {
116            v.announceForAccessibility(v.getContentDescription());
117        }
118    }
119
120    public CharSequence getCurrentSecurityModeContentDescription() {
121        View v = (View) getSecurityView(mCurrentSecuritySelection);
122        if (v != null) {
123            return v.getContentDescription();
124        }
125        return "";
126    }
127
128    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
129        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
130        KeyguardSecurityView view = null;
131        final int children = mSecurityViewFlipper.getChildCount();
132        for (int child = 0; child < children; child++) {
133            if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
134                view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
135                break;
136            }
137        }
138        int layoutId = getLayoutIdFor(securityMode);
139        if (view == null && layoutId != 0) {
140            final LayoutInflater inflater = LayoutInflater.from(mContext);
141            if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
142            View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
143            mSecurityViewFlipper.addView(v);
144            updateSecurityView(v);
145            view = (KeyguardSecurityView)v;
146        }
147
148        return view;
149    }
150
151    private void updateSecurityView(View view) {
152        if (view instanceof KeyguardSecurityView) {
153            KeyguardSecurityView ksv = (KeyguardSecurityView) view;
154            ksv.setKeyguardCallback(mCallback);
155            ksv.setLockPatternUtils(mLockPatternUtils);
156        } else {
157            Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
158        }
159    }
160
161    protected void onFinishInflate() {
162        mSecurityViewFlipper = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
163        mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
164    }
165
166    public void setLockPatternUtils(LockPatternUtils utils) {
167        mLockPatternUtils = utils;
168        mSecurityModel.setLockPatternUtils(utils);
169        mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
170    }
171
172    private void showDialog(String title, String message) {
173        final AlertDialog dialog = new AlertDialog.Builder(mContext)
174            .setTitle(title)
175            .setMessage(message)
176            .setNeutralButton(R.string.ok, null)
177            .create();
178        if (!(mContext instanceof Activity)) {
179            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
180        }
181        dialog.show();
182    }
183
184    private void showTimeoutDialog(int timeoutMs) {
185        int timeoutInSeconds = (int) timeoutMs / 1000;
186        int messageId = 0;
187
188        switch (mSecurityModel.getSecurityMode()) {
189            case Pattern:
190                messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
191                break;
192            case PIN:
193                messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
194                break;
195            case Password:
196                messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
197                break;
198            // These don't have timeout dialogs.
199            case Invalid:
200            case None:
201            case SimPin:
202            case SimPuk:
203                break;
204        }
205
206        if (messageId != 0) {
207            final String message = mContext.getString(messageId,
208                    KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(
209                            KeyguardUpdateMonitor.getCurrentUser()),
210                    timeoutInSeconds);
211            showDialog(null, message);
212        }
213    }
214
215    private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
216        String message = null;
217        switch (userType) {
218            case USER_TYPE_PRIMARY:
219                message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
220                        attempts, remaining);
221                break;
222            case USER_TYPE_SECONDARY_USER:
223                message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user,
224                        attempts, remaining);
225                break;
226            case USER_TYPE_WORK_PROFILE:
227                message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile,
228                        attempts, remaining);
229                break;
230        }
231        showDialog(null, message);
232    }
233
234    private void showWipeDialog(int attempts, int userType) {
235        String message = null;
236        switch (userType) {
237            case USER_TYPE_PRIMARY:
238                message = mContext.getString(R.string.kg_failed_attempts_now_wiping,
239                        attempts);
240                break;
241            case USER_TYPE_SECONDARY_USER:
242                message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user,
243                        attempts);
244                break;
245            case USER_TYPE_WORK_PROFILE:
246                message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile,
247                        attempts);
248                break;
249        }
250        showDialog(null, message);
251    }
252
253    private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
254        final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
255        final int failedAttempts = monitor.getFailedUnlockAttempts(userId) + 1; // +1 for this time
256
257        if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
258
259        final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
260        final int failedAttemptsBeforeWipe =
261                dpm.getMaximumFailedPasswordsForWipe(null, userId);
262
263        final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
264                (failedAttemptsBeforeWipe - failedAttempts)
265                : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
266        if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
267            // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
268            // N attempts. Once we get below the grace period, we post this dialog every time as a
269            // clear warning until the deletion fires.
270            // Check which profile has the strictest policy for failed password attempts
271            final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
272            int userType = USER_TYPE_PRIMARY;
273            if (expiringUser == userId) {
274                // TODO: http://b/23522538
275                if (expiringUser != UserHandle.USER_SYSTEM) {
276                    userType = USER_TYPE_SECONDARY_USER;
277                }
278            } else if (expiringUser != UserHandle.USER_NULL) {
279                userType = USER_TYPE_WORK_PROFILE;
280            } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
281            if (remainingBeforeWipe > 0) {
282                showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
283            } else {
284                // Too many attempts. The device will be wiped shortly.
285                Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
286                showWipeDialog(failedAttempts, userType);
287            }
288        }
289        monitor.reportFailedStrongAuthUnlockAttempt(userId);
290        mLockPatternUtils.reportFailedPasswordAttempt(userId);
291        if (timeoutMs > 0) {
292            showTimeoutDialog(timeoutMs);
293        }
294    }
295
296    /**
297     * Shows the primary security screen for the user. This will be either the multi-selector
298     * or the user's security method.
299     * @param turningOff true if the device is being turned off
300     */
301    void showPrimarySecurityScreen(boolean turningOff) {
302        SecurityMode securityMode = mSecurityModel.getSecurityMode();
303        if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
304        showSecurityScreen(securityMode);
305    }
306
307    /**
308     * Shows the next security screen if there is one.
309     * @param authenticated true if the user entered the correct authentication
310     * @return true if keyguard is done
311     */
312    boolean showNextSecurityScreenOrFinish(boolean authenticated) {
313        if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
314        boolean finish = false;
315        boolean strongAuth = false;
316        if (mUpdateMonitor.getUserCanSkipBouncer(
317                KeyguardUpdateMonitor.getCurrentUser())) {
318            finish = true;
319        } else if (SecurityMode.None == mCurrentSecuritySelection) {
320            SecurityMode securityMode = mSecurityModel.getSecurityMode();
321            if (SecurityMode.None == securityMode) {
322                finish = true; // no security required
323            } else {
324                showSecurityScreen(securityMode); // switch to the alternate security view
325            }
326        } else if (authenticated) {
327            switch (mCurrentSecuritySelection) {
328                case Pattern:
329                case Password:
330                case PIN:
331                    strongAuth = true;
332                    finish = true;
333                    break;
334
335                case SimPin:
336                case SimPuk:
337                    // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
338                    SecurityMode securityMode = mSecurityModel.getSecurityMode();
339                    if (securityMode != SecurityMode.None
340                            || !mLockPatternUtils.isLockScreenDisabled(
341                            KeyguardUpdateMonitor.getCurrentUser())) {
342                        showSecurityScreen(securityMode);
343                    } else {
344                        finish = true;
345                    }
346                    break;
347
348                default:
349                    Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
350                    showPrimarySecurityScreen(false);
351                    break;
352            }
353        }
354        if (finish) {
355            mSecurityCallback.finish(strongAuth);
356        }
357        return finish;
358    }
359
360    /**
361     * Switches to the given security view unless it's already being shown, in which case
362     * this is a no-op.
363     *
364     * @param securityMode
365     */
366    private void showSecurityScreen(SecurityMode securityMode) {
367        if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
368
369        if (securityMode == mCurrentSecuritySelection) return;
370
371        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
372        KeyguardSecurityView newView = getSecurityView(securityMode);
373
374        // Emulate Activity life cycle
375        if (oldView != null) {
376            oldView.onPause();
377            oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
378        }
379        if (securityMode != SecurityMode.None) {
380            newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
381            newView.setKeyguardCallback(mCallback);
382        }
383
384        // Find and show this child.
385        final int childCount = mSecurityViewFlipper.getChildCount();
386
387        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
388        for (int i = 0; i < childCount; i++) {
389            if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
390                mSecurityViewFlipper.setDisplayedChild(i);
391                break;
392            }
393        }
394
395        mCurrentSecuritySelection = securityMode;
396        mSecurityCallback.onSecurityModeChanged(securityMode,
397                securityMode != SecurityMode.None && newView.needsInput());
398    }
399
400    private KeyguardSecurityViewFlipper getFlipper() {
401        for (int i = 0; i < getChildCount(); i++) {
402            View child = getChildAt(i);
403            if (child instanceof KeyguardSecurityViewFlipper) {
404                return (KeyguardSecurityViewFlipper) child;
405            }
406        }
407        return null;
408    }
409
410    private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
411        public void userActivity() {
412            if (mSecurityCallback != null) {
413                mSecurityCallback.userActivity();
414            }
415        }
416
417        public void dismiss(boolean authenticated) {
418            mSecurityCallback.dismiss(authenticated);
419        }
420
421        public boolean isVerifyUnlockOnly() {
422            return mIsVerifyUnlockOnly;
423        }
424
425        public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
426            KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
427            if (success) {
428                monitor.clearFailedUnlockAttempts();
429                mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
430            } else {
431                KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
432            }
433        }
434
435        public void reset() {
436            mSecurityCallback.reset();
437        }
438    };
439
440    // The following is used to ignore callbacks from SecurityViews that are no longer current
441    // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
442    // state for the current security method.
443    private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
444        @Override
445        public void userActivity() { }
446        @Override
447        public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
448        @Override
449        public boolean isVerifyUnlockOnly() { return false; }
450        @Override
451        public void dismiss(boolean securityVerified) { }
452        @Override
453        public void reset() {}
454    };
455
456    private int getSecurityViewIdForMode(SecurityMode securityMode) {
457        switch (securityMode) {
458            case Pattern: return R.id.keyguard_pattern_view;
459            case PIN: return R.id.keyguard_pin_view;
460            case Password: return R.id.keyguard_password_view;
461            case SimPin: return R.id.keyguard_sim_pin_view;
462            case SimPuk: return R.id.keyguard_sim_puk_view;
463        }
464        return 0;
465    }
466
467    protected int getLayoutIdFor(SecurityMode securityMode) {
468        switch (securityMode) {
469            case Pattern: return R.layout.keyguard_pattern_view;
470            case PIN: return R.layout.keyguard_pin_view;
471            case Password: return R.layout.keyguard_password_view;
472            case SimPin: return R.layout.keyguard_sim_pin_view;
473            case SimPuk: return R.layout.keyguard_sim_puk_view;
474            default:
475                return 0;
476        }
477    }
478
479    public SecurityMode getSecurityMode() {
480        return mSecurityModel.getSecurityMode();
481    }
482
483    public SecurityMode getCurrentSecurityMode() {
484        return mCurrentSecuritySelection;
485    }
486
487    public void verifyUnlock() {
488        mIsVerifyUnlockOnly = true;
489        showSecurityScreen(getSecurityMode());
490    }
491
492    public SecurityMode getCurrentSecuritySelection() {
493        return mCurrentSecuritySelection;
494    }
495
496    public void dismiss(boolean authenticated) {
497        mCallback.dismiss(authenticated);
498    }
499
500    public boolean needsInput() {
501        return mSecurityViewFlipper.needsInput();
502    }
503
504    @Override
505    public void setKeyguardCallback(KeyguardSecurityCallback callback) {
506        mSecurityViewFlipper.setKeyguardCallback(callback);
507    }
508
509    @Override
510    public void reset() {
511        mSecurityViewFlipper.reset();
512    }
513
514    @Override
515    public KeyguardSecurityCallback getCallback() {
516        return mSecurityViewFlipper.getCallback();
517    }
518
519    @Override
520    public void showPromptReason(int reason) {
521        if (mCurrentSecuritySelection != SecurityMode.None) {
522            if (reason != PROMPT_REASON_NONE) {
523                Log.i(TAG, "Strong auth required, reason: " + reason);
524            }
525            getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
526        }
527    }
528
529
530    public void showMessage(String message, int color) {
531        if (mCurrentSecuritySelection != SecurityMode.None) {
532            getSecurityView(mCurrentSecuritySelection).showMessage(message, color);
533        }
534    }
535
536    @Override
537    public void showUsabilityHint() {
538        mSecurityViewFlipper.showUsabilityHint();
539    }
540
541}
542
543