Home | History | Annotate | Download | only in keyguard
      1 /*
      2  * Copyright (C) 2012 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 com.android.internal.widget.LockPatternUtils;
     20 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
     21 import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
     22 
     23 import android.app.Activity;
     24 import android.app.ActivityManager;
     25 import android.app.ActivityOptions;
     26 import android.app.AlertDialog;
     27 import android.app.SearchManager;
     28 import android.app.admin.DevicePolicyManager;
     29 import android.appwidget.AppWidgetHost;
     30 import android.appwidget.AppWidgetHostView;
     31 import android.appwidget.AppWidgetManager;
     32 import android.appwidget.AppWidgetProviderInfo;
     33 import android.content.ComponentName;
     34 import android.content.Context;
     35 import android.content.Intent;
     36 import android.content.IntentSender;
     37 import android.content.pm.PackageManager.NameNotFoundException;
     38 import android.content.pm.UserInfo;
     39 import android.content.res.Resources;
     40 import android.graphics.Canvas;
     41 import android.graphics.Rect;
     42 import android.media.RemoteControlClient;
     43 import android.os.Looper;
     44 import android.os.Parcel;
     45 import android.os.Parcelable;
     46 import android.os.SystemClock;
     47 import android.os.UserHandle;
     48 import android.os.UserManager;
     49 import android.provider.Settings;
     50 import android.util.AttributeSet;
     51 import android.util.Log;
     52 import android.util.Slog;
     53 import android.view.LayoutInflater;
     54 import android.view.MotionEvent;
     55 import android.view.View;
     56 import android.view.WindowManager;
     57 import android.widget.RemoteViews.OnClickHandler;
     58 
     59 import java.io.File;
     60 import java.lang.ref.WeakReference;
     61 import java.util.List;
     62 
     63 public class KeyguardHostView extends KeyguardViewBase {
     64     private static final String TAG = "KeyguardHostView";
     65 
     66     // Transport control states.
     67     static final int TRANSPORT_GONE = 0;
     68     static final int TRANSPORT_INVISIBLE = 1;
     69     static final int TRANSPORT_VISIBLE = 2;
     70 
     71     private int mTransportState = TRANSPORT_GONE;
     72 
     73     // Use this to debug all of keyguard
     74     public static boolean DEBUG = KeyguardViewMediator.DEBUG;
     75     public static boolean DEBUGXPORT = true; // debug music transport control
     76 
     77     // Found in KeyguardAppWidgetPickActivity.java
     78     static final int APPWIDGET_HOST_ID = 0x4B455947;
     79 
     80     private final int MAX_WIDGETS = 5;
     81 
     82     private AppWidgetHost mAppWidgetHost;
     83     private AppWidgetManager mAppWidgetManager;
     84     private KeyguardWidgetPager mAppWidgetContainer;
     85     private KeyguardSecurityViewFlipper mSecurityViewContainer;
     86     private KeyguardSelectorView mKeyguardSelectorView;
     87     private KeyguardTransportControlView mTransportControl;
     88     private boolean mIsVerifyUnlockOnly;
     89     private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
     90     private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
     91     private int mAppWidgetToShow;
     92 
     93     protected OnDismissAction mDismissAction;
     94 
     95     protected int mFailedAttempts;
     96     private LockPatternUtils mLockPatternUtils;
     97 
     98     private KeyguardSecurityModel mSecurityModel;
     99     private KeyguardViewStateManager mViewStateManager;
    100 
    101     private Rect mTempRect = new Rect();
    102 
    103     private int mDisabledFeatures;
    104 
    105     private boolean mCameraDisabled;
    106 
    107     private boolean mSafeModeEnabled;
    108 
    109     private boolean mUserSetupCompleted;
    110 
    111     // User for whom this host view was created.  Final because we should never change the
    112     // id without reconstructing an instance of KeyguardHostView. See note below...
    113     private final int mUserId;
    114 
    115     private KeyguardMultiUserSelectorView mKeyguardMultiUserSelectorView;
    116 
    117     protected int mClientGeneration;
    118 
    119     protected boolean mShowSecurityWhenReturn;
    120 
    121     private final Rect mInsets = new Rect();
    122 
    123     private MyOnClickHandler mOnClickHandler = new MyOnClickHandler(this);
    124 
    125     private Runnable mPostBootCompletedRunnable;
    126 
    127     /*package*/ interface UserSwitcherCallback {
    128         void hideSecurityView(int duration);
    129         void showSecurityView();
    130         void showUnlockHint();
    131         void userActivity();
    132     }
    133 
    134     interface TransportControlCallback {
    135         void userActivity();
    136     }
    137 
    138     /*package*/ interface OnDismissAction {
    139         /* returns true if the dismiss should be deferred */
    140         boolean onDismiss();
    141     }
    142 
    143     public KeyguardHostView(Context context) {
    144         this(context, null);
    145     }
    146 
    147     public KeyguardHostView(Context context, AttributeSet attrs) {
    148         super(context, attrs);
    149 
    150         if (DEBUG) Log.e(TAG, "KeyguardHostView()");
    151 
    152         mLockPatternUtils = new LockPatternUtils(context);
    153 
    154         // Note: This depends on KeyguardHostView getting reconstructed every time the
    155         // user switches, since mUserId will be used for the entire session.
    156         // Once created, keyguard should *never* re-use this instance with another user.
    157         // In other words, mUserId should never change - hence it's marked final.
    158         mUserId = mLockPatternUtils.getCurrentUser();
    159 
    160         DevicePolicyManager dpm =
    161                 (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
    162         if (dpm != null) {
    163             mDisabledFeatures = getDisabledFeatures(dpm);
    164             mCameraDisabled = dpm.getCameraDisabled(null);
    165         }
    166 
    167         mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
    168 
    169         // These need to be created with the user context...
    170         Context userContext = null;
    171         try {
    172             final String packageName = "system";
    173             userContext = mContext.createPackageContextAsUser(packageName, 0,
    174                     new UserHandle(mUserId));
    175 
    176         } catch (NameNotFoundException e) {
    177             e.printStackTrace();
    178             // This should never happen, but it's better to have no widgets than to crash.
    179             userContext = context;
    180         }
    181 
    182         mAppWidgetHost = new AppWidgetHost(userContext, APPWIDGET_HOST_ID, mOnClickHandler,
    183                 Looper.myLooper());
    184 
    185         mAppWidgetManager = AppWidgetManager.getInstance(userContext);
    186 
    187         mSecurityModel = new KeyguardSecurityModel(context);
    188 
    189         mViewStateManager = new KeyguardViewStateManager(this);
    190 
    191         mUserSetupCompleted = Settings.Secure.getIntForUser(mContext.getContentResolver(),
    192                 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
    193 
    194         // Ensure we have the current state *before* we call showAppropriateWidgetPage()
    195         getInitialTransportState();
    196 
    197         if (mSafeModeEnabled) {
    198             Log.v(TAG, "Keyguard widgets disabled by safe mode");
    199         }
    200         if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
    201             Log.v(TAG, "Keyguard widgets disabled by DPM");
    202         }
    203         if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) {
    204             Log.v(TAG, "Keyguard secure camera disabled by DPM");
    205         }
    206     }
    207 
    208     public void announceCurrentSecurityMethod() {
    209         View v = (View) getSecurityView(mCurrentSecuritySelection);
    210         if (v != null) {
    211             v.announceForAccessibility(v.getContentDescription());
    212         }
    213     }
    214 
    215     private void getInitialTransportState() {
    216         DisplayClientState dcs = KeyguardUpdateMonitor.getInstance(mContext)
    217                 .getCachedDisplayClientState();
    218         mTransportState = (dcs.clearing ? TRANSPORT_GONE :
    219             (isMusicPlaying(dcs.playbackState) ? TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
    220 
    221         if (DEBUG) Log.v(TAG, "Initial transport state: "
    222                 + mTransportState + ", pbstate=" + dcs.playbackState);
    223     }
    224 
    225     private void cleanupAppWidgetIds() {
    226         if (mSafeModeEnabled || widgetsDisabled()) return;
    227 
    228         // Clean up appWidgetIds that are bound to lockscreen, but not actually used
    229         // This is only to clean up after another bug: we used to not call
    230         // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code
    231         // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks
    232         // that are triggered by deleteAppWidgetId, which is why we're doing this
    233         int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets();
    234         int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds();
    235         for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) {
    236             int appWidgetId = appWidgetIdsBoundToHost[i];
    237             if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) {
    238                 Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id "
    239                         + appWidgetId);
    240                 mAppWidgetHost.deleteAppWidgetId(appWidgetId);
    241             }
    242         }
    243     }
    244 
    245     private static boolean contains(int[] array, int target) {
    246         for (int value : array) {
    247             if (value == target) {
    248                 return true;
    249             }
    250         }
    251         return false;
    252     }
    253 
    254     private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
    255             new KeyguardUpdateMonitorCallback() {
    256         @Override
    257         public void onBootCompleted() {
    258             if (mPostBootCompletedRunnable != null) {
    259                 mPostBootCompletedRunnable.run();
    260                 mPostBootCompletedRunnable = null;
    261             }
    262         }
    263         @Override
    264         public void onUserSwitchComplete(int userId) {
    265             if (mKeyguardMultiUserSelectorView != null) {
    266                 mKeyguardMultiUserSelectorView.finalizeActiveUserView(true);
    267             }
    268         }
    269         @Override
    270         void onMusicClientIdChanged(
    271                 int clientGeneration, boolean clearing, android.app.PendingIntent intent) {
    272             // Set transport state to invisible until we know music is playing (below)
    273             if (DEBUGXPORT && (mClientGeneration != clientGeneration || clearing)) {
    274                 Log.v(TAG, (clearing ? "hide" : "show") + " transport, gen:" + clientGeneration);
    275             }
    276             mClientGeneration = clientGeneration;
    277             final int newState = (clearing ? TRANSPORT_GONE
    278                     : (mTransportState == TRANSPORT_VISIBLE ?
    279                     TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
    280             if (newState != mTransportState) {
    281                 mTransportState = newState;
    282                 if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
    283                 KeyguardHostView.this.post(mSwitchPageRunnable);
    284             }
    285         }
    286         @Override
    287         public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
    288             if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
    289             if (mTransportState != TRANSPORT_GONE) {
    290                 final int newState = (isMusicPlaying(playbackState) ?
    291                         TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
    292                 if (newState != mTransportState) {
    293                     mTransportState = newState;
    294                     if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
    295                     KeyguardHostView.this.post(mSwitchPageRunnable);
    296                 }
    297             }
    298         }
    299     };
    300 
    301     private static final boolean isMusicPlaying(int playbackState) {
    302         // This should agree with the list in AudioService.isPlaystateActive()
    303         switch (playbackState) {
    304             case RemoteControlClient.PLAYSTATE_PLAYING:
    305             case RemoteControlClient.PLAYSTATE_BUFFERING:
    306             case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
    307             case RemoteControlClient.PLAYSTATE_REWINDING:
    308             case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
    309             case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
    310                 return true;
    311             default:
    312                 return false;
    313         }
    314     }
    315 
    316     private SlidingChallengeLayout mSlidingChallengeLayout;
    317     private MultiPaneChallengeLayout mMultiPaneChallengeLayout;
    318 
    319     @Override
    320     public boolean onTouchEvent(MotionEvent ev) {
    321         boolean result = super.onTouchEvent(ev);
    322         mTempRect.set(0, 0, 0, 0);
    323         offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
    324         ev.offsetLocation(mTempRect.left, mTempRect.top);
    325         result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
    326         ev.offsetLocation(-mTempRect.left, -mTempRect.top);
    327         return result;
    328     }
    329 
    330     @Override
    331     protected void dispatchDraw(Canvas canvas) {
    332         super.dispatchDraw(canvas);
    333         if (mViewMediatorCallback != null) {
    334             mViewMediatorCallback.keyguardDoneDrawing();
    335         }
    336     }
    337 
    338     private int getWidgetPosition(int id) {
    339         final KeyguardWidgetPager appWidgetContainer = mAppWidgetContainer;
    340         final int children = appWidgetContainer.getChildCount();
    341         for (int i = 0; i < children; i++) {
    342             final View content = appWidgetContainer.getWidgetPageAt(i).getContent();
    343             if (content != null && content.getId() == id) {
    344                 return i;
    345             } else if (content == null) {
    346                 // Attempt to track down bug #8886916
    347                 Log.w(TAG, "*** Null content at " + "i=" + i + ",id=" + id + ",N=" + children);
    348             }
    349         }
    350         return -1;
    351     }
    352 
    353     @Override
    354     protected void onFinishInflate() {
    355         // Grab instances of and make any necessary changes to the main layouts. Create
    356         // view state manager and wire up necessary listeners / callbacks.
    357         View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target);
    358         mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
    359         mAppWidgetContainer.setVisibility(VISIBLE);
    360         mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
    361         mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget);
    362         mAppWidgetContainer.setMinScale(0.5f);
    363 
    364         mSlidingChallengeLayout = (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
    365         if (mSlidingChallengeLayout != null) {
    366             mSlidingChallengeLayout.setOnChallengeScrolledListener(mViewStateManager);
    367         }
    368         mAppWidgetContainer.setViewStateManager(mViewStateManager);
    369         mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
    370 
    371         mMultiPaneChallengeLayout =
    372                 (MultiPaneChallengeLayout) findViewById(R.id.multi_pane_challenge);
    373         ChallengeLayout challenge = mSlidingChallengeLayout != null ? mSlidingChallengeLayout :
    374                 mMultiPaneChallengeLayout;
    375         challenge.setOnBouncerStateChangedListener(mViewStateManager);
    376         mAppWidgetContainer.setBouncerAnimationDuration(challenge.getBouncerAnimationDuration());
    377         mViewStateManager.setPagedView(mAppWidgetContainer);
    378         mViewStateManager.setChallengeLayout(challenge);
    379         mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
    380         mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
    381         mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
    382 
    383         setBackButtonEnabled(false);
    384 
    385         if (KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
    386             updateAndAddWidgets();
    387         } else {
    388             // We can't add widgets until after boot completes because AppWidgetHost may try
    389             // to contact the providers.  Do it later.
    390             mPostBootCompletedRunnable = new Runnable() {
    391                 @Override
    392                 public void run() {
    393                     updateAndAddWidgets();
    394                 }
    395             };
    396         }
    397 
    398         showPrimarySecurityScreen(false);
    399         updateSecurityViews();
    400         enableUserSelectorIfNecessary();
    401     }
    402 
    403     private void updateAndAddWidgets() {
    404         cleanupAppWidgetIds();
    405         addDefaultWidgets();
    406         addWidgetsFromSettings();
    407         maybeEnableAddButton();
    408         checkAppWidgetConsistency();
    409 
    410         // Don't let the user drag the challenge down if widgets are disabled.
    411         if (mSlidingChallengeLayout != null) {
    412             mSlidingChallengeLayout.setEnableChallengeDragging(!widgetsDisabled());
    413         }
    414 
    415         // Select the appropriate page
    416         mSwitchPageRunnable.run();
    417 
    418         // This needs to be called after the pages are all added.
    419         mViewStateManager.showUsabilityHints();
    420     }
    421 
    422     private void maybeEnableAddButton() {
    423         if (!shouldEnableAddWidget()) {
    424             mAppWidgetContainer.setAddWidgetEnabled(false);
    425         }
    426     }
    427 
    428     private void setBackButtonEnabled(boolean enabled) {
    429         if (mContext instanceof Activity) return;  // always enabled in activity mode
    430         setSystemUiVisibility(enabled ?
    431                 getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_BACK :
    432                 getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
    433     }
    434 
    435     private boolean shouldEnableAddWidget() {
    436         return numWidgets() < MAX_WIDGETS && mUserSetupCompleted;
    437     }
    438 
    439     private int getDisabledFeatures(DevicePolicyManager dpm) {
    440         int disabledFeatures = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
    441         if (dpm != null) {
    442             final int currentUser = mLockPatternUtils.getCurrentUser();
    443             disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
    444         }
    445         return disabledFeatures;
    446     }
    447 
    448     private boolean widgetsDisabled() {
    449         boolean disabledByLowRamDevice = ActivityManager.isLowRamDeviceStatic();
    450         boolean disabledByDpm =
    451                 (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
    452         boolean disabledByUser = !mLockPatternUtils.getWidgetsEnabled();
    453         return disabledByLowRamDevice || disabledByDpm || disabledByUser;
    454     }
    455 
    456     private boolean cameraDisabledByDpm() {
    457         return mCameraDisabled
    458                 || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
    459     }
    460 
    461     private void updateSecurityViews() {
    462         int children = mSecurityViewContainer.getChildCount();
    463         for (int i = 0; i < children; i++) {
    464             updateSecurityView(mSecurityViewContainer.getChildAt(i));
    465         }
    466     }
    467 
    468     private void updateSecurityView(View view) {
    469         if (view instanceof KeyguardSecurityView) {
    470             KeyguardSecurityView ksv = (KeyguardSecurityView) view;
    471             ksv.setKeyguardCallback(mCallback);
    472             ksv.setLockPatternUtils(mLockPatternUtils);
    473             if (mViewStateManager.isBouncing()) {
    474                 ksv.showBouncer(0);
    475             } else {
    476                 ksv.hideBouncer(0);
    477             }
    478         } else {
    479             Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
    480         }
    481     }
    482 
    483     void setLockPatternUtils(LockPatternUtils utils) {
    484         mSecurityModel.setLockPatternUtils(utils);
    485         mLockPatternUtils = utils;
    486         updateSecurityViews();
    487     }
    488 
    489     @Override
    490     protected void onAttachedToWindow() {
    491         super.onAttachedToWindow();
    492         mAppWidgetHost.startListening();
    493         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
    494     }
    495 
    496     @Override
    497     protected void onDetachedFromWindow() {
    498         super.onDetachedFromWindow();
    499         mAppWidgetHost.stopListening();
    500         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
    501     }
    502 
    503     void addWidget(AppWidgetHostView view, int pageIndex) {
    504         mAppWidgetContainer.addWidget(view, pageIndex);
    505     }
    506 
    507     private KeyguardWidgetPager.Callbacks mWidgetCallbacks
    508             = new KeyguardWidgetPager.Callbacks() {
    509         @Override
    510         public void userActivity() {
    511             KeyguardHostView.this.userActivity();
    512         }
    513 
    514         @Override
    515         public void onUserActivityTimeoutChanged() {
    516             KeyguardHostView.this.onUserActivityTimeoutChanged();
    517         }
    518 
    519         @Override
    520         public void onAddView(View v) {
    521             if (!shouldEnableAddWidget()) {
    522                 mAppWidgetContainer.setAddWidgetEnabled(false);
    523             }
    524         }
    525 
    526         @Override
    527         public void onRemoveView(View v, boolean deletePermanently) {
    528             if (deletePermanently) {
    529                 final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
    530                 if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID &&
    531                         appWidgetId != LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
    532                     mAppWidgetHost.deleteAppWidgetId(appWidgetId);
    533                 }
    534             }
    535         }
    536 
    537         @Override
    538         public void onRemoveViewAnimationCompleted() {
    539             if (shouldEnableAddWidget()) {
    540                 mAppWidgetContainer.setAddWidgetEnabled(true);
    541             }
    542         }
    543     };
    544 
    545     public void initializeSwitchingUserState(boolean switching) {
    546         if (!switching && mKeyguardMultiUserSelectorView != null) {
    547             mKeyguardMultiUserSelectorView.finalizeActiveUserView(false);
    548         }
    549     }
    550 
    551     public void userActivity() {
    552         if (mViewMediatorCallback != null) {
    553             mViewMediatorCallback.userActivity();
    554         }
    555     }
    556 
    557     public void onUserActivityTimeoutChanged() {
    558         if (mViewMediatorCallback != null) {
    559             mViewMediatorCallback.onUserActivityTimeoutChanged();
    560         }
    561     }
    562 
    563     @Override
    564     public long getUserActivityTimeout() {
    565         // Currently only considering user activity timeouts needed by widgets.
    566         // Could also take into account longer timeouts for certain security views.
    567         if (mAppWidgetContainer != null) {
    568             return mAppWidgetContainer.getUserActivityTimeout();
    569         }
    570         return -1;
    571     }
    572 
    573     private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
    574 
    575         public void userActivity(long timeout) {
    576             if (mViewMediatorCallback != null) {
    577                 mViewMediatorCallback.userActivity(timeout);
    578             }
    579         }
    580 
    581         public void dismiss(boolean authenticated) {
    582             showNextSecurityScreenOrFinish(authenticated);
    583         }
    584 
    585         public boolean isVerifyUnlockOnly() {
    586             return mIsVerifyUnlockOnly;
    587         }
    588 
    589         public void reportSuccessfulUnlockAttempt() {
    590             KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
    591             mLockPatternUtils.reportSuccessfulPasswordAttempt();
    592         }
    593 
    594         public void reportFailedUnlockAttempt() {
    595             if (mCurrentSecuritySelection == SecurityMode.Biometric) {
    596                 KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt();
    597             } else {
    598                 KeyguardHostView.this.reportFailedUnlockAttempt();
    599             }
    600         }
    601 
    602         public int getFailedAttempts() {
    603             return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
    604         }
    605 
    606         @Override
    607         public void showBackupSecurity() {
    608             KeyguardHostView.this.showBackupSecurityScreen();
    609         }
    610 
    611         @Override
    612         public void setOnDismissAction(OnDismissAction action) {
    613             KeyguardHostView.this.setOnDismissAction(action);
    614         }
    615 
    616     };
    617 
    618     private void showDialog(String title, String message) {
    619         final AlertDialog dialog = new AlertDialog.Builder(mContext)
    620             .setTitle(title)
    621             .setMessage(message)
    622             .setNeutralButton(R.string.ok, null)
    623             .create();
    624         if (!(mContext instanceof Activity)) {
    625             dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
    626         }
    627         dialog.show();
    628     }
    629 
    630     private void showTimeoutDialog() {
    631         int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
    632         int messageId = 0;
    633 
    634         switch (mSecurityModel.getSecurityMode()) {
    635             case Pattern:
    636                 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
    637                 break;
    638             case PIN:
    639                 messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
    640                 break;
    641             case Password:
    642                 messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
    643                 break;
    644         }
    645 
    646         if (messageId != 0) {
    647             final String message = mContext.getString(messageId,
    648                     KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
    649                     timeoutInSeconds);
    650             showDialog(null, message);
    651         }
    652     }
    653 
    654     private void showAlmostAtWipeDialog(int attempts, int remaining) {
    655         String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
    656                 attempts, remaining);
    657         showDialog(null, message);
    658     }
    659 
    660     private void showWipeDialog(int attempts) {
    661         String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
    662         showDialog(null, message);
    663     }
    664 
    665     private void showAlmostAtAccountLoginDialog() {
    666         final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
    667         final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
    668                 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
    669         String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
    670                 count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
    671         showDialog(null, message);
    672     }
    673 
    674     private void reportFailedUnlockAttempt() {
    675         final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
    676         final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
    677 
    678         if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
    679 
    680         SecurityMode mode = mSecurityModel.getSecurityMode();
    681         final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
    682 
    683         final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
    684                 .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
    685 
    686         final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
    687                 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
    688 
    689         final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
    690                 (failedAttemptsBeforeWipe - failedAttempts)
    691                 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
    692 
    693         boolean showTimeout = false;
    694         if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
    695             // If we reach this code, it means the user has installed a DevicePolicyManager
    696             // that requests device wipe after N attempts.  Once we get below the grace
    697             // period, we'll post this dialog every time as a clear warning until the
    698             // bombshell hits and the device is wiped.
    699             if (remainingBeforeWipe > 0) {
    700                 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
    701             } else {
    702                 // Too many attempts. The device will be wiped shortly.
    703                 Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
    704                 showWipeDialog(failedAttempts);
    705             }
    706         } else {
    707             showTimeout =
    708                 (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
    709             if (usingPattern && mEnableFallback) {
    710                 if (failedAttempts == failedAttemptWarning) {
    711                     showAlmostAtAccountLoginDialog();
    712                     showTimeout = false; // don't show both dialogs
    713                 } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
    714                     mLockPatternUtils.setPermanentlyLocked(true);
    715                     showSecurityScreen(SecurityMode.Account);
    716                     // don't show timeout dialog because we show account unlock screen next
    717                     showTimeout = false;
    718                 }
    719             }
    720         }
    721         monitor.reportFailedUnlockAttempt();
    722         mLockPatternUtils.reportFailedPasswordAttempt();
    723         if (showTimeout) {
    724             showTimeoutDialog();
    725         }
    726     }
    727 
    728     /**
    729      * Shows the primary security screen for the user. This will be either the multi-selector
    730      * or the user's security method.
    731      * @param turningOff true if the device is being turned off
    732      */
    733     void showPrimarySecurityScreen(boolean turningOff) {
    734         SecurityMode securityMode = mSecurityModel.getSecurityMode();
    735         if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
    736         if (!turningOff &&
    737                 KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
    738             // If we're not turning off, then allow biometric alternate.
    739             // We'll reload it when the device comes back on.
    740             securityMode = mSecurityModel.getAlternateFor(securityMode);
    741         }
    742         showSecurityScreen(securityMode);
    743     }
    744 
    745     /**
    746      * Shows the backup security screen for the current security mode.  This could be used for
    747      * password recovery screens but is currently only used for pattern unlock to show the
    748      * account unlock screen and biometric unlock to show the user's normal unlock.
    749      */
    750     private void showBackupSecurityScreen() {
    751         if (DEBUG) Log.d(TAG, "showBackupSecurity()");
    752         SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
    753         showSecurityScreen(backup);
    754     }
    755 
    756     public boolean showNextSecurityScreenIfPresent() {
    757         SecurityMode securityMode = mSecurityModel.getSecurityMode();
    758         // Allow an alternate, such as biometric unlock
    759         securityMode = mSecurityModel.getAlternateFor(securityMode);
    760         if (SecurityMode.None == securityMode) {
    761             return false;
    762         } else {
    763             showSecurityScreen(securityMode); // switch to the alternate security view
    764             return true;
    765         }
    766     }
    767 
    768     private void showNextSecurityScreenOrFinish(boolean authenticated) {
    769         if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
    770         boolean finish = false;
    771         if (SecurityMode.None == mCurrentSecuritySelection) {
    772             SecurityMode securityMode = mSecurityModel.getSecurityMode();
    773             // Allow an alternate, such as biometric unlock
    774             securityMode = mSecurityModel.getAlternateFor(securityMode);
    775             if (SecurityMode.None == securityMode) {
    776                 finish = true; // no security required
    777             } else {
    778                 showSecurityScreen(securityMode); // switch to the alternate security view
    779             }
    780         } else if (authenticated) {
    781             switch (mCurrentSecuritySelection) {
    782                 case Pattern:
    783                 case Password:
    784                 case PIN:
    785                 case Account:
    786                 case Biometric:
    787                     finish = true;
    788                     break;
    789 
    790                 case SimPin:
    791                 case SimPuk:
    792                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
    793                     SecurityMode securityMode = mSecurityModel.getSecurityMode();
    794                     if (securityMode != SecurityMode.None) {
    795                         showSecurityScreen(securityMode);
    796                     } else {
    797                         finish = true;
    798                     }
    799                     break;
    800 
    801                 default:
    802                     Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
    803                     showPrimarySecurityScreen(false);
    804                     break;
    805             }
    806         } else {
    807             showPrimarySecurityScreen(false);
    808         }
    809         if (finish) {
    810             // If the alternate unlock was suppressed, it can now be safely
    811             // enabled because the user has left keyguard.
    812             KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
    813 
    814             // If there's a pending runnable because the user interacted with a widget
    815             // and we're leaving keyguard, then run it.
    816             boolean deferKeyguardDone = false;
    817             if (mDismissAction != null) {
    818                 deferKeyguardDone = mDismissAction.onDismiss();
    819                 mDismissAction = null;
    820             }
    821             if (mViewMediatorCallback != null) {
    822                 if (deferKeyguardDone) {
    823                     mViewMediatorCallback.keyguardDonePending();
    824                 } else {
    825                     mViewMediatorCallback.keyguardDone(true);
    826                 }
    827             }
    828         } else {
    829             mViewStateManager.showBouncer(true);
    830         }
    831     }
    832 
    833     private static class MyOnClickHandler extends OnClickHandler {
    834 
    835         // weak reference to the hostView to avoid keeping a live reference
    836         // due to Binder GC linkages to AppWidgetHost. By the same token,
    837         // this click handler should not keep references to any large
    838         // objects.
    839         WeakReference<KeyguardHostView> mThis;
    840 
    841         MyOnClickHandler(KeyguardHostView hostView) {
    842             mThis = new WeakReference<KeyguardHostView>(hostView);
    843         }
    844 
    845         @Override
    846         public boolean onClickHandler(final View view,
    847                 final android.app.PendingIntent pendingIntent,
    848                 final Intent fillInIntent) {
    849             KeyguardHostView hostView = mThis.get();
    850             if (hostView == null) {
    851                 return false;
    852             }
    853             if (pendingIntent.isActivity()) {
    854                 hostView.setOnDismissAction(new OnDismissAction() {
    855                     public boolean onDismiss() {
    856                         try {
    857                             // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
    858                             Context context = view.getContext();
    859                             ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
    860                                     0, 0,
    861                                     view.getMeasuredWidth(), view.getMeasuredHeight());
    862                             context.startIntentSender(
    863                                     pendingIntent.getIntentSender(), fillInIntent,
    864                                     Intent.FLAG_ACTIVITY_NEW_TASK,
    865                                     Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
    866                         } catch (IntentSender.SendIntentException e) {
    867                             android.util.Log.e(TAG, "Cannot send pending intent: ", e);
    868                         } catch (Exception e) {
    869                             android.util.Log.e(TAG, "Cannot send pending intent due to " +
    870                                     "unknown exception: ", e);
    871                         }
    872                         return false;
    873                     }
    874                 });
    875 
    876                 if (hostView.mViewStateManager.isChallengeShowing()) {
    877                     hostView.mViewStateManager.showBouncer(true);
    878                 } else {
    879                     hostView.mCallback.dismiss(false);
    880                 }
    881                 return true;
    882             } else {
    883                 return super.onClickHandler(view, pendingIntent, fillInIntent);
    884             }
    885         };
    886     };
    887 
    888     // Used to ignore callbacks from methods that are no longer current (e.g. face unlock).
    889     // This avoids unwanted asynchronous events from messing with the state.
    890     private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
    891 
    892         @Override
    893         public void userActivity(long timeout) {
    894         }
    895 
    896         @Override
    897         public void showBackupSecurity() {
    898         }
    899 
    900         @Override
    901         public void setOnDismissAction(OnDismissAction action) {
    902         }
    903 
    904         @Override
    905         public void reportSuccessfulUnlockAttempt() {
    906         }
    907 
    908         @Override
    909         public void reportFailedUnlockAttempt() {
    910         }
    911 
    912         @Override
    913         public boolean isVerifyUnlockOnly() {
    914             return false;
    915         }
    916 
    917         @Override
    918         public int getFailedAttempts() {
    919             return 0;
    920         }
    921 
    922         @Override
    923         public void dismiss(boolean securityVerified) {
    924         }
    925     };
    926 
    927     /**
    928      * Sets an action to perform when keyguard is dismissed.
    929      * @param action
    930      */
    931     protected void setOnDismissAction(OnDismissAction action) {
    932         mDismissAction = action;
    933     }
    934 
    935     private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
    936         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
    937         KeyguardSecurityView view = null;
    938         final int children = mSecurityViewContainer.getChildCount();
    939         for (int child = 0; child < children; child++) {
    940             if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
    941                 view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
    942                 break;
    943             }
    944         }
    945         int layoutId = getLayoutIdFor(securityMode);
    946         if (view == null && layoutId != 0) {
    947             final LayoutInflater inflater = LayoutInflater.from(mContext);
    948             if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
    949             View v = inflater.inflate(layoutId, mSecurityViewContainer, false);
    950             mSecurityViewContainer.addView(v);
    951             updateSecurityView(v);
    952             view = (KeyguardSecurityView)v;
    953         }
    954 
    955         if (view instanceof KeyguardSelectorView) {
    956             KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
    957             View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
    958             selectorView.setCarrierArea(carrierText);
    959         }
    960 
    961         return view;
    962     }
    963 
    964     /**
    965      * Switches to the given security view unless it's already being shown, in which case
    966      * this is a no-op.
    967      *
    968      * @param securityMode
    969      */
    970     private void showSecurityScreen(SecurityMode securityMode) {
    971         if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
    972 
    973         if (securityMode == mCurrentSecuritySelection) return;
    974 
    975         KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
    976         KeyguardSecurityView newView = getSecurityView(securityMode);
    977 
    978         // Enter full screen mode if we're in SIM or Account screen
    979         boolean fullScreenEnabled = getResources().getBoolean(R.bool.kg_sim_puk_account_full_screen);
    980         boolean isSimOrAccount = securityMode == SecurityMode.SimPin
    981                 || securityMode == SecurityMode.SimPuk
    982                 || securityMode == SecurityMode.Account;
    983         mAppWidgetContainer.setVisibility(
    984                 isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
    985 
    986         // Don't show camera or search in navbar when SIM or Account screen is showing
    987         setSystemUiVisibility(isSimOrAccount ?
    988                 (getSystemUiVisibility() | View.STATUS_BAR_DISABLE_SEARCH)
    989                 : (getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_SEARCH));
    990 
    991         if (mSlidingChallengeLayout != null) {
    992             mSlidingChallengeLayout.setChallengeInteractive(!fullScreenEnabled);
    993         }
    994 
    995         // Emulate Activity life cycle
    996         if (oldView != null) {
    997             oldView.onPause();
    998             oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
    999         }
   1000         newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
   1001         newView.setKeyguardCallback(mCallback);
   1002 
   1003         final boolean needsInput = newView.needsInput();
   1004         if (mViewMediatorCallback != null) {
   1005             mViewMediatorCallback.setNeedsInput(needsInput);
   1006         }
   1007 
   1008         // Find and show this child.
   1009         final int childCount = mSecurityViewContainer.getChildCount();
   1010 
   1011         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
   1012         for (int i = 0; i < childCount; i++) {
   1013             if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
   1014                 mSecurityViewContainer.setDisplayedChild(i);
   1015                 break;
   1016             }
   1017         }
   1018 
   1019         if (securityMode == SecurityMode.None) {
   1020             // Discard current runnable if we're switching back to the selector view
   1021             setOnDismissAction(null);
   1022         }
   1023         if (securityMode == SecurityMode.Account && !mLockPatternUtils.isPermanentlyLocked()) {
   1024             // we're showing account as a backup, provide a way to get back to primary
   1025             setBackButtonEnabled(true);
   1026         }
   1027         mCurrentSecuritySelection = securityMode;
   1028     }
   1029 
   1030     @Override
   1031     public void onScreenTurnedOn() {
   1032         if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
   1033         showPrimarySecurityScreen(false);
   1034         getSecurityView(mCurrentSecuritySelection).onResume(KeyguardSecurityView.SCREEN_ON);
   1035 
   1036         // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
   1037         // layout is blank but forcing a layout causes it to reappear (e.g. with with
   1038         // hierarchyviewer).
   1039         requestLayout();
   1040 
   1041         if (mViewStateManager != null) {
   1042             mViewStateManager.showUsabilityHints();
   1043         }
   1044 
   1045         requestFocus();
   1046     }
   1047 
   1048     @Override
   1049     public void onScreenTurnedOff() {
   1050         if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
   1051                 Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
   1052         // Once the screen turns off, we no longer consider this to be first boot and we want the
   1053         // biometric unlock to start next time keyguard is shown.
   1054         KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
   1055         // We use mAppWidgetToShow to show a particular widget after you add it-- once the screen
   1056         // turns off we reset that behavior
   1057         clearAppWidgetToShow();
   1058         if (KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
   1059             checkAppWidgetConsistency();
   1060         }
   1061         showPrimarySecurityScreen(true);
   1062         getSecurityView(mCurrentSecuritySelection).onPause();
   1063         CameraWidgetFrame cameraPage = findCameraPage();
   1064         if (cameraPage != null) {
   1065             cameraPage.onScreenTurnedOff();
   1066         }
   1067 
   1068         clearFocus();
   1069     }
   1070 
   1071     public void clearAppWidgetToShow() {
   1072         mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
   1073     }
   1074 
   1075     @Override
   1076     public void show() {
   1077         if (DEBUG) Log.d(TAG, "show()");
   1078         showPrimarySecurityScreen(false);
   1079     }
   1080 
   1081     @Override
   1082     public void verifyUnlock() {
   1083         SecurityMode securityMode = mSecurityModel.getSecurityMode();
   1084         if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
   1085             if (mViewMediatorCallback != null) {
   1086                 mViewMediatorCallback.keyguardDone(true);
   1087             }
   1088         } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
   1089                 && securityMode != KeyguardSecurityModel.SecurityMode.PIN
   1090                 && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
   1091             // can only verify unlock when in pattern/password mode
   1092             if (mViewMediatorCallback != null) {
   1093                 mViewMediatorCallback.keyguardDone(false);
   1094             }
   1095         } else {
   1096             // otherwise, go to the unlock screen, see if they can verify it
   1097             mIsVerifyUnlockOnly = true;
   1098             showSecurityScreen(securityMode);
   1099         }
   1100     }
   1101 
   1102     private int getSecurityViewIdForMode(SecurityMode securityMode) {
   1103         switch (securityMode) {
   1104             case None: return R.id.keyguard_selector_view;
   1105             case Pattern: return R.id.keyguard_pattern_view;
   1106             case PIN: return R.id.keyguard_pin_view;
   1107             case Password: return R.id.keyguard_password_view;
   1108             case Biometric: return R.id.keyguard_face_unlock_view;
   1109             case Account: return R.id.keyguard_account_view;
   1110             case SimPin: return R.id.keyguard_sim_pin_view;
   1111             case SimPuk: return R.id.keyguard_sim_puk_view;
   1112         }
   1113         return 0;
   1114     }
   1115 
   1116     private int getLayoutIdFor(SecurityMode securityMode) {
   1117         switch (securityMode) {
   1118             case None: return R.layout.keyguard_selector_view;
   1119             case Pattern: return R.layout.keyguard_pattern_view;
   1120             case PIN: return R.layout.keyguard_pin_view;
   1121             case Password: return R.layout.keyguard_password_view;
   1122             case Biometric: return R.layout.keyguard_face_unlock_view;
   1123             case Account: return R.layout.keyguard_account_view;
   1124             case SimPin: return R.layout.keyguard_sim_pin_view;
   1125             case SimPuk: return R.layout.keyguard_sim_puk_view;
   1126             default:
   1127                 return 0;
   1128         }
   1129     }
   1130 
   1131     private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) {
   1132         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId);
   1133         if (appWidgetInfo != null) {
   1134             AppWidgetHostView view = mAppWidgetHost.createView(mContext, appId, appWidgetInfo);
   1135             addWidget(view, pageIndex);
   1136             return true;
   1137         } else {
   1138             if (updateDbIfFailed) {
   1139                 Log.w(TAG, "*** AppWidgetInfo for app widget id " + appId + "  was null for user"
   1140                         + mUserId + ", deleting");
   1141                 mAppWidgetHost.deleteAppWidgetId(appId);
   1142                 mLockPatternUtils.removeAppWidget(appId);
   1143             }
   1144             return false;
   1145         }
   1146     }
   1147 
   1148     private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
   1149         new CameraWidgetFrame.Callbacks() {
   1150             @Override
   1151             public void onLaunchingCamera() {
   1152                 setSliderHandleAlpha(0);
   1153             }
   1154 
   1155             @Override
   1156             public void onCameraLaunchedSuccessfully() {
   1157                 if (mAppWidgetContainer.isCameraPage(mAppWidgetContainer.getCurrentPage())) {
   1158                     mAppWidgetContainer.scrollLeft();
   1159                 }
   1160                 setSliderHandleAlpha(1);
   1161                 mShowSecurityWhenReturn = true;
   1162             }
   1163 
   1164             @Override
   1165             public void onCameraLaunchedUnsuccessfully() {
   1166                 setSliderHandleAlpha(1);
   1167             }
   1168 
   1169             private void setSliderHandleAlpha(float alpha) {
   1170                 SlidingChallengeLayout slider =
   1171                         (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
   1172                 if (slider != null) {
   1173                     slider.setHandleAlpha(alpha);
   1174                 }
   1175             }
   1176         };
   1177 
   1178     private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
   1179         @Override
   1180         Context getContext() {
   1181             return mContext;
   1182         }
   1183 
   1184         @Override
   1185         KeyguardSecurityCallback getCallback() {
   1186             return mCallback;
   1187         }
   1188 
   1189         @Override
   1190         LockPatternUtils getLockPatternUtils() {
   1191             return mLockPatternUtils;
   1192         }
   1193     };
   1194 
   1195     private int numWidgets() {
   1196         final int childCount = mAppWidgetContainer.getChildCount();
   1197         int widgetCount = 0;
   1198         for (int i = 0; i < childCount; i++) {
   1199             if (mAppWidgetContainer.isWidgetPage(i)) {
   1200                 widgetCount++;
   1201             }
   1202         }
   1203         return widgetCount;
   1204     }
   1205 
   1206     private void addDefaultWidgets() {
   1207         if (!mSafeModeEnabled && !widgetsDisabled()) {
   1208             LayoutInflater inflater = LayoutInflater.from(mContext);
   1209             View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false);
   1210             mAppWidgetContainer.addWidget(addWidget, 0);
   1211             View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
   1212             addWidgetButton.setOnClickListener(new OnClickListener() {
   1213                 @Override
   1214                 public void onClick(View v) {
   1215                     // Pass in an invalid widget id... the picker will allocate an ID for us
   1216                     mActivityLauncher.launchWidgetPicker(AppWidgetManager.INVALID_APPWIDGET_ID);
   1217                 }
   1218             });
   1219         }
   1220 
   1221         // We currently disable cameras in safe mode because we support loading 3rd party
   1222         // cameras we can't trust.  TODO: plumb safe mode into camera creation code and only
   1223         // inflate system-provided camera?
   1224         if (!mSafeModeEnabled && !cameraDisabledByDpm() && mUserSetupCompleted
   1225                 && mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
   1226             View cameraWidget =
   1227                     CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
   1228             if (cameraWidget != null) {
   1229                 mAppWidgetContainer.addWidget(cameraWidget);
   1230             }
   1231         }
   1232     }
   1233 
   1234     /**
   1235      * Create KeyguardTransportControlView on demand.
   1236      * @return
   1237      */
   1238     private KeyguardTransportControlView getOrCreateTransportControl() {
   1239         if (mTransportControl == null) {
   1240             LayoutInflater inflater = LayoutInflater.from(mContext);
   1241             mTransportControl = (KeyguardTransportControlView)
   1242                     inflater.inflate(R.layout.keyguard_transport_control_view, this, false);
   1243             mTransportControl.setTransportControlCallback(new TransportControlCallback() {
   1244                 public void userActivity() {
   1245                     mViewMediatorCallback.userActivity();
   1246                 }
   1247             });
   1248         }
   1249         return mTransportControl;
   1250     }
   1251 
   1252     private int getInsertPageIndex() {
   1253         View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
   1254         int insertionIndex = mAppWidgetContainer.indexOfChild(addWidget);
   1255         if (insertionIndex < 0) {
   1256             insertionIndex = 0; // no add widget page found
   1257         } else {
   1258             insertionIndex++; // place after add widget
   1259         }
   1260         return insertionIndex;
   1261     }
   1262 
   1263     private void addDefaultStatusWidget(int index) {
   1264         LayoutInflater inflater = LayoutInflater.from(mContext);
   1265         View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
   1266         mAppWidgetContainer.addWidget(statusWidget, index);
   1267     }
   1268 
   1269     private void addWidgetsFromSettings() {
   1270         if (mSafeModeEnabled || widgetsDisabled()) {
   1271             addDefaultStatusWidget(0);
   1272             return;
   1273         }
   1274 
   1275         int insertionIndex = getInsertPageIndex();
   1276 
   1277         // Add user-selected widget
   1278         final int[] widgets = mLockPatternUtils.getAppWidgets();
   1279 
   1280         if (widgets == null) {
   1281             Log.d(TAG, "Problem reading widgets");
   1282         } else {
   1283             for (int i = widgets.length -1; i >= 0; i--) {
   1284                 if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
   1285                     addDefaultStatusWidget(insertionIndex);
   1286                 } else {
   1287                     // We add the widgets from left to right, starting after the first page after
   1288                     // the add page. We count down, since the order will be persisted from right
   1289                     // to left, starting after camera.
   1290                     addWidget(widgets[i], insertionIndex, true);
   1291                 }
   1292             }
   1293         }
   1294     }
   1295 
   1296     private int allocateIdForDefaultAppWidget() {
   1297         int appWidgetId;
   1298         Resources res = getContext().getResources();
   1299         ComponentName defaultAppWidget = new ComponentName(
   1300                 res.getString(R.string.widget_default_package_name),
   1301                 res.getString(R.string.widget_default_class_name));
   1302 
   1303         // Note: we don't support configuring the widget
   1304         appWidgetId = mAppWidgetHost.allocateAppWidgetId();
   1305 
   1306         try {
   1307             mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
   1308         } catch (IllegalArgumentException e) {
   1309             Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
   1310             mAppWidgetHost.deleteAppWidgetId(appWidgetId);
   1311             appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
   1312         }
   1313         return appWidgetId;
   1314     }
   1315 
   1316     public void checkAppWidgetConsistency() {
   1317         final int childCount = mAppWidgetContainer.getChildCount();
   1318         boolean widgetPageExists = false;
   1319         for (int i = 0; i < childCount; i++) {
   1320             if (mAppWidgetContainer.isWidgetPage(i)) {
   1321                 widgetPageExists = true;
   1322                 break;
   1323             }
   1324         }
   1325         if (!widgetPageExists) {
   1326             final int insertPageIndex = getInsertPageIndex();
   1327 
   1328             final boolean userAddedWidgetsEnabled = !widgetsDisabled();
   1329 
   1330             boolean addedDefaultAppWidget = false;
   1331 
   1332             if (!mSafeModeEnabled) {
   1333                 if (userAddedWidgetsEnabled) {
   1334                     int appWidgetId = allocateIdForDefaultAppWidget();
   1335                     if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
   1336                         addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, true);
   1337                     }
   1338                 } else {
   1339                     // note: even if widgetsDisabledByDpm() returns true, we still bind/create
   1340                     // the default appwidget if possible
   1341                     int appWidgetId = mLockPatternUtils.getFallbackAppWidgetId();
   1342                     if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
   1343                         appWidgetId = allocateIdForDefaultAppWidget();
   1344                         if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
   1345                             mLockPatternUtils.writeFallbackAppWidgetId(appWidgetId);
   1346                         }
   1347                     }
   1348                     if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
   1349                         addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, false);
   1350                         if (!addedDefaultAppWidget) {
   1351                             mAppWidgetHost.deleteAppWidgetId(appWidgetId);
   1352                             mLockPatternUtils.writeFallbackAppWidgetId(
   1353                                     AppWidgetManager.INVALID_APPWIDGET_ID);
   1354                         }
   1355                     }
   1356                 }
   1357             }
   1358 
   1359             // Use the built-in status/clock view if we can't inflate the default widget
   1360             if (!addedDefaultAppWidget) {
   1361                 addDefaultStatusWidget(insertPageIndex);
   1362             }
   1363 
   1364             // trigger DB updates only if user-added widgets are enabled
   1365             if (!mSafeModeEnabled && userAddedWidgetsEnabled) {
   1366                 mAppWidgetContainer.onAddView(
   1367                         mAppWidgetContainer.getChildAt(insertPageIndex), insertPageIndex);
   1368             }
   1369         }
   1370     }
   1371 
   1372     Runnable mSwitchPageRunnable = new Runnable() {
   1373         @Override
   1374         public void run() {
   1375            showAppropriateWidgetPage();
   1376         }
   1377     };
   1378 
   1379     static class SavedState extends BaseSavedState {
   1380         int transportState;
   1381         int appWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
   1382         Rect insets = new Rect();
   1383 
   1384         SavedState(Parcelable superState) {
   1385             super(superState);
   1386         }
   1387 
   1388         private SavedState(Parcel in) {
   1389             super(in);
   1390             this.transportState = in.readInt();
   1391             this.appWidgetToShow = in.readInt();
   1392             this.insets = in.readParcelable(null);
   1393         }
   1394 
   1395         @Override
   1396         public void writeToParcel(Parcel out, int flags) {
   1397             super.writeToParcel(out, flags);
   1398             out.writeInt(this.transportState);
   1399             out.writeInt(this.appWidgetToShow);
   1400             out.writeParcelable(insets, 0);
   1401         }
   1402 
   1403         public static final Parcelable.Creator<SavedState> CREATOR
   1404                 = new Parcelable.Creator<SavedState>() {
   1405             public SavedState createFromParcel(Parcel in) {
   1406                 return new SavedState(in);
   1407             }
   1408 
   1409             public SavedState[] newArray(int size) {
   1410                 return new SavedState[size];
   1411             }
   1412         };
   1413     }
   1414 
   1415     @Override
   1416     public Parcelable onSaveInstanceState() {
   1417         if (DEBUG) Log.d(TAG, "onSaveInstanceState, tstate=" + mTransportState);
   1418         Parcelable superState = super.onSaveInstanceState();
   1419         SavedState ss = new SavedState(superState);
   1420         // If the transport is showing, force it to show it on restore.
   1421         final boolean showing = mTransportControl != null
   1422                 && mAppWidgetContainer.getWidgetPageIndex(mTransportControl) >= 0;
   1423         ss.transportState =  showing ? TRANSPORT_VISIBLE : mTransportState;
   1424         ss.appWidgetToShow = mAppWidgetToShow;
   1425         ss.insets.set(mInsets);
   1426         return ss;
   1427     }
   1428 
   1429     @Override
   1430     public void onRestoreInstanceState(Parcelable state) {
   1431         if (!(state instanceof SavedState)) {
   1432             super.onRestoreInstanceState(state);
   1433             return;
   1434         }
   1435         SavedState ss = (SavedState) state;
   1436         super.onRestoreInstanceState(ss.getSuperState());
   1437         mTransportState = (ss.transportState);
   1438         mAppWidgetToShow = ss.appWidgetToShow;
   1439         setInsets(ss.insets);
   1440         if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
   1441         post(mSwitchPageRunnable);
   1442     }
   1443 
   1444     @Override
   1445     protected boolean fitSystemWindows(Rect insets) {
   1446         setInsets(insets);
   1447         return true;
   1448     }
   1449 
   1450     private void setInsets(Rect insets) {
   1451         mInsets.set(insets);
   1452         if (mSlidingChallengeLayout != null) mSlidingChallengeLayout.setInsets(mInsets);
   1453         if (mMultiPaneChallengeLayout != null) mMultiPaneChallengeLayout.setInsets(mInsets);
   1454 
   1455         final CameraWidgetFrame cameraWidget = findCameraPage();
   1456         if (cameraWidget != null) cameraWidget.setInsets(mInsets);
   1457     }
   1458 
   1459     @Override
   1460     public void onWindowFocusChanged(boolean hasWindowFocus) {
   1461         super.onWindowFocusChanged(hasWindowFocus);
   1462         if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
   1463         if (hasWindowFocus && mShowSecurityWhenReturn) {
   1464             SlidingChallengeLayout slider =
   1465                 (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
   1466             if (slider != null) {
   1467                 slider.setHandleAlpha(1);
   1468                 slider.showChallenge(true);
   1469             }
   1470             mShowSecurityWhenReturn = false;
   1471         }
   1472     }
   1473 
   1474     private void showAppropriateWidgetPage() {
   1475         int state = mTransportState;
   1476         ensureTransportPresentOrRemoved(state);
   1477         int pageToShow = getAppropriateWidgetPage(state);
   1478         mAppWidgetContainer.setCurrentPage(pageToShow);
   1479     }
   1480 
   1481     /**
   1482      * Examines the current state and adds the transport to the widget pager when the state changes.
   1483      *
   1484      * Showing the initial transport and keeping it around is a bit tricky because the signals
   1485      * coming from music players aren't always clear. Here's how the states are handled:
   1486      *
   1487      * {@link TRANSPORT_GONE} means we have no reason to show the transport - remove it if present.
   1488      *
   1489      * {@link TRANSPORT_INVISIBLE} means we have potential to show the transport because a music
   1490      * player is registered but not currently playing music (or we don't know the state yet). The
   1491      * code adds it conditionally on play state.
   1492      *
   1493      * {@link #TRANSPORT_VISIBLE} means a music player is active and transport should be showing.
   1494      *
   1495      * Once the transport is showing, we always show it until keyguard is dismissed. This state is
   1496      * maintained by onSave/RestoreInstanceState(). This state is cleared in
   1497      * {@link KeyguardViewManager#hide} when keyguard is dismissed, which causes the transport to be
   1498      * gone when keyguard is restarted until we get an update with the current state.
   1499      *
   1500      * @param state
   1501      */
   1502     private void ensureTransportPresentOrRemoved(int state) {
   1503         final boolean showing = getWidgetPosition(R.id.keyguard_transport_control) != -1;
   1504         final boolean visible = state == TRANSPORT_VISIBLE;
   1505         final boolean shouldBeVisible = state == TRANSPORT_INVISIBLE && isMusicPlaying(state);
   1506         if (!showing && (visible || shouldBeVisible)) {
   1507             if (DEBUGXPORT) Log.v(TAG, "add transport");
   1508             // insert to left of camera if it exists, otherwise after right-most widget
   1509             int lastWidget = mAppWidgetContainer.getChildCount() - 1;
   1510             int position = 0; // handle no widget case
   1511             if (lastWidget >= 0) {
   1512                 position = mAppWidgetContainer.isCameraPage(lastWidget) ?
   1513                         lastWidget : lastWidget + 1;
   1514             }
   1515             mAppWidgetContainer.addWidget(getOrCreateTransportControl(), position);
   1516         } else if (showing && state == TRANSPORT_GONE) {
   1517             if (DEBUGXPORT) Log.v(TAG, "remove transport");
   1518             mAppWidgetContainer.removeWidget(getOrCreateTransportControl());
   1519             mTransportControl = null;
   1520             KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(null);
   1521         }
   1522     }
   1523 
   1524     private CameraWidgetFrame findCameraPage() {
   1525         for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) {
   1526             if (mAppWidgetContainer.isCameraPage(i)) {
   1527                 return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i);
   1528             }
   1529         }
   1530         return null;
   1531     }
   1532 
   1533     boolean isMusicPage(int pageIndex) {
   1534         return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
   1535     }
   1536 
   1537     private int getAppropriateWidgetPage(int musicTransportState) {
   1538         // assumes at least one widget (besides camera + add)
   1539         if (mAppWidgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
   1540             final int childCount = mAppWidgetContainer.getChildCount();
   1541             for (int i = 0; i < childCount; i++) {
   1542                 if (mAppWidgetContainer.getWidgetPageAt(i).getContentAppWidgetId()
   1543                         == mAppWidgetToShow) {
   1544                     return i;
   1545                 }
   1546             }
   1547             mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
   1548         }
   1549         // if music playing, show transport
   1550         if (musicTransportState == TRANSPORT_VISIBLE) {
   1551             if (DEBUG) Log.d(TAG, "Music playing, show transport");
   1552             return mAppWidgetContainer.getWidgetPageIndex(getOrCreateTransportControl());
   1553         }
   1554 
   1555         // else show the right-most widget (except for camera)
   1556         int rightMost = mAppWidgetContainer.getChildCount() - 1;
   1557         if (mAppWidgetContainer.isCameraPage(rightMost)) {
   1558             rightMost--;
   1559         }
   1560         if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
   1561         return rightMost;
   1562     }
   1563 
   1564     private void enableUserSelectorIfNecessary() {
   1565         if (!UserManager.supportsMultipleUsers()) {
   1566             return; // device doesn't support multi-user mode
   1567         }
   1568         final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
   1569         if (um == null) {
   1570             Throwable t = new Throwable();
   1571             t.fillInStackTrace();
   1572             Log.e(TAG, "user service is null.", t);
   1573             return;
   1574         }
   1575 
   1576         // if there are multiple users, we need to enable to multi-user switcher
   1577         final List<UserInfo> users = um.getUsers(true);
   1578         if (users == null) {
   1579             Throwable t = new Throwable();
   1580             t.fillInStackTrace();
   1581             Log.e(TAG, "list of users is null.", t);
   1582             return;
   1583         }
   1584 
   1585         final View multiUserView = findViewById(R.id.keyguard_user_selector);
   1586         if (multiUserView == null) {
   1587             Throwable t = new Throwable();
   1588             t.fillInStackTrace();
   1589             Log.e(TAG, "can't find user_selector in layout.", t);
   1590             return;
   1591         }
   1592 
   1593         if (users.size() > 1) {
   1594             if (multiUserView instanceof KeyguardMultiUserSelectorView) {
   1595                 mKeyguardMultiUserSelectorView = (KeyguardMultiUserSelectorView) multiUserView;
   1596                 mKeyguardMultiUserSelectorView.setVisibility(View.VISIBLE);
   1597                 mKeyguardMultiUserSelectorView.addUsers(users);
   1598                 UserSwitcherCallback callback = new UserSwitcherCallback() {
   1599                     @Override
   1600                     public void hideSecurityView(int duration) {
   1601                         mSecurityViewContainer.animate().alpha(0).setDuration(duration);
   1602                     }
   1603 
   1604                     @Override
   1605                     public void showSecurityView() {
   1606                         mSecurityViewContainer.setAlpha(1.0f);
   1607                     }
   1608 
   1609                     @Override
   1610                     public void showUnlockHint() {
   1611                         if (mKeyguardSelectorView != null) {
   1612                             mKeyguardSelectorView.showUsabilityHint();
   1613                         }
   1614                     }
   1615 
   1616                     @Override
   1617                     public void userActivity() {
   1618                         if (mViewMediatorCallback != null) {
   1619                             mViewMediatorCallback.userActivity();
   1620                         }
   1621                     }
   1622                 };
   1623                 mKeyguardMultiUserSelectorView.setCallback(callback);
   1624             } else {
   1625                 Throwable t = new Throwable();
   1626                 t.fillInStackTrace();
   1627                 if (multiUserView == null) {
   1628                     Log.e(TAG, "could not find the user_selector.", t);
   1629                 } else {
   1630                     Log.e(TAG, "user_selector is the wrong type.", t);
   1631                 }
   1632             }
   1633         }
   1634     }
   1635 
   1636     @Override
   1637     public void cleanUp() {
   1638         // Make sure we let go of all widgets and their package contexts promptly. If we don't do
   1639         // this, and the associated application is uninstalled, it can cause a soft reboot.
   1640         int count = mAppWidgetContainer.getChildCount();
   1641         for (int i = 0; i < count; i++) {
   1642             KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i);
   1643             frame.removeAllViews();
   1644         }
   1645     }
   1646 
   1647     /**
   1648      * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
   1649      * some cases where we wish to disable it, notably when the menu button placement or technology
   1650      * is prone to false positives.
   1651      *
   1652      * @return true if the menu key should be enabled
   1653      */
   1654     private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
   1655     private boolean shouldEnableMenuKey() {
   1656         final Resources res = getResources();
   1657         final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
   1658         final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
   1659         final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
   1660         return !configDisabled || isTestHarness || fileOverride;
   1661     }
   1662 
   1663     public void goToWidget(int appWidgetId) {
   1664         mAppWidgetToShow = appWidgetId;
   1665         mSwitchPageRunnable.run();
   1666     }
   1667 
   1668     public boolean handleMenuKey() {
   1669         // The following enables the MENU key to work for testing automation
   1670         if (shouldEnableMenuKey()) {
   1671             showNextSecurityScreenOrFinish(false);
   1672             return true;
   1673         }
   1674         return false;
   1675     }
   1676 
   1677     public boolean handleBackKey() {
   1678         if (mCurrentSecuritySelection == SecurityMode.Account) {
   1679             // go back to primary screen and re-disable back
   1680             setBackButtonEnabled(false);
   1681             showPrimarySecurityScreen(false /*turningOff*/);
   1682             return true;
   1683         }
   1684         if (mCurrentSecuritySelection != SecurityMode.None) {
   1685             mCallback.dismiss(false);
   1686             return true;
   1687         }
   1688         return false;
   1689     }
   1690 
   1691     /**
   1692      *  Dismisses the keyguard by going to the next screen or making it gone.
   1693      */
   1694     public void dismiss() {
   1695         showNextSecurityScreenOrFinish(false);
   1696     }
   1697 
   1698     public void showAssistant() {
   1699         final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
   1700           .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
   1701 
   1702         if (intent == null) return;
   1703 
   1704         final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
   1705                 R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit,
   1706                 getHandler(), null);
   1707 
   1708         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1709 
   1710         mActivityLauncher.launchActivityWithAnimation(
   1711                 intent, false, opts.toBundle(), null, null);
   1712     }
   1713 
   1714     public void dispatch(MotionEvent event) {
   1715         mAppWidgetContainer.handleExternalCameraEvent(event);
   1716     }
   1717 
   1718     public void launchCamera() {
   1719         mActivityLauncher.launchCamera(getHandler(), null);
   1720     }
   1721 
   1722 }
   1723