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