Home | History | Annotate | Download | only in keyguard
      1 /*
      2  * Copyright (C) 2007 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.keyguard;
     18 
     19 import android.app.Activity;
     20 import android.app.ActivityManager;
     21 import android.content.Context;
     22 import android.content.res.Resources;
     23 import android.graphics.Canvas;
     24 import android.media.AudioManager;
     25 import android.os.SystemClock;
     26 import android.service.trust.TrustAgentService;
     27 import android.telephony.TelephonyManager;
     28 import android.util.AttributeSet;
     29 import android.util.Log;
     30 import android.view.KeyEvent;
     31 import android.view.accessibility.AccessibilityEvent;
     32 import android.widget.FrameLayout;
     33 
     34 import com.android.internal.widget.LockPatternUtils;
     35 import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
     36 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
     37 import com.android.settingslib.Utils;
     38 
     39 import java.io.File;
     40 
     41 /**
     42  * Base class for keyguard view.  {@link #reset} is where you should
     43  * reset the state of your view.  Use the {@link KeyguardViewCallback} via
     44  * {@link #getCallback()} to send information back (such as poking the wake lock,
     45  * or finishing the keyguard).
     46  *
     47  * Handles intercepting of media keys that still work when the keyguard is
     48  * showing.
     49  */
     50 public class KeyguardHostView extends FrameLayout implements SecurityCallback {
     51 
     52     public interface OnDismissAction {
     53         /**
     54          * @return true if the dismiss should be deferred
     55          */
     56         boolean onDismiss();
     57     }
     58 
     59     private AudioManager mAudioManager;
     60     private TelephonyManager mTelephonyManager = null;
     61     protected ViewMediatorCallback mViewMediatorCallback;
     62     protected LockPatternUtils mLockPatternUtils;
     63     private OnDismissAction mDismissAction;
     64     private Runnable mCancelAction;
     65 
     66     private final KeyguardUpdateMonitorCallback mUpdateCallback =
     67             new KeyguardUpdateMonitorCallback() {
     68 
     69         @Override
     70         public void onUserSwitchComplete(int userId) {
     71             getSecurityContainer().showPrimarySecurityScreen(false /* turning off */);
     72         }
     73 
     74         @Override
     75         public void onTrustGrantedWithFlags(int flags, int userId) {
     76             if (userId != KeyguardUpdateMonitor.getCurrentUser()) return;
     77             if (!isAttachedToWindow()) return;
     78             boolean bouncerVisible = isVisibleToUser();
     79             boolean initiatedByUser =
     80                     (flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0;
     81             boolean dismissKeyguard =
     82                     (flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0;
     83 
     84             if (initiatedByUser || dismissKeyguard) {
     85                 if (mViewMediatorCallback.isScreenOn() && (bouncerVisible || dismissKeyguard)) {
     86                     if (!bouncerVisible) {
     87                         // The trust agent dismissed the keyguard without the user proving
     88                         // that they are present (by swiping up to show the bouncer). That's fine if
     89                         // the user proved presence via some other way to the trust agent.
     90                         Log.i(TAG, "TrustAgent dismissed Keyguard.");
     91                     }
     92                     dismiss(false /* authenticated */, userId);
     93                 } else {
     94                     mViewMediatorCallback.playTrustedSound();
     95                 }
     96             }
     97         }
     98     };
     99 
    100     // Whether the volume keys should be handled by keyguard. If true, then
    101     // they will be handled here for specific media types such as music, otherwise
    102     // the audio service will bring up the volume dialog.
    103     private static final boolean KEYGUARD_MANAGES_VOLUME = false;
    104     public static final boolean DEBUG = KeyguardConstants.DEBUG;
    105     private static final String TAG = "KeyguardViewBase";
    106 
    107     private KeyguardSecurityContainer mSecurityContainer;
    108 
    109     public KeyguardHostView(Context context) {
    110         this(context, null);
    111     }
    112 
    113     public KeyguardHostView(Context context, AttributeSet attrs) {
    114         super(context, attrs);
    115         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateCallback);
    116     }
    117 
    118     @Override
    119     protected void dispatchDraw(Canvas canvas) {
    120         super.dispatchDraw(canvas);
    121         if (mViewMediatorCallback != null) {
    122             mViewMediatorCallback.keyguardDoneDrawing();
    123         }
    124     }
    125 
    126     /**
    127      * Sets an action to run when keyguard finishes.
    128      *
    129      * @param action
    130      */
    131     public void setOnDismissAction(OnDismissAction action, Runnable cancelAction) {
    132         if (mCancelAction != null) {
    133             mCancelAction.run();
    134             mCancelAction = null;
    135         }
    136         mDismissAction = action;
    137         mCancelAction = cancelAction;
    138     }
    139 
    140     public boolean hasDismissActions() {
    141         return mDismissAction != null || mCancelAction != null;
    142     }
    143 
    144     public void cancelDismissAction() {
    145         setOnDismissAction(null, null);
    146     }
    147 
    148     @Override
    149     protected void onFinishInflate() {
    150         mSecurityContainer =
    151                 findViewById(R.id.keyguard_security_container);
    152         mLockPatternUtils = new LockPatternUtils(mContext);
    153         mSecurityContainer.setLockPatternUtils(mLockPatternUtils);
    154         mSecurityContainer.setSecurityCallback(this);
    155         mSecurityContainer.showPrimarySecurityScreen(false);
    156     }
    157 
    158     /**
    159      * Called when the view needs to be shown.
    160      */
    161     public void showPrimarySecurityScreen() {
    162         if (DEBUG) Log.d(TAG, "show()");
    163         mSecurityContainer.showPrimarySecurityScreen(false);
    164     }
    165 
    166     /**
    167      * Show a string explaining why the security view needs to be solved.
    168      *
    169      * @param reason a flag indicating which string should be shown, see
    170      *               {@link KeyguardSecurityView#PROMPT_REASON_NONE},
    171      *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and
    172      *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
    173      */
    174     public void showPromptReason(int reason) {
    175         mSecurityContainer.showPromptReason(reason);
    176     }
    177 
    178     public void showMessage(CharSequence message, int color) {
    179         mSecurityContainer.showMessage(message, color);
    180     }
    181 
    182     public void showErrorMessage(CharSequence message) {
    183         showMessage(message, Utils.getColorError(mContext));
    184     }
    185 
    186     /**
    187      * Dismisses the keyguard by going to the next screen or making it gone.
    188      * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
    189      * @return True if the keyguard is done.
    190      */
    191     public boolean dismiss(int targetUserId) {
    192         return dismiss(false, targetUserId);
    193     }
    194 
    195     public boolean handleBackKey() {
    196         if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) {
    197             mSecurityContainer.dismiss(false, KeyguardUpdateMonitor.getCurrentUser());
    198             return true;
    199         }
    200         return false;
    201     }
    202 
    203     protected KeyguardSecurityContainer getSecurityContainer() {
    204         return mSecurityContainer;
    205     }
    206 
    207     @Override
    208     public boolean dismiss(boolean authenticated, int targetUserId) {
    209         return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId);
    210     }
    211 
    212     /**
    213      * Authentication has happened and it's time to dismiss keyguard. This function
    214      * should clean up and inform KeyguardViewMediator.
    215      *
    216      * @param strongAuth whether the user has authenticated with strong authentication like
    217      *                   pattern, password or PIN but not by trust agents or fingerprint
    218      * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
    219      */
    220     @Override
    221     public void finish(boolean strongAuth, int targetUserId) {
    222         // If there's a pending runnable because the user interacted with a widget
    223         // and we're leaving keyguard, then run it.
    224         boolean deferKeyguardDone = false;
    225         if (mDismissAction != null) {
    226             deferKeyguardDone = mDismissAction.onDismiss();
    227             mDismissAction = null;
    228             mCancelAction = null;
    229         }
    230         if (mViewMediatorCallback != null) {
    231             if (deferKeyguardDone) {
    232                 mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
    233             } else {
    234                 mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
    235             }
    236         }
    237     }
    238 
    239     @Override
    240     public void reset() {
    241         mViewMediatorCallback.resetKeyguard();
    242     }
    243 
    244     public void resetSecurityContainer() {
    245         mSecurityContainer.reset();
    246     }
    247 
    248     @Override
    249     public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
    250         if (mViewMediatorCallback != null) {
    251             mViewMediatorCallback.setNeedsInput(needsInput);
    252         }
    253     }
    254 
    255     public CharSequence getAccessibilityTitleForCurrentMode() {
    256         return mSecurityContainer.getTitle();
    257     }
    258 
    259     public void userActivity() {
    260         if (mViewMediatorCallback != null) {
    261             mViewMediatorCallback.userActivity();
    262         }
    263     }
    264 
    265     /**
    266      * Called when the Keyguard is not actively shown anymore on the screen.
    267      */
    268     public void onPause() {
    269         if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
    270                 Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
    271         mSecurityContainer.showPrimarySecurityScreen(true);
    272         mSecurityContainer.onPause();
    273         clearFocus();
    274     }
    275 
    276     /**
    277      * Called when the Keyguard is actively shown on the screen.
    278      */
    279     public void onResume() {
    280         if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
    281         mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON);
    282         requestFocus();
    283     }
    284 
    285     /**
    286      * Starts the animation when the Keyguard gets shown.
    287      */
    288     public void startAppearAnimation() {
    289         mSecurityContainer.startAppearAnimation();
    290     }
    291 
    292     public void startDisappearAnimation(Runnable finishRunnable) {
    293         if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) {
    294             finishRunnable.run();
    295         }
    296     }
    297 
    298     /**
    299      * Called before this view is being removed.
    300      */
    301     public void cleanUp() {
    302         getSecurityContainer().onPause();
    303     }
    304 
    305     @Override
    306     public boolean dispatchKeyEvent(KeyEvent event) {
    307         if (interceptMediaKey(event)) {
    308             return true;
    309         }
    310         return super.dispatchKeyEvent(event);
    311     }
    312 
    313     /**
    314      * Allows the media keys to work when the keyguard is showing.
    315      * The media keys should be of no interest to the actual keyguard view(s),
    316      * so intercepting them here should not be of any harm.
    317      * @param event The key event
    318      * @return whether the event was consumed as a media key.
    319      */
    320     public boolean interceptMediaKey(KeyEvent event) {
    321         final int keyCode = event.getKeyCode();
    322         if (event.getAction() == KeyEvent.ACTION_DOWN) {
    323             switch (keyCode) {
    324                 case KeyEvent.KEYCODE_MEDIA_PLAY:
    325                 case KeyEvent.KEYCODE_MEDIA_PAUSE:
    326                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
    327                     /* Suppress PLAY/PAUSE toggle when phone is ringing or
    328                      * in-call to avoid music playback */
    329                     if (mTelephonyManager == null) {
    330                         mTelephonyManager = (TelephonyManager) getContext().getSystemService(
    331                                 Context.TELEPHONY_SERVICE);
    332                     }
    333                     if (mTelephonyManager != null &&
    334                             mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
    335                         return true;  // suppress key event
    336                     }
    337                 case KeyEvent.KEYCODE_MUTE:
    338                 case KeyEvent.KEYCODE_HEADSETHOOK:
    339                 case KeyEvent.KEYCODE_MEDIA_STOP:
    340                 case KeyEvent.KEYCODE_MEDIA_NEXT:
    341                 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
    342                 case KeyEvent.KEYCODE_MEDIA_REWIND:
    343                 case KeyEvent.KEYCODE_MEDIA_RECORD:
    344                 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
    345                 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
    346                     handleMediaKeyEvent(event);
    347                     return true;
    348                 }
    349 
    350                 case KeyEvent.KEYCODE_VOLUME_UP:
    351                 case KeyEvent.KEYCODE_VOLUME_DOWN:
    352                 case KeyEvent.KEYCODE_VOLUME_MUTE: {
    353                     if (KEYGUARD_MANAGES_VOLUME) {
    354                         synchronized (this) {
    355                             if (mAudioManager == null) {
    356                                 mAudioManager = (AudioManager) getContext().getSystemService(
    357                                         Context.AUDIO_SERVICE);
    358                             }
    359                         }
    360                         // Volume buttons should only function for music (local or remote).
    361                         // TODO: Actually handle MUTE.
    362                         mAudioManager.adjustSuggestedStreamVolume(
    363                                 keyCode == KeyEvent.KEYCODE_VOLUME_UP
    364                                         ? AudioManager.ADJUST_RAISE
    365                                         : AudioManager.ADJUST_LOWER /* direction */,
    366                                 AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
    367                         // Don't execute default volume behavior
    368                         return true;
    369                     } else {
    370                         return false;
    371                     }
    372                 }
    373             }
    374         } else if (event.getAction() == KeyEvent.ACTION_UP) {
    375             switch (keyCode) {
    376                 case KeyEvent.KEYCODE_MUTE:
    377                 case KeyEvent.KEYCODE_HEADSETHOOK:
    378                 case KeyEvent.KEYCODE_MEDIA_PLAY:
    379                 case KeyEvent.KEYCODE_MEDIA_PAUSE:
    380                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
    381                 case KeyEvent.KEYCODE_MEDIA_STOP:
    382                 case KeyEvent.KEYCODE_MEDIA_NEXT:
    383                 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
    384                 case KeyEvent.KEYCODE_MEDIA_REWIND:
    385                 case KeyEvent.KEYCODE_MEDIA_RECORD:
    386                 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
    387                 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
    388                     handleMediaKeyEvent(event);
    389                     return true;
    390                 }
    391             }
    392         }
    393         return false;
    394     }
    395 
    396     private void handleMediaKeyEvent(KeyEvent keyEvent) {
    397         synchronized (this) {
    398             if (mAudioManager == null) {
    399                 mAudioManager = (AudioManager) getContext().getSystemService(
    400                         Context.AUDIO_SERVICE);
    401             }
    402         }
    403         mAudioManager.dispatchMediaKeyEvent(keyEvent);
    404     }
    405 
    406     @Override
    407     public void dispatchSystemUiVisibilityChanged(int visibility) {
    408         super.dispatchSystemUiVisibilityChanged(visibility);
    409 
    410         if (!(mContext instanceof Activity)) {
    411             setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
    412         }
    413     }
    414 
    415     /**
    416      * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
    417      * some cases where we wish to disable it, notably when the menu button placement or technology
    418      * is prone to false positives.
    419      *
    420      * @return true if the menu key should be enabled
    421      */
    422     private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
    423     public boolean shouldEnableMenuKey() {
    424         final Resources res = getResources();
    425         final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
    426         final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
    427         final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
    428         return !configDisabled || isTestHarness || fileOverride;
    429     }
    430 
    431     public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) {
    432         mViewMediatorCallback = viewMediatorCallback;
    433         // Update ViewMediator with the current input method requirements
    434         mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput());
    435     }
    436 
    437     public void setLockPatternUtils(LockPatternUtils utils) {
    438         mLockPatternUtils = utils;
    439         mSecurityContainer.setLockPatternUtils(utils);
    440     }
    441 
    442     public SecurityMode getSecurityMode() {
    443         return mSecurityContainer.getSecurityMode();
    444     }
    445 
    446     public SecurityMode getCurrentSecurityMode() {
    447         return mSecurityContainer.getCurrentSecurityMode();
    448     }
    449 }
    450