Home | History | Annotate | Download | only in phone
      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  */
     16 
     17 package com.android.systemui.statusbar.phone;
     18 
     19 import android.app.ActivityManager;
     20 import android.content.Context;
     21 import android.os.Handler;
     22 import android.os.UserHandle;
     23 import android.os.UserManager;
     24 import android.util.Slog;
     25 import android.view.KeyEvent;
     26 import android.view.LayoutInflater;
     27 import android.view.View;
     28 import android.view.ViewGroup;
     29 import android.view.ViewTreeObserver;
     30 import android.view.WindowInsets;
     31 import android.view.accessibility.AccessibilityEvent;
     32 
     33 import com.android.internal.widget.LockPatternUtils;
     34 import com.android.keyguard.KeyguardHostView;
     35 import com.android.keyguard.KeyguardSecurityView;
     36 import com.android.keyguard.KeyguardUpdateMonitor;
     37 import com.android.keyguard.KeyguardUpdateMonitorCallback;
     38 import com.android.keyguard.R;
     39 import com.android.keyguard.ViewMediatorCallback;
     40 import com.android.systemui.DejankUtils;
     41 import com.android.systemui.classifier.FalsingManager;
     42 import com.android.systemui.keyguard.DismissCallbackRegistry;
     43 
     44 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
     45 import static com.android.keyguard.KeyguardSecurityModel.SecurityMode;
     46 
     47 /**
     48  * A class which manages the bouncer on the lockscreen.
     49  */
     50 public class KeyguardBouncer {
     51 
     52     final static private String TAG = "KeyguardBouncer";
     53 
     54     protected final Context mContext;
     55     protected final ViewMediatorCallback mCallback;
     56     protected final LockPatternUtils mLockPatternUtils;
     57     protected final ViewGroup mContainer;
     58     private final FalsingManager mFalsingManager;
     59     private final DismissCallbackRegistry mDismissCallbackRegistry;
     60     private final Handler mHandler;
     61     protected KeyguardHostView mKeyguardView;
     62     protected ViewGroup mRoot;
     63     private boolean mShowingSoon;
     64     private int mBouncerPromptReason;
     65     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
     66             new KeyguardUpdateMonitorCallback() {
     67                 @Override
     68                 public void onStrongAuthStateChanged(int userId) {
     69                     mBouncerPromptReason = mCallback.getBouncerPromptReason();
     70                 }
     71             };
     72     private final Runnable mRemoveViewRunnable = this::removeView;
     73     private int mStatusBarHeight;
     74 
     75     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
     76             LockPatternUtils lockPatternUtils, ViewGroup container,
     77             DismissCallbackRegistry dismissCallbackRegistry) {
     78         mContext = context;
     79         mCallback = callback;
     80         mLockPatternUtils = lockPatternUtils;
     81         mContainer = container;
     82         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
     83         mFalsingManager = FalsingManager.getInstance(mContext);
     84         mDismissCallbackRegistry = dismissCallbackRegistry;
     85         mHandler = new Handler();
     86     }
     87 
     88     public void show(boolean resetSecuritySelection) {
     89         final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser();
     90         if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
     91             // In split system user mode, we never unlock system user.
     92             return;
     93         }
     94         mFalsingManager.onBouncerShown();
     95         ensureView();
     96         if (resetSecuritySelection) {
     97             // showPrimarySecurityScreen() updates the current security method. This is needed in
     98             // case we are already showing and the current security method changed.
     99             mKeyguardView.showPrimarySecurityScreen();
    100         }
    101         if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
    102             return;
    103         }
    104 
    105         final int activeUserId = ActivityManager.getCurrentUser();
    106         final boolean isSystemUser =
    107                 UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM;
    108         final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId;
    109 
    110         // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is
    111         // set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.
    112         if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
    113             return;
    114         }
    115 
    116         // This condition may indicate an error on Android, so log it.
    117         if (!allowDismissKeyguard) {
    118             Slog.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId);
    119         }
    120 
    121         mShowingSoon = true;
    122 
    123         // Split up the work over multiple frames.
    124         DejankUtils.postAfterTraversal(mShowRunnable);
    125     }
    126 
    127     private final Runnable mShowRunnable = new Runnable() {
    128         @Override
    129         public void run() {
    130             mRoot.setVisibility(View.VISIBLE);
    131             mKeyguardView.onResume();
    132             showPromptReason(mBouncerPromptReason);
    133             // We might still be collapsed and the view didn't have time to layout yet or still
    134             // be small, let's wait on the predraw to do the animation in that case.
    135             if (mKeyguardView.getHeight() != 0 && mKeyguardView.getHeight() != mStatusBarHeight) {
    136                 mKeyguardView.startAppearAnimation();
    137             } else {
    138                 mKeyguardView.getViewTreeObserver().addOnPreDrawListener(
    139                         new ViewTreeObserver.OnPreDrawListener() {
    140                             @Override
    141                             public boolean onPreDraw() {
    142                                 mKeyguardView.getViewTreeObserver().removeOnPreDrawListener(this);
    143                                 mKeyguardView.startAppearAnimation();
    144                                 return true;
    145                             }
    146                         });
    147                 mKeyguardView.requestLayout();
    148             }
    149             mShowingSoon = false;
    150             mKeyguardView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    151         }
    152     };
    153 
    154     /**
    155      * Show a string explaining why the security view needs to be solved.
    156      *
    157      * @param reason a flag indicating which string should be shown, see
    158      *               {@link KeyguardSecurityView#PROMPT_REASON_NONE}
    159      *               and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}
    160      */
    161     public void showPromptReason(int reason) {
    162         mKeyguardView.showPromptReason(reason);
    163     }
    164 
    165     public void showMessage(String message, int color) {
    166         mKeyguardView.showMessage(message, color);
    167     }
    168 
    169     private void cancelShowRunnable() {
    170         DejankUtils.removeCallbacks(mShowRunnable);
    171         mShowingSoon = false;
    172     }
    173 
    174     public void showWithDismissAction(OnDismissAction r, Runnable cancelAction) {
    175         ensureView();
    176         mKeyguardView.setOnDismissAction(r, cancelAction);
    177         show(false /* resetSecuritySelection */);
    178     }
    179 
    180     public void hide(boolean destroyView) {
    181         if (isShowing()) {
    182             mDismissCallbackRegistry.notifyDismissCancelled();
    183         }
    184         mFalsingManager.onBouncerHidden();
    185         cancelShowRunnable();
    186         if (mKeyguardView != null) {
    187             mKeyguardView.cancelDismissAction();
    188             mKeyguardView.cleanUp();
    189         }
    190         if (mRoot != null) {
    191             mRoot.setVisibility(View.INVISIBLE);
    192             if (destroyView) {
    193 
    194                 // We have a ViewFlipper that unregisters a broadcast when being detached, which may
    195                 // be slow because of AM lock contention during unlocking. We can delay it a bit.
    196                 mHandler.postDelayed(mRemoveViewRunnable, 50);
    197             }
    198         }
    199     }
    200 
    201     /**
    202      * See {@link StatusBarKeyguardViewManager#startPreHideAnimation}.
    203      */
    204     public void startPreHideAnimation(Runnable runnable) {
    205         if (mKeyguardView != null) {
    206             mKeyguardView.startDisappearAnimation(runnable);
    207         } else if (runnable != null) {
    208             runnable.run();
    209         }
    210     }
    211 
    212     /**
    213      * Reset the state of the view.
    214      */
    215     public void reset() {
    216         cancelShowRunnable();
    217         inflateView();
    218         mFalsingManager.onBouncerHidden();
    219     }
    220 
    221     public void onScreenTurnedOff() {
    222         if (mKeyguardView != null && mRoot != null && mRoot.getVisibility() == View.VISIBLE) {
    223             mKeyguardView.onPause();
    224         }
    225     }
    226 
    227     public boolean isShowing() {
    228         return mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE);
    229     }
    230 
    231     public void prepare() {
    232         boolean wasInitialized = mRoot != null;
    233         ensureView();
    234         if (wasInitialized) {
    235             mKeyguardView.showPrimarySecurityScreen();
    236         }
    237         mBouncerPromptReason = mCallback.getBouncerPromptReason();
    238     }
    239 
    240     protected void ensureView() {
    241         // Removal of the view might be deferred to reduce unlock latency,
    242         // in this case we need to force the removal, otherwise we'll
    243         // end up in an unpredictable state.
    244         boolean forceRemoval = mHandler.hasCallbacks(mRemoveViewRunnable);
    245         if (mRoot == null || forceRemoval) {
    246             inflateView();
    247         }
    248     }
    249 
    250     protected void inflateView() {
    251         removeView();
    252         mHandler.removeCallbacks(mRemoveViewRunnable);
    253         mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
    254         mKeyguardView = mRoot.findViewById(R.id.keyguard_host_view);
    255         mKeyguardView.setLockPatternUtils(mLockPatternUtils);
    256         mKeyguardView.setViewMediatorCallback(mCallback);
    257         mContainer.addView(mRoot, mContainer.getChildCount());
    258         mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset(
    259                 com.android.systemui.R.dimen.status_bar_height);
    260         mRoot.setVisibility(View.INVISIBLE);
    261 
    262         final WindowInsets rootInsets = mRoot.getRootWindowInsets();
    263         if (rootInsets != null) {
    264             mRoot.dispatchApplyWindowInsets(rootInsets);
    265         }
    266     }
    267 
    268     protected void removeView() {
    269         if (mRoot != null && mRoot.getParent() == mContainer) {
    270             mContainer.removeView(mRoot);
    271             mRoot = null;
    272         }
    273     }
    274 
    275     public boolean onBackPressed() {
    276         return mKeyguardView != null && mKeyguardView.handleBackKey();
    277     }
    278 
    279     /**
    280      * @return True if and only if the security method should be shown before showing the
    281      * notifications on Keyguard, like SIM PIN/PUK.
    282      */
    283     public boolean needsFullscreenBouncer() {
    284         ensureView();
    285         if (mKeyguardView != null) {
    286             SecurityMode mode = mKeyguardView.getSecurityMode();
    287             return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
    288         }
    289         return false;
    290     }
    291 
    292     /**
    293      * Like {@link #needsFullscreenBouncer}, but uses the currently visible security method, which
    294      * makes this method much faster.
    295      */
    296     public boolean isFullscreenBouncer() {
    297         if (mKeyguardView != null) {
    298             SecurityMode mode = mKeyguardView.getCurrentSecurityMode();
    299             return mode == SecurityMode.SimPin || mode == SecurityMode.SimPuk;
    300         }
    301         return false;
    302     }
    303 
    304     /**
    305      * WARNING: This method might cause Binder calls.
    306      */
    307     public boolean isSecure() {
    308         return mKeyguardView == null || mKeyguardView.getSecurityMode() != SecurityMode.None;
    309     }
    310 
    311     public boolean shouldDismissOnMenuPressed() {
    312         return mKeyguardView.shouldEnableMenuKey();
    313     }
    314 
    315     public boolean interceptMediaKey(KeyEvent event) {
    316         ensureView();
    317         return mKeyguardView.interceptMediaKey(event);
    318     }
    319 
    320     public void notifyKeyguardAuthenticated(boolean strongAuth) {
    321         ensureView();
    322         mKeyguardView.finish(strongAuth, KeyguardUpdateMonitor.getCurrentUser());
    323     }
    324 }
    325