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.app.ActivityOptions;
     22 import android.app.SearchManager;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.res.Resources;
     26 import android.graphics.Canvas;
     27 import android.media.AudioManager;
     28 import android.media.IAudioService;
     29 import android.os.Bundle;
     30 import android.os.RemoteException;
     31 import android.os.ServiceManager;
     32 import android.os.SystemClock;
     33 import android.os.UserHandle;
     34 import android.telephony.TelephonyManager;
     35 import android.util.AttributeSet;
     36 import android.util.Log;
     37 import android.util.Slog;
     38 import android.view.KeyEvent;
     39 import android.view.MotionEvent;
     40 import android.view.View;
     41 import android.view.accessibility.AccessibilityEvent;
     42 import android.widget.FrameLayout;
     43 
     44 import com.android.internal.widget.LockPatternUtils;
     45 import com.android.keyguard.KeyguardHostView.OnDismissAction;
     46 import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
     47 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
     48 
     49 import java.io.File;
     50 
     51 /**
     52  * Base class for keyguard view.  {@link #reset} is where you should
     53  * reset the state of your view.  Use the {@link KeyguardViewCallback} via
     54  * {@link #getCallback()} to send information back (such as poking the wake lock,
     55  * or finishing the keyguard).
     56  *
     57  * Handles intercepting of media keys that still work when the keyguard is
     58  * showing.
     59  */
     60 public abstract class KeyguardViewBase extends FrameLayout implements SecurityCallback {
     61 
     62     private AudioManager mAudioManager;
     63     private TelephonyManager mTelephonyManager = null;
     64     protected ViewMediatorCallback mViewMediatorCallback;
     65     protected LockPatternUtils mLockPatternUtils;
     66     private OnDismissAction mDismissAction;
     67 
     68     // Whether the volume keys should be handled by keyguard. If true, then
     69     // they will be handled here for specific media types such as music, otherwise
     70     // the audio service will bring up the volume dialog.
     71     private static final boolean KEYGUARD_MANAGES_VOLUME = false;
     72     public static final boolean DEBUG = KeyguardConstants.DEBUG;
     73     private static final String TAG = "KeyguardViewBase";
     74 
     75     private KeyguardSecurityContainer mSecurityContainer;
     76 
     77     public KeyguardViewBase(Context context) {
     78         this(context, null);
     79     }
     80 
     81     public KeyguardViewBase(Context context, AttributeSet attrs) {
     82         super(context, attrs);
     83     }
     84 
     85     @Override
     86     protected void dispatchDraw(Canvas canvas) {
     87         super.dispatchDraw(canvas);
     88         if (mViewMediatorCallback != null) {
     89             mViewMediatorCallback.keyguardDoneDrawing();
     90         }
     91     }
     92 
     93     /**
     94      * Sets an action to run when keyguard finishes.
     95      *
     96      * @param action
     97      */
     98     public void setOnDismissAction(OnDismissAction action) {
     99         mDismissAction = action;
    100     }
    101 
    102     @Override
    103     protected void onFinishInflate() {
    104         mSecurityContainer =
    105                 (KeyguardSecurityContainer) findViewById(R.id.keyguard_security_container);
    106         mLockPatternUtils = new LockPatternUtils(mContext);
    107         mSecurityContainer.setLockPatternUtils(mLockPatternUtils);
    108         mSecurityContainer.setSecurityCallback(this);
    109         mSecurityContainer.showPrimarySecurityScreen(false);
    110         // mSecurityContainer.updateSecurityViews(false /* not bouncing */);
    111     }
    112 
    113     /**
    114      * Called when the view needs to be shown.
    115      */
    116     public void showPrimarySecurityScreen() {
    117         if (DEBUG) Log.d(TAG, "show()");
    118         mSecurityContainer.showPrimarySecurityScreen(false);
    119     }
    120 
    121     /**
    122      *  Dismisses the keyguard by going to the next screen or making it gone.
    123      *
    124      *  @return True if the keyguard is done.
    125      */
    126     public boolean dismiss() {
    127         return dismiss(false);
    128     }
    129 
    130     protected void showBouncer(boolean show) {
    131         CharSequence what = getContext().getResources().getText(
    132                 show ? R.string.keyguard_accessibility_show_bouncer
    133                         : R.string.keyguard_accessibility_hide_bouncer);
    134         announceForAccessibility(what);
    135         announceCurrentSecurityMethod();
    136     }
    137 
    138     public boolean handleBackKey() {
    139         if (mSecurityContainer.getCurrentSecuritySelection() == SecurityMode.Account) {
    140             // go back to primary screen
    141             mSecurityContainer.showPrimarySecurityScreen(false /*turningOff*/);
    142             return true;
    143         }
    144         if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) {
    145             mSecurityContainer.dismiss(false);
    146             return true;
    147         }
    148         return false;
    149     }
    150 
    151     protected void announceCurrentSecurityMethod() {
    152         mSecurityContainer.announceCurrentSecurityMethod();
    153     }
    154 
    155     @Override
    156     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
    157         if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
    158             event.getText().add(mSecurityContainer.getCurrentSecurityModeContentDescription());
    159             return true;
    160         } else {
    161             return super.dispatchPopulateAccessibilityEvent(event);
    162         }
    163     }
    164 
    165     protected KeyguardSecurityContainer getSecurityContainer() {
    166         return mSecurityContainer;
    167     }
    168 
    169     @Override
    170     public boolean dismiss(boolean authenticated) {
    171         return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated);
    172     }
    173 
    174     /**
    175      * Authentication has happened and it's time to dismiss keyguard. This function
    176      * should clean up and inform KeyguardViewMediator.
    177      */
    178     @Override
    179     public void finish() {
    180         // If the alternate unlock was suppressed, it can now be safely
    181         // enabled because the user has left keyguard.
    182         KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
    183 
    184         // If there's a pending runnable because the user interacted with a widget
    185         // and we're leaving keyguard, then run it.
    186         boolean deferKeyguardDone = false;
    187         if (mDismissAction != null) {
    188             deferKeyguardDone = mDismissAction.onDismiss();
    189             mDismissAction = null;
    190         }
    191         if (mViewMediatorCallback != null) {
    192             if (deferKeyguardDone) {
    193                 mViewMediatorCallback.keyguardDonePending();
    194             } else {
    195                 mViewMediatorCallback.keyguardDone(true);
    196             }
    197         }
    198     }
    199 
    200     @Override
    201     public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
    202         if (mViewMediatorCallback != null) {
    203             mViewMediatorCallback.setNeedsInput(needsInput);
    204         }
    205     }
    206 
    207     public void userActivity() {
    208         if (mViewMediatorCallback != null) {
    209             mViewMediatorCallback.userActivity();
    210         }
    211     }
    212 
    213     protected void onUserActivityTimeoutChanged() {
    214         if (mViewMediatorCallback != null) {
    215             mViewMediatorCallback.onUserActivityTimeoutChanged();
    216         }
    217     }
    218 
    219     /**
    220      * Called when the Keyguard is not actively shown anymore on the screen.
    221      */
    222     public void onPause() {
    223         if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
    224                 Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
    225         // Once the screen turns off, we no longer consider this to be first boot and we want the
    226         // biometric unlock to start next time keyguard is shown.
    227         KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
    228         mSecurityContainer.showPrimarySecurityScreen(true);
    229         mSecurityContainer.onPause();
    230         clearFocus();
    231     }
    232 
    233     /**
    234      * Called when the Keyguard is actively shown on the screen.
    235      */
    236     public void onResume() {
    237         if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
    238         mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON);
    239         requestFocus();
    240     }
    241 
    242     /**
    243      * Starts the animation when the Keyguard gets shown.
    244      */
    245     public void startAppearAnimation() {
    246         mSecurityContainer.startAppearAnimation();
    247     }
    248 
    249     public void startDisappearAnimation(Runnable finishRunnable) {
    250         if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) {
    251             finishRunnable.run();
    252         }
    253     }
    254 
    255     /**
    256      * Verify that the user can get past the keyguard securely.  This is called,
    257      * for example, when the phone disables the keyguard but then wants to launch
    258      * something else that requires secure access.
    259      *
    260      * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
    261      */
    262     public void verifyUnlock() {
    263         SecurityMode securityMode = mSecurityContainer.getSecurityMode();
    264         if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
    265             if (mViewMediatorCallback != null) {
    266                 mViewMediatorCallback.keyguardDone(true);
    267             }
    268         } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
    269                 && securityMode != KeyguardSecurityModel.SecurityMode.PIN
    270                 && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
    271             // can only verify unlock when in pattern/password mode
    272             if (mViewMediatorCallback != null) {
    273                 mViewMediatorCallback.keyguardDone(false);
    274             }
    275         } else {
    276             // otherwise, go to the unlock screen, see if they can verify it
    277             mSecurityContainer.verifyUnlock();
    278         }
    279     }
    280 
    281     /**
    282      * Called before this view is being removed.
    283      */
    284     abstract public void cleanUp();
    285 
    286     /**
    287      * Gets the desired user activity timeout in milliseconds, or -1 if the
    288      * default should be used.
    289      */
    290     abstract public long getUserActivityTimeout();
    291 
    292     @Override
    293     public boolean dispatchKeyEvent(KeyEvent event) {
    294         if (interceptMediaKey(event)) {
    295             return true;
    296         }
    297         return super.dispatchKeyEvent(event);
    298     }
    299 
    300     /**
    301      * Allows the media keys to work when the keyguard is showing.
    302      * The media keys should be of no interest to the actual keyguard view(s),
    303      * so intercepting them here should not be of any harm.
    304      * @param event The key event
    305      * @return whether the event was consumed as a media key.
    306      */
    307     public boolean interceptMediaKey(KeyEvent event) {
    308         final int keyCode = event.getKeyCode();
    309         if (event.getAction() == KeyEvent.ACTION_DOWN) {
    310             switch (keyCode) {
    311                 case KeyEvent.KEYCODE_MEDIA_PLAY:
    312                 case KeyEvent.KEYCODE_MEDIA_PAUSE:
    313                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
    314                     /* Suppress PLAY/PAUSE toggle when phone is ringing or
    315                      * in-call to avoid music playback */
    316                     if (mTelephonyManager == null) {
    317                         mTelephonyManager = (TelephonyManager) getContext().getSystemService(
    318                                 Context.TELEPHONY_SERVICE);
    319                     }
    320                     if (mTelephonyManager != null &&
    321                             mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
    322                         return true;  // suppress key event
    323                     }
    324                 case KeyEvent.KEYCODE_MUTE:
    325                 case KeyEvent.KEYCODE_HEADSETHOOK:
    326                 case KeyEvent.KEYCODE_MEDIA_STOP:
    327                 case KeyEvent.KEYCODE_MEDIA_NEXT:
    328                 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
    329                 case KeyEvent.KEYCODE_MEDIA_REWIND:
    330                 case KeyEvent.KEYCODE_MEDIA_RECORD:
    331                 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
    332                 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
    333                     handleMediaKeyEvent(event);
    334                     return true;
    335                 }
    336 
    337                 case KeyEvent.KEYCODE_VOLUME_UP:
    338                 case KeyEvent.KEYCODE_VOLUME_DOWN:
    339                 case KeyEvent.KEYCODE_VOLUME_MUTE: {
    340                     if (KEYGUARD_MANAGES_VOLUME) {
    341                         synchronized (this) {
    342                             if (mAudioManager == null) {
    343                                 mAudioManager = (AudioManager) getContext().getSystemService(
    344                                         Context.AUDIO_SERVICE);
    345                             }
    346                         }
    347                         // Volume buttons should only function for music (local or remote).
    348                         // TODO: Actually handle MUTE.
    349                         mAudioManager.adjustSuggestedStreamVolume(
    350                                 keyCode == KeyEvent.KEYCODE_VOLUME_UP
    351                                         ? AudioManager.ADJUST_RAISE
    352                                         : AudioManager.ADJUST_LOWER /* direction */,
    353                                 AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
    354                         // Don't execute default volume behavior
    355                         return true;
    356                     } else {
    357                         return false;
    358                     }
    359                 }
    360             }
    361         } else if (event.getAction() == KeyEvent.ACTION_UP) {
    362             switch (keyCode) {
    363                 case KeyEvent.KEYCODE_MUTE:
    364                 case KeyEvent.KEYCODE_HEADSETHOOK:
    365                 case KeyEvent.KEYCODE_MEDIA_PLAY:
    366                 case KeyEvent.KEYCODE_MEDIA_PAUSE:
    367                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
    368                 case KeyEvent.KEYCODE_MEDIA_STOP:
    369                 case KeyEvent.KEYCODE_MEDIA_NEXT:
    370                 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
    371                 case KeyEvent.KEYCODE_MEDIA_REWIND:
    372                 case KeyEvent.KEYCODE_MEDIA_RECORD:
    373                 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
    374                 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
    375                     handleMediaKeyEvent(event);
    376                     return true;
    377                 }
    378             }
    379         }
    380         return false;
    381     }
    382 
    383     private void handleMediaKeyEvent(KeyEvent keyEvent) {
    384         synchronized (this) {
    385             if (mAudioManager == null) {
    386                 mAudioManager = (AudioManager) getContext().getSystemService(
    387                         Context.AUDIO_SERVICE);
    388             }
    389         }
    390         mAudioManager.dispatchMediaKeyEvent(keyEvent);
    391     }
    392 
    393     @Override
    394     public void dispatchSystemUiVisibilityChanged(int visibility) {
    395         super.dispatchSystemUiVisibilityChanged(visibility);
    396 
    397         if (!(mContext instanceof Activity)) {
    398             setSystemUiVisibility(STATUS_BAR_DISABLE_BACK);
    399         }
    400     }
    401 
    402     /**
    403      * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
    404      * some cases where we wish to disable it, notably when the menu button placement or technology
    405      * is prone to false positives.
    406      *
    407      * @return true if the menu key should be enabled
    408      */
    409     private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
    410     private boolean shouldEnableMenuKey() {
    411         final Resources res = getResources();
    412         final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
    413         final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
    414         final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
    415         return !configDisabled || isTestHarness || fileOverride;
    416     }
    417 
    418     public boolean handleMenuKey() {
    419         // The following enables the MENU key to work for testing automation
    420         if (shouldEnableMenuKey()) {
    421             dismiss();
    422             return true;
    423         }
    424         return false;
    425     }
    426 
    427     public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) {
    428         mViewMediatorCallback = viewMediatorCallback;
    429         // Update ViewMediator with the current input method requirements
    430         mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput());
    431     }
    432 
    433     protected KeyguardActivityLauncher getActivityLauncher() {
    434         return mActivityLauncher;
    435     }
    436 
    437     private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
    438         @Override
    439         Context getContext() {
    440             return mContext;
    441         }
    442 
    443         @Override
    444         void setOnDismissAction(OnDismissAction action) {
    445             KeyguardViewBase.this.setOnDismissAction(action);
    446         }
    447 
    448         @Override
    449         LockPatternUtils getLockPatternUtils() {
    450             return mLockPatternUtils;
    451         }
    452 
    453         @Override
    454         void requestDismissKeyguard() {
    455             KeyguardViewBase.this.dismiss(false);
    456         }
    457     };
    458 
    459     public void showAssistant() {
    460         final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
    461           .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
    462 
    463         if (intent == null) return;
    464 
    465         final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
    466                 R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit,
    467                 getHandler(), null);
    468 
    469         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    470         mActivityLauncher.launchActivityWithAnimation(intent, false, opts.toBundle(), null, null);
    471     }
    472 
    473     public void launchCamera() {
    474         mActivityLauncher.launchCamera(getHandler(), null);
    475     }
    476 
    477     public void setLockPatternUtils(LockPatternUtils utils) {
    478         mLockPatternUtils = utils;
    479         mSecurityContainer.setLockPatternUtils(utils);
    480     }
    481 
    482     public SecurityMode getSecurityMode() {
    483         return mSecurityContainer.getSecurityMode();
    484     }
    485 
    486     public SecurityMode getCurrentSecurityMode() {
    487         return mSecurityContainer.getCurrentSecurityMode();
    488     }
    489 
    490     protected abstract void onUserSwitching(boolean switching);
    491 
    492     protected abstract void onCreateOptions(Bundle options);
    493 
    494     protected abstract void onExternalMotionEvent(MotionEvent event);
    495 
    496 }
    497