Merge "Updated assets." into jb-mr1-lockscreen-dev
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 6624eb8..a300776 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -3495,25 +3495,29 @@
         }
 
         /**
-         * Returns true if video snapshot is supported. That is, applications
+         * <p>Returns true if video snapshot is supported. That is, applications
          * can call {@link #takePicture(Camera.ShutterCallback,
-         * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}
-         * during recording. Applications do not need to call {@link
-         * #startPreview()} after taking a picture. The preview will be still
-         * active. Other than that, taking a picture during recording is
-         * identical to taking a picture normally. All settings and methods
-         * related to takePicture work identically. Ex: {@link
-         * #getPictureSize()}, {@link #getSupportedPictureSizes()}, {@link
-         * #setJpegQuality(int)}, {@link #setRotation(int)}, and etc. The
-         * picture will have an EXIF header. {@link #FLASH_MODE_AUTO} and {@link
-         * #FLASH_MODE_ON} also still work, but the video will record the flash.
+         * Camera.PictureCallback, Camera.PictureCallback,
+         * Camera.PictureCallback)} during recording. Applications do not need
+         * to call {@link #startPreview()} after taking a picture. The preview
+         * will be still active. Other than that, taking a picture during
+         * recording is identical to taking a picture normally. All settings and
+         * methods related to takePicture work identically. Ex:
+         * {@link #getPictureSize()}, {@link #getSupportedPictureSizes()},
+         * {@link #setJpegQuality(int)}, {@link #setRotation(int)}, and etc. The
+         * picture will have an EXIF header. {@link #FLASH_MODE_AUTO} and
+         * {@link #FLASH_MODE_ON} also still work, but the video will record the
+         * flash.</p>
          *
-         * Applications can set shutter callback as null to avoid the shutter
+         * <p>Applications can set shutter callback as null to avoid the shutter
          * sound. It is also recommended to set raw picture and post view
-         * callbacks to null to avoid the interrupt of preview display.
+         * callbacks to null to avoid the interrupt of preview display.</p>
          *
-         * Field-of-view of the recorded video may be different from that of the
-         * captured pictures.
+         * <p>Field-of-view of the recorded video may be different from that of the
+         * captured pictures. The maximum size of a video snapshot may be
+         * smaller than that for regular still captures. If the current picture
+         * size is set higher than can be supported by video snapshot, the
+         * picture will be captured at the maximum supported size instead.</p>
          *
          * @return true if video snapshot is supported.
          */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f482335..b94f0b9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3229,6 +3229,13 @@
             "lock_screen_appwidget_ids";
 
         /**
+         * Id of the appwidget shown on the lock screen when appwidgets are disabled.
+         * @hide
+         */
+        public static final String LOCK_SCREEN_FALLBACK_APPWIDGET_ID =
+            "lock_screen_fallback_appwidget_id";
+
+        /**
          * Index of the lockscreen appwidget to restore, -1 if none.
          * @hide
          */
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5397eb6..b1a44c5 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -289,13 +289,7 @@
     public void setError(CharSequence error, Drawable icon) {
         mError = TextUtils.stringOrSpannedString(error);
         mErrorWasChanged = true;
-        final Drawables dr = mTextView.mDrawables;
-        if (dr != null) {
-            mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
-                    dr.mDrawableBottom);
-        } else {
-            mTextView.setCompoundDrawables(null, null, icon, null);
-        }
+
         if (mError == null) {
             if (mErrorPopup != null) {
                 if (mErrorPopup.isShowing()) {
@@ -304,10 +298,21 @@
 
                 mErrorPopup = null;
             }
+
+            setErrorIcon(null);
+        } else if (mTextView.isFocused()) {
+            showError();
+            setErrorIcon(icon);
+        }
+    }
+
+    private void setErrorIcon(Drawable icon) {
+        final Drawables dr = mTextView.mDrawables;
+        if (dr != null) {
+            mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
+                    dr.mDrawableBottom);
         } else {
-            if (mTextView.isFocused()) {
-                showError();
-            }
+            mTextView.setCompoundDrawables(null, null, icon, null);
         }
     }
 
@@ -316,6 +321,8 @@
             if (mErrorPopup.isShowing()) {
                 mErrorPopup.dismiss();
             }
+
+            setErrorIcon(null);
         }
 
         mShowErrorAfterAttach = false;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e5e1a2b..26eaafb 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManagerNative;
 import android.app.admin.DevicePolicyManager;
+import android.appwidget.AppWidgetManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -35,6 +36,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.IWindowManager;
 import android.view.View;
 import android.widget.Button;
 
@@ -109,6 +111,11 @@
     public static final String KEYGUARD_SHOW_SECURITY_CHALLENGE = "showsecuritychallenge";
 
     /**
+     * Tells the keyguard to show the widget with the specified id when the keyguard is created.
+     */
+    public static final String KEYGUARD_SHOW_APPWIDGET = "showappwidget";
+
+    /**
      * Options used to lock the device upon user switch.
      */
     public static final Bundle USER_SWITCH_LOCK_OPTIONS = new Bundle();
@@ -1112,6 +1119,25 @@
         return sb.toString();
     }
 
+    // appwidget used when appwidgets are disabled (we make an exception for
+    // default clock widget)
+    public void writeFallbackAppWidgetId(int appWidgetId) {
+        Settings.Secure.putIntForUser(mContentResolver,
+                Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID,
+                appWidgetId,
+                UserHandle.USER_CURRENT);
+    }
+
+    // appwidget used when appwidgets are disabled (we make an exception for
+    // default clock widget)
+    public int getFallbackAppWidgetId() {
+        return Settings.Secure.getIntForUser(
+                mContentResolver,
+                Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID,
+                AppWidgetManager.INVALID_APPWIDGET_ID,
+                UserHandle.USER_CURRENT);
+    }
+
     private void writeAppWidgets(int[] appWidgetIds) {
         Settings.Secure.putStringForUser(mContentResolver,
                         Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS,
@@ -1162,21 +1188,6 @@
         return true;
     }
 
-    public int getStickyAppWidgetIndex() {
-        return Settings.Secure.getIntForUser(
-                mContentResolver,
-                Settings.Secure.LOCK_SCREEN_STICKY_APPWIDGET,
-                -1,
-                UserHandle.USER_CURRENT);
-    }
-
-    public void setStickyAppWidgetIndex(int value) {
-        Settings.Secure.putIntForUser(mContentResolver,
-                Settings.Secure.LOCK_SCREEN_STICKY_APPWIDGET,
-                value,
-                UserHandle.USER_CURRENT);
-    }
-
     private long getLong(String secureSettingKey, long defaultValue) {
         try {
             return getLockSettings().getLong(secureSettingKey, defaultValue,
@@ -1325,5 +1336,15 @@
     public boolean getPowerButtonInstantlyLocks() {
         return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true);
     }
+    
+    public static boolean isSafeModeEnabled() {
+        try {
+            return IWindowManager.Stub.asInterface(
+                    ServiceManager.getService("window")).isSafeModeEnabled();
+        } catch (RemoteException e) {
+            // Shouldn't happen!
+        }
+        return false;
+    }
 
 }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 406f644..5397a29 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3168,8 +3168,7 @@
 
         if (lidOpen) {
             if (keyguardIsShowingTq()) {
-                mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(
-                        KeyEvent.KEYCODE_POWER, mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(KeyEvent.KEYCODE_POWER);
             } else {
                 mPowerManager.wakeUp(SystemClock.uptimeMillis());
             }
@@ -3388,11 +3387,10 @@
             // When the screen is off and the key is not injected, determine whether
             // to wake the device but don't pass the key to the application.
             result = 0;
-            if (down && isWakeKey) {
+            if (down && isWakeKey && isWakeKeyWhenScreenOff(keyCode)) {
                 if (keyguardActive) {
-                    // If the keyguard is showing, let it decide what to do with the wake key.
-                    mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode,
-                            mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED);
+                    // If the keyguard is showing, let it wake the device when ready.
+                    mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode);
                 } else {
                     // Otherwise, wake the device ourselves.
                     result |= ACTION_WAKE_UP;
@@ -3614,6 +3612,40 @@
         return result;
     }
 
+    /**
+     * When the screen is off we ignore some keys that might otherwise typically
+     * be considered wake keys.  We filter them out here.
+     *
+     * {@link KeyEvent#KEYCODE_POWER} is notably absent from this list because it
+     * is always considered a wake key.
+     */
+    private boolean isWakeKeyWhenScreenOff(int keyCode) {
+        switch (keyCode) {
+            // ignore volume keys unless docked
+            case KeyEvent.KEYCODE_VOLUME_UP:
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_MUTE:
+                return mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+            // ignore media and camera keys
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+            case KeyEvent.KEYCODE_CAMERA:
+                return false;
+        }
+        return true;
+    }
+
+
     /** {@inheritDoc} */
     @Override
     public int interceptMotionBeforeQueueingWhenScreenOff(int policyFlags) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java b/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java
index f3ea992..a38e86d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/CarrierText.java
@@ -85,10 +85,21 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mSeparator = getResources().getString(R.string.kg_text_message_separator);
-        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mCallback);
         setSelected(true); // Allow marquee to work.
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mCallback);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mCallback);
+    }
+
     /**
      * Top-level function for creating carrier text. Makes text based on simState, PLMN
      * and SPN as well as device capabilities, such as being emergency call capable.
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
index cab4ed9..cd7324c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyButton.java
@@ -64,6 +64,18 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback);
+    }
+
+    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mLockPatternUtils = new LockPatternUtils(mContext);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 9fe3144..1ee0e86 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -57,7 +57,7 @@
 import java.util.List;
 
 public class KeyguardHostView extends KeyguardViewBase {
-    private static final String TAG = "KeyguardViewHost";
+    private static final String TAG = "KeyguardHostView";
 
     // Use this to debug all of keyguard
     public static boolean DEBUG = KeyguardViewMediator.DEBUG;
@@ -73,6 +73,10 @@
     private boolean mIsVerifyUnlockOnly;
     private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
     private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
+    private int mAppWidgetToShow;
+
+    private boolean mBootCompleted = false;
+    private boolean mCheckAppWidgetConsistencyOnBootCompleted = false;
 
     protected Runnable mLaunchRunnable;
 
@@ -82,11 +86,16 @@
     private KeyguardSecurityModel mSecurityModel;
     private KeyguardViewStateManager mViewStateManager;
 
-    int mLocalStickyWidget = -1;
     boolean mPersitentStickyWidgetLoaded = false;
 
     private Rect mTempRect = new Rect();
 
+    private int mDisabledFeatures;
+
+    private boolean mCameraDisabled;
+
+    private boolean mSafeModeEnabled;
+
     /*package*/ interface TransportCallback {
         void onListenerDetached();
         void onListenerAttached();
@@ -113,8 +122,40 @@
         mSecurityModel = new KeyguardSecurityModel(context);
 
         mViewStateManager = new KeyguardViewStateManager(this);
+
+        DevicePolicyManager dpm =
+            (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        if (dpm != null) {
+            mDisabledFeatures = getDisabledFeatures(dpm);
+            mCameraDisabled = dpm.getCameraDisabled(null);
+        }
+
+        mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
+
+        if (mSafeModeEnabled) {
+            Log.v(TAG, "Keyguard widgets disabled by safe mode");
+        }
+        if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
+            Log.v(TAG, "Keyguard widgets disabled by DPM");
+        }
+        if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) {
+            Log.v(TAG, "Keyguard secure camera disabled by DPM");
+        }
     }
 
+    private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
+            new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onBootCompleted() {
+            mBootCompleted = true;
+            if (mCheckAppWidgetConsistencyOnBootCompleted) {
+                checkAppWidgetConsistency();
+                mSwitchPageRunnable.run();
+                mCheckAppWidgetConsistencyOnBootCompleted = false;
+            }
+        }
+    };
+
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         boolean result = super.onTouchEvent(ev);
@@ -177,9 +218,10 @@
         }
 
         addDefaultWidgets();
-        addWidgetsFromSettings();
-        mSwitchPageRunnable.run();
 
+        addWidgetsFromSettings();
+        checkAppWidgetConsistency();
+        mSwitchPageRunnable.run();
         // This needs to be called after the pages are all added.
         mViewStateManager.showUsabilityHints();
 
@@ -187,6 +229,24 @@
         updateSecurityViews();
     }
 
+    private int getDisabledFeatures(DevicePolicyManager dpm) {
+        int disabledFeatures = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
+        if (dpm != null) {
+            final int currentUser = mLockPatternUtils.getCurrentUser();
+            disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
+        }
+        return disabledFeatures;
+    }
+
+    private boolean widgetsDisabledByDpm() {
+        return (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
+    }
+
+    private boolean cameraDisabledByDpm() {
+        return mCameraDisabled
+                || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
+    }
+
     private void updateSecurityViews() {
         int children = mSecurityViewContainer.getChildCount();
         for (int i = 0; i < children; i++) {
@@ -219,12 +279,14 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mAppWidgetHost.startListening();
+        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         mAppWidgetHost.stopListening();
+        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
     }
 
     private AppWidgetHost getAppWidgetHost() {
@@ -722,7 +784,6 @@
         // Once the screen turns off, we no longer consider this to be first boot and we want the
         // biometric unlock to start next time keyguard is shown.
         KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
-        saveStickyWidgetIndex();
         checkAppWidgetConsistency();
         showPrimarySecurityScreen(true);
         getSecurityView(mCurrentSecuritySelection).onPause();
@@ -821,15 +882,18 @@
         }
     }
 
-    private boolean addWidget(int appId, int pageIndex) {
+    private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) {
         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId);
         if (appWidgetInfo != null) {
             AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
             addWidget(view, pageIndex);
             return true;
         } else {
-            Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting");
-            mLockPatternUtils.removeAppWidget(appId);
+            if (updateDbIfFailed) {
+                Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting");
+                mAppWidgetHost.deleteAppWidgetId(appId);
+                mLockPatternUtils.removeAppWidget(appId);
+            }
             return false;
         }
     }
@@ -885,9 +949,28 @@
         LayoutInflater inflater = LayoutInflater.from(mContext);
         inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
 
-        View addWidget = inflater.inflate(R.layout.keyguard_add_widget, null, true);
-        mAppWidgetContainer.addWidget(addWidget);
-        if (mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
+        if (!mSafeModeEnabled && !widgetsDisabledByDpm()) {
+            View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false);
+            mAppWidgetContainer.addWidget(addWidget);
+            View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
+            addWidgetButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+                    if (appWidgetId != -1) {
+                        mActivityLauncher.launchWidgetPicker(appWidgetId);
+                    } else {
+                        Log.e(TAG, "Unable to allocate an AppWidget id in lock screen");
+                    }
+                }
+            });
+        }
+
+        // We currently disable cameras in safe mode because we support loading 3rd party
+        // cameras we can't trust.  TODO: plumb safe mode into camera creation code and only
+        // inflate system-provided camera?
+        if (!mSafeModeEnabled && !cameraDisabledByDpm()
+                && mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
             View cameraWidget =
                     CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
             if (cameraWidget != null) {
@@ -895,24 +978,11 @@
             }
         }
 
-        View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
-        addWidgetButton.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
-                if (appWidgetId != -1) {
-                    mActivityLauncher.launchWidgetPicker(appWidgetId);
-                } else {
-                    Log.e(TAG, "Unable to allocate an AppWidget id in lock screen");
-                }
-            }
-        });
-
         enableUserSelectorIfNecessary();
         initializeTransportControl();
     }
 
-    private void removeTransportFromWidgetPager() {
+    private boolean removeTransportFromWidgetPager() {
         int page = getWidgetPosition(R.id.keyguard_transport_control);
         if (page != -1) {
             mAppWidgetContainer.removeWidget(mTransportControl);
@@ -921,8 +991,9 @@
             KeyguardHostView.this.addView(mTransportControl);
             mTransportControl.setVisibility(View.GONE);
             mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_GONE);
-            mTransportControl.post(mSwitchPageRunnable);
+            return true;
         }
+        return false;
     }
 
     private void addTransportToWidgetPager() {
@@ -951,8 +1022,9 @@
             mTransportControl.setKeyguardCallback(new TransportCallback() {
                 @Override
                 public void onListenerDetached() {
-                    removeTransportFromWidgetPager();
-                    mTransportControl.post(mSwitchPageRunnable);
+                    if (removeTransportFromWidgetPager()) {
+                        mTransportControl.post(mSwitchPageRunnable);
+                    }
                 }
 
                 @Override
@@ -969,14 +1041,15 @@
         }
     }
 
-    private int getAddPageIndex() {
+    private int getInsertPageIndex() {
         View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
-        int addPageIndex = mAppWidgetContainer.indexOfChild(addWidget);
-        // This shouldn't happen, but just to be safe!
-        if (addPageIndex < 0) {
-            addPageIndex = 0;
+        int insertionIndex = mAppWidgetContainer.indexOfChild(addWidget);
+        if (insertionIndex < 0) {
+            insertionIndex = 0; // no add widget page found
+        } else {
+            insertionIndex++; // place after add widget
         }
-        return addPageIndex;
+        return insertionIndex;
     }
 
     private void addDefaultStatusWidget(int index) {
@@ -986,18 +1059,11 @@
     }
 
     private void addWidgetsFromSettings() {
-        DevicePolicyManager dpm =
-                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        if (dpm != null) {
-            final int currentUser = mLockPatternUtils.getCurrentUser();
-            final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
-            if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
-                Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
-                return;
-            }
+        if (mSafeModeEnabled || widgetsDisabledByDpm()) {
+            return;
         }
 
-        int addPageIndex = getAddPageIndex();
+        int insertionIndex = getInsertPageIndex();
 
         // Add user-selected widget
         final int[] widgets = mLockPatternUtils.getAppWidgets();
@@ -1007,50 +1073,95 @@
         } else {
             for (int i = widgets.length -1; i >= 0; i--) {
                 if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
-                    addDefaultStatusWidget(addPageIndex + 1);
+                    addDefaultStatusWidget(insertionIndex);
                 } else {
                     // We add the widgets from left to right, starting after the first page after
                     // the add page. We count down, since the order will be persisted from right
                     // to left, starting after camera.
-                    addWidget(widgets[i], addPageIndex + 1);
+                    addWidget(widgets[i], insertionIndex, true);
                 }
             }
         }
-        checkAppWidgetConsistency();
     }
 
+    private int allocateIdForDefaultAppWidget() {
+        int appWidgetId;
+        Resources res = getContext().getResources();
+        ComponentName defaultAppWidget = new ComponentName(
+                res.getString(R.string.widget_default_package_name),
+                res.getString(R.string.widget_default_class_name));
+
+        // Note: we don't support configuring the widget
+        appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+        try {
+            mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
+
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
+            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+            appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+        }
+        return appWidgetId;
+    }
     public void checkAppWidgetConsistency() {
+        // Since this method may bind a widget (which we can't do until boot completed) we
+        // may have to defer it until after boot complete.
+        if (!mBootCompleted) {
+            mCheckAppWidgetConsistencyOnBootCompleted = true;
+            return;
+        }
         final int childCount = mAppWidgetContainer.getChildCount();
         boolean widgetPageExists = false;
         for (int i = 0; i < childCount; i++) {
-            if (isWidgetPage(i)) {
+            if (mAppWidgetContainer.isWidgetPage(i)) {
                 widgetPageExists = true;
                 break;
             }
         }
         if (!widgetPageExists) {
-            final int addPageIndex = getAddPageIndex();
+            final int insertPageIndex = getInsertPageIndex();
 
-            Resources res = getContext().getResources();
-            ComponentName defaultAppWidget = new ComponentName(
-                    res.getString(R.string.widget_default_package_name),
-                    res.getString(R.string.widget_default_class_name));
+            final boolean userAddedWidgetsEnabled = !widgetsDisabledByDpm();
+            boolean addedDefaultAppWidget = false;
 
-            // Note: we don't support configuring the widget
-            int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
-            boolean bindSuccessful = false;
-            try {
-                mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
-                bindSuccessful = true;
-            } catch (IllegalArgumentException e) {
-                Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
+            if (!mSafeModeEnabled) {
+                if (userAddedWidgetsEnabled) {
+                    int appWidgetId = allocateIdForDefaultAppWidget();
+                    if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+                        addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, true);
+                    }
+                } else {
+                    // note: even if widgetsDisabledByDpm() returns true, we still bind/create
+                    // the default appwidget if possible
+                    int appWidgetId = mLockPatternUtils.getFallbackAppWidgetId();
+                    if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+                        appWidgetId = allocateIdForDefaultAppWidget();
+                        if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+                            mLockPatternUtils.writeFallbackAppWidgetId(appWidgetId);
+                        }
+                    }
+                    if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+                        addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, false);
+                        if (!addedDefaultAppWidget) {
+                            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+                            mLockPatternUtils.writeFallbackAppWidgetId(
+                                    AppWidgetManager.INVALID_APPWIDGET_ID);
+                        }
+                    }
+                }
             }
+
             // Use the built-in status/clock view if we can't inflate the default widget
-            if (!(bindSuccessful && addWidget(appWidgetId, addPageIndex + 1))) {
-                addDefaultStatusWidget(addPageIndex + 1);
+            if (!addedDefaultAppWidget) {
+                addDefaultStatusWidget(insertPageIndex);
             }
-            mAppWidgetContainer.onAddView(
-                    mAppWidgetContainer.getChildAt(addPageIndex + 1), addPageIndex + 1);
+
+            // trigger DB updates only if user-added widgets are enabled
+            if (!mSafeModeEnabled && userAddedWidgetsEnabled) {
+                mAppWidgetContainer.onAddView(
+                        mAppWidgetContainer.getChildAt(insertPageIndex), insertPageIndex);
+            }
         }
     }
 
@@ -1094,7 +1205,6 @@
     @Override
     public Parcelable onSaveInstanceState() {
         if (DEBUG) Log.d(TAG, "onSaveInstanceState");
-        saveStickyWidgetIndex();
         Parcelable superState = super.onSaveInstanceState();
         SavedState ss = new SavedState(superState);
         ss.transportState = mViewStateManager.getTransportState();
@@ -1118,9 +1228,7 @@
     public void onWindowFocusChanged(boolean hasWindowFocus) {
         super.onWindowFocusChanged(hasWindowFocus);
         if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
-        if (!hasWindowFocus) {
-            saveStickyWidgetIndex();
-        } else if (mShowSecurityWhenReturn) {
+        if (hasWindowFocus && mShowSecurityWhenReturn) {
             SlidingChallengeLayout slider =
                 (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
             if (slider != null) {
@@ -1163,56 +1271,29 @@
         return false;
     }
 
-    private int getStickyWidget() {
-        // The first time we query the persistent state. From that point, we use a locally updated
-        // notion of the sticky widget page.
-        if (!mPersitentStickyWidgetLoaded) {
-            mLocalStickyWidget = mLockPatternUtils.getStickyAppWidgetIndex();
-            mPersitentStickyWidgetLoaded = true;
-        }
-        return mLocalStickyWidget;
-    }
-
-    public void updateStickyWidget(int index) {
-        if (index < 0 || index >= mAppWidgetContainer.getChildCount()) {
-            return;
-        }
-        if (mAppWidgetContainer.isAddPage(index)) {
-            return;
-        }
-        if (mAppWidgetContainer.isCameraPage(index)) {
-            return;
-        }
-        if (isMusicPage(index)) {
-            return;
-        }
-
-        mLocalStickyWidget = index;
-    }
-
     boolean isMusicPage(int pageIndex) {
         return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
     }
 
     private int getAppropriateWidgetPage(boolean isMusicPlaying) {
         // assumes at least one widget (besides camera + add)
-
+        if (mAppWidgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
+            final int childCount = mAppWidgetContainer.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                if (mAppWidgetContainer.getWidgetPageAt(i).getContentAppWidgetId()
+                        == mAppWidgetToShow) {
+                    mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
+                    return i;
+                }
+            }
+            mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
+        }
         // if music playing, show transport
         if (isMusicPlaying) {
             if (DEBUG) Log.d(TAG, "Music playing, show transport");
             return mAppWidgetContainer.getWidgetPageIndex(mTransportControl);
         }
 
-        // if we have a valid sticky widget, show it
-        int stickyWidgetIndex = getStickyWidget();
-        if (stickyWidgetIndex > -1
-                && stickyWidgetIndex < mAppWidgetContainer.getChildCount()
-                && !mAppWidgetContainer.isAddPage(stickyWidgetIndex)
-                && !mAppWidgetContainer.isCameraPage(stickyWidgetIndex)) {
-            if (DEBUG) Log.d(TAG, "Valid sticky widget found, show page " + stickyWidgetIndex);
-            return stickyWidgetIndex;
-        }
-
         // else show the right-most widget (except for camera)
         int rightMost = mAppWidgetContainer.getChildCount() - 1;
         if (mAppWidgetContainer.isCameraPage(rightMost)) {
@@ -1222,13 +1303,6 @@
         return rightMost;
     }
 
-    private void saveStickyWidgetIndex() {
-        if (DEBUG) Log.d(TAG, "saveStickyWidgetIndex: " + mLocalStickyWidget);
-        if (mPersitentStickyWidgetLoaded && mLocalStickyWidget >= 0) {
-            mLockPatternUtils.setStickyAppWidgetIndex(mLocalStickyWidget);
-        }
-    }
-
     private void enableUserSelectorIfNecessary() {
         if (!UserManager.supportsMultipleUsers()) {
             return; // device doesn't support multi-user mode
@@ -1300,6 +1374,11 @@
         mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
     }
 
+    public void goToWidget(int appWidgetId) {
+        mAppWidgetToShow = appWidgetId;
+        mSwitchPageRunnable.run();
+    }
+
     public boolean handleMenuKey() {
         // The following enables the MENU key to work for testing automation
         if (shouldEnableMenuKey()) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index 5fb8cf0..51f0418 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -81,6 +81,8 @@
     private static final int MSG_USER_SWITCHED = 310;
     private static final int MSG_USER_REMOVED = 311;
     private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
+    protected static final int MSG_BOOT_COMPLETED = 313;
+
 
     private static KeyguardUpdateMonitor sInstance;
 
@@ -152,6 +154,9 @@
                 case MSG_KEYGUARD_VISIBILITY_CHANGED:
                     handleKeyguardVisibilityChanged(msg.arg1);
                     break;
+                case MSG_BOOT_COMPLETED:
+                    handleBootCompleted();
+                    break;
 
             }
         }
@@ -198,6 +203,8 @@
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED,
                        intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
+            } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_BOOT_COMPLETED));
             }
         }
     };
@@ -340,6 +347,7 @@
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
+        filter.addAction(Intent.ACTION_BOOT_COMPLETED);
         context.registerReceiver(mBroadcastReceiver, filter);
 
         try {
@@ -420,6 +428,18 @@
     }
 
     /**
+     * Handle {@link #MSG_BOOT_COMPLETED}
+     */
+    protected void handleBootCompleted() {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onBootCompleted();
+            }
+        }
+    }
+
+    /**
      * Handle {@link #MSG_USER_SWITCHED}
      */
     protected void handleUserRemoved(int userId) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
index 8c9ac8b..1ba1388 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitorCallback.java
@@ -99,4 +99,12 @@
      * Called when a user is removed.
      */
     void onUserRemoved(int userId) { }
+
+    /**
+     * Called when boot completed.
+     *
+     * Note, this callback will only be received if boot complete occurs after registering with
+     * KeyguardUpdateMonitor.
+     */
+    void onBootCompleted() { }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
index 9e3424d..6fcacd3 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewBase.java
@@ -260,4 +260,5 @@
             KeyguardViewMediator.ViewMediatorCallback viewMediatorCallback) {
         mViewMediatorCallback = viewMediatorCallback;
     }
+
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index 6d88652..365c530 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.appwidget.AppWidgetManager;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -240,6 +241,11 @@
             if (options.getBoolean(LockPatternUtils.KEYGUARD_SHOW_SECURITY_CHALLENGE)) {
                 mKeyguardView.showNextSecurityScreenIfPresent();
             }
+            int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET,
+                    AppWidgetManager.INVALID_APPWIDGET_ID);
+            if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
+                mKeyguardView.goToWidget(widgetToShow);
+            }
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index 3648d99..5e19271 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -175,7 +175,7 @@
      * Does not turn on screen, held while a call to {@link KeyguardViewManager#wakeWhenReadyTq(int)}
      * is called to make sure the device doesn't sleep before it has a chance to poke
      * the wake lock.
-     * @see #wakeWhenReadyLocked(int)
+     * @see #wakeWhenReady(int)
      */
     private PowerManager.WakeLock mWakeAndHandOff;
 
@@ -935,8 +935,8 @@
      * @see #handleWakeWhenReady
      * @see #onWakeKeyWhenKeyguardShowingTq(int)
      */
-    private void wakeWhenReadyLocked(int keyCode) {
-        if (DBG_WAKE) Log.d(TAG, "wakeWhenReadyLocked(" + keyCode + ")");
+    private void wakeWhenReady(int keyCode) {
+        if (DBG_WAKE) Log.d(TAG, "wakeWhenReady(" + keyCode + ")");
 
         /**
          * acquire the handoff lock that will keep the cpu running.  this will
@@ -1014,54 +1014,14 @@
      * action should be posted to a handler.
      *
      * @param keyCode The keycode of the key that woke the device
-     * @param isDocked True if the device is in the dock
-     * @return Whether we poked the wake lock (and turned the screen on)
      */
-    public boolean onWakeKeyWhenKeyguardShowingTq(int keyCode, boolean isDocked) {
+    public void onWakeKeyWhenKeyguardShowingTq(int keyCode) {
         if (DEBUG) Log.d(TAG, "onWakeKeyWhenKeyguardShowing(" + keyCode + ")");
 
-        if (isWakeKeyWhenKeyguardShowing(keyCode, isDocked)) {
-            // give the keyguard view manager a chance to adjust the state of the
-            // keyguard based on the key that woke the device before poking
-            // the wake lock
-            wakeWhenReadyLocked(keyCode);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * When the keyguard is showing we ignore some keys that might otherwise typically
-     * be considered wake keys.  We filter them out here.
-     *
-     * {@link KeyEvent#KEYCODE_POWER} is notably absent from this list because it
-     * is always considered a wake key.
-     */
-    private boolean isWakeKeyWhenKeyguardShowing(int keyCode, boolean isDocked) {
-        switch (keyCode) {
-            // ignore volume keys unless docked
-            case KeyEvent.KEYCODE_VOLUME_UP:
-            case KeyEvent.KEYCODE_VOLUME_DOWN:
-            case KeyEvent.KEYCODE_VOLUME_MUTE:
-                return isDocked;
-
-            // ignore media and camera keys
-            case KeyEvent.KEYCODE_MUTE:
-            case KeyEvent.KEYCODE_HEADSETHOOK:
-            case KeyEvent.KEYCODE_MEDIA_PLAY:
-            case KeyEvent.KEYCODE_MEDIA_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_STOP:
-            case KeyEvent.KEYCODE_MEDIA_NEXT:
-            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-            case KeyEvent.KEYCODE_MEDIA_REWIND:
-            case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-            case KeyEvent.KEYCODE_CAMERA:
-                return false;
-        }
-        return true;
+        // give the keyguard view manager a chance to adjust the state of the
+        // keyguard based on the key that woke the device before poking
+        // the wake lock
+        wakeWhenReady(keyCode);
     }
 
     /**
@@ -1073,17 +1033,14 @@
      * The 'Tq' suffix is per the documentation in {@link WindowManagerPolicy}.
      * Be sure not to take any action that takes a long time; any significant
      * action should be posted to a handler.
-     *
-     * @return Whether we poked the wake lock (and turned the screen on)
      */
-    public boolean onWakeMotionWhenKeyguardShowingTq() {
+    public void onWakeMotionWhenKeyguardShowingTq() {
         if (DEBUG) Log.d(TAG, "onWakeMotionWhenKeyguardShowing()");
 
         // give the keyguard view manager a chance to adjust the state of the
         // keyguard based on the key that woke the device before poking
         // the wake lock
-        wakeWhenReadyLocked(KeyEvent.KEYCODE_UNKNOWN);
-        return true;
+        wakeWhenReady(KeyEvent.KEYCODE_UNKNOWN);
     }
 
     public void keyguardDone(boolean authenticated, boolean wakeup) {
@@ -1350,7 +1307,7 @@
     }
 
     /**
-     * Handle message sent by {@link #wakeWhenReadyLocked(int)}
+     * Handle message sent by {@link #wakeWhenReady(int)}
      * @param keyCode The key that woke the device.
      * @see #WAKE_WHEN_READY
      */
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
index 922ced2..4948343 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
@@ -147,10 +147,6 @@
         // We only modify the page state if it is not currently under control by the slider.
         // This prevents conflicts.
 
-        if (mKeyguardHostView != null) {
-            mKeyguardHostView.updateStickyWidget(newPageIndex);
-        }
-
         // If the page hasn't switched, don't bother with any of this
         if (mCurrentPage == newPageIndex) return;
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index c930ecc..f20b8d4 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -22,9 +22,9 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.TimeInterpolator;
 import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import android.content.res.Resources;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.util.AttributeSet;
@@ -38,7 +38,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 
-import com.android.internal.R;
 import com.android.internal.widget.LockPatternUtils;
 
 import java.util.ArrayList;
@@ -69,8 +68,6 @@
     private int mPage = 0;
     private Callbacks mCallbacks;
 
-    private boolean mCameraWidgetEnabled;
-
     private int mWidgetToResetAfterFadeOut;
 
     // Bouncer
@@ -97,10 +94,6 @@
 
         setPageSwitchListener(this);
 
-        Resources r = getResources();
-        mCameraWidgetEnabled = r.getBoolean(R.bool.kg_enable_camera_default_widget);
-        mCenterSmallWidgetsVertically =
-                r.getBoolean(com.android.internal.R.bool.kg_center_small_widgets_vertically);
         mBackgroundWorkerThread = new HandlerThread("KeyguardWidgetPager Worker");
         mBackgroundWorkerThread.start();
         mBackgroundWorkerHandler = new Handler(mBackgroundWorkerThread.getLooper());
@@ -180,6 +173,22 @@
         }
     }
 
+    private void updateWidgetFramesImportantForAccessibility() {
+        final int pageCount = getPageCount();
+        for (int i = 0; i < pageCount; i++) {
+            KeyguardWidgetFrame frame = getWidgetPageAt(i);
+            updateWidgetFrameImportantForAccessibility(frame);
+        }
+    }
+
+    private void updateWidgetFrameImportantForAccessibility(KeyguardWidgetFrame frame) {
+        if (frame.getContentAlpha() <= 0) {
+            frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+        } else {
+            frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+        }
+    }
+
     private void userActivity() {
         if (mCallbacks != null) {
             mCallbacks.onUserActivityTimeoutChanged();
@@ -312,6 +321,7 @@
                 content.getContentDescription());
             frame.setContentDescription(contentDescription);
         }
+        updateWidgetFrameImportantForAccessibility(frame);
     }
 
     /**
@@ -485,35 +495,31 @@
         }
     }
 
+    public boolean isWidgetPage(int pageIndex) {
+        if (pageIndex < 0 || pageIndex >= getChildCount()) {
+            return false;
+        }
+        View v = getChildAt(pageIndex);
+        if (v != null && v instanceof KeyguardWidgetFrame) {
+            KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v;
+            return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID;
+        }
+        return false;
+    }
+
     @Override
     void boundByReorderablePages(boolean isReordering, int[] range) {
         if (isReordering) {
-            if (isAddWidgetPageVisible()) {
+            // Remove non-widget pages from the range
+            while (range[1] > range[0] && !isWidgetPage(range[1])) {
+                range[1]--;
+            }
+            while (range[0] < range[1] && !isWidgetPage(range[0])) {
                 range[0]++;
             }
-            if (isMusicWidgetVisible()) {
-                range[1]--;
-            }
-            if (isCameraWidgetVisible()) {
-                range[1]--;
-            }
         }
     }
 
-    /*
-     * Special widgets
-     */
-    boolean isAddWidgetPageVisible() {
-        // TODO: Make proper test once we decide whether the add-page is always showing
-        return true;
-    }
-    boolean isMusicWidgetVisible() {
-        return mViewStateManager.getTransportState() != KeyguardViewStateManager.TRANSPORT_GONE;
-    }
-    boolean isCameraWidgetVisible() {
-        return mCameraWidgetEnabled;
-    }
-
     protected void reorderStarting() {
         showOutlinesAndSidePages();
     }
@@ -559,6 +565,12 @@
     }
 
     @Override
+    void setCurrentPage(int currentPage) {
+        super.setCurrentPage(currentPage);
+        updateWidgetFramesImportantForAccessibility();
+    }
+
+    @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         mHasMeasure = false;
@@ -669,6 +681,7 @@
                     }
                     mWidgetToResetAfterFadeOut = -1;
                 }
+                updateWidgetFramesImportantForAccessibility();
             }
         });
         mChildrenOutlineFadeAnimation.start();
@@ -761,7 +774,7 @@
 
     boolean isAddPage(int pageIndex) {
         View v = getChildAt(pageIndex);
-        return v != null && v.getId() == R.id.keyguard_add_widget;
+        return v != null && v.getId() == com.android.internal.R.id.keyguard_add_widget;
     }
 
     boolean isCameraPage(int pageIndex) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
index 3bc39eb..97e7f95 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
@@ -16,21 +16,22 @@
 
 package com.android.internal.policy.impl.keyguard;
 
+import com.android.internal.R;
+
 import android.animation.Animator;
-import android.animation.AnimatorSet;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
-import com.android.internal.R;
-
 public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayout {
     private static final String TAG = "MultiPaneChallengeLayout";
 
@@ -47,7 +48,8 @@
     private OnBouncerStateChangedListener mBouncerListener;
 
     private final Rect mTempRect = new Rect();
-    private final Context mContext;
+
+    private final DisplayMetrics mDisplayMetrics;
 
     private final OnClickListener mScrimClickListener = new OnClickListener() {
         @Override
@@ -67,13 +69,14 @@
     public MultiPaneChallengeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        mContext = context;
-
         final TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.MultiPaneChallengeLayout, defStyleAttr, 0);
         mOrientation = a.getInt(R.styleable.MultiPaneChallengeLayout_orientation,
                 HORIZONTAL);
         a.recycle();
+
+        final Resources res = getResources();
+        mDisplayMetrics = res.getDisplayMetrics();
     }
 
     @Override
@@ -169,15 +172,32 @@
         mScrimView.setOnClickListener(mScrimClickListener);
     }
 
+    private int getVirtualHeight(LayoutParams lp, int height, int heightUsed) {
+        int virtualHeight = height;
+        final View root = getRootView();
+        if (root != null) {
+            // This calculation is super dodgy and relies on several assumptions.
+            // Specifically that the root of the window will be padded in for insets
+            // and that the window is LAYOUT_IN_SCREEN.
+            virtualHeight = mDisplayMetrics.heightPixels - root.getPaddingTop();
+        }
+        if (lp.childType == LayoutParams.CHILD_TYPE_WIDGET ||
+                lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
+            // Always measure the widget pager/user switcher as if there were no IME insets
+            // on the window. We want to avoid resizing widgets when possible as it can
+            // be ugly/expensive. This lets us simply clip them instead.
+            return virtualHeight - heightUsed;
+        }
+        return Math.min(virtualHeight - heightUsed, height);
+    }
+
     @Override
-    protected void onMeasure(int widthSpec, int heightSpec) {
+    protected void onMeasure(final int widthSpec, final int heightSpec) {
         if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
                 MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
             throw new IllegalArgumentException(
                     "MultiPaneChallengeLayout must be measured with an exact size");
         }
-        float squashedLayoutThreshold =
-                mContext.getResources().getDimension(R.dimen.kg_squashed_layout_threshold);
 
         final int width = MeasureSpec.getSize(widthSpec);
         final int height = MeasureSpec.getSize(heightSpec);
@@ -213,32 +233,26 @@
                 mUserSwitcherView = child;
 
                 if (child.getVisibility() == GONE) continue;
-                if (height < squashedLayoutThreshold) {
-                    int zero = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
-                    measureChild(child, zero, zero);
-                } else {
-                    int adjustedWidthSpec = widthSpec;
-                    int adjustedHeightSpec = heightSpec;
-                    if (lp.maxWidth >= 0) {
-                        adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
-                                Math.min(lp.maxWidth, MeasureSpec.getSize(widthSpec)),
-                                MeasureSpec.EXACTLY);
-                    }
-                    if (lp.maxHeight >= 0) {
-                        adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
-                                Math.min(lp.maxHeight, MeasureSpec.getSize(heightSpec)),
-                                MeasureSpec.EXACTLY);
-                    }
-                    // measureChildWithMargins will resolve layout direction for the LayoutParams
-                    measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
 
-                    // Only subtract out space from one dimension. Favor vertical.
-                    // Offset by 1.5x to add some balance along the other edge.
-                    if (Gravity.isVertical(lp.gravity)) {
-                        heightUsed += child.getMeasuredHeight() * 1.5f;
-                    } else if (Gravity.isHorizontal(lp.gravity)) {
-                        widthUsed += child.getMeasuredWidth() * 1.5f;
-                    }
+                int adjustedWidthSpec = widthSpec;
+                int adjustedHeightSpec = heightSpec;
+                if (lp.maxWidth >= 0) {
+                    adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
+                            Math.min(lp.maxWidth, width), MeasureSpec.EXACTLY);
+                }
+                if (lp.maxHeight >= 0) {
+                    adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
+                            Math.min(lp.maxHeight, height), MeasureSpec.EXACTLY);
+                }
+                // measureChildWithMargins will resolve layout direction for the LayoutParams
+                measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0);
+
+                // Only subtract out space from one dimension. Favor vertical.
+                // Offset by 1.5x to add some balance along the other edge.
+                if (Gravity.isVertical(lp.gravity)) {
+                    heightUsed += child.getMeasuredHeight() * 1.5f;
+                } else if (Gravity.isHorizontal(lp.gravity)) {
+                    widthUsed += child.getMeasuredWidth() * 1.5f;
                 }
             } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
                 setScrimView(child);
@@ -258,6 +272,8 @@
                 continue;
             }
 
+            final int virtualHeight = getVirtualHeight(lp, height, heightUsed);
+
             int adjustedWidthSpec;
             int adjustedHeightSpec;
             if (lp.centerWithinArea > 0) {
@@ -266,19 +282,19 @@
                             (int) ((width - widthUsed) * lp.centerWithinArea + 0.5f),
                             MeasureSpec.EXACTLY);
                     adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
-                            MeasureSpec.getSize(heightSpec) - heightUsed, MeasureSpec.EXACTLY);
+                            virtualHeight, MeasureSpec.EXACTLY);
                 } else {
                     adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
-                            MeasureSpec.getSize(widthSpec) - widthUsed, MeasureSpec.EXACTLY);
+                            width - widthUsed, MeasureSpec.EXACTLY);
                     adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
-                            (int) ((height - heightUsed) * lp.centerWithinArea + 0.5f),
+                            (int) (virtualHeight * lp.centerWithinArea + 0.5f),
                             MeasureSpec.EXACTLY);
                 }
             } else {
                 adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
-                        MeasureSpec.getSize(widthSpec) - widthUsed, MeasureSpec.EXACTLY);
+                        width - widthUsed, MeasureSpec.EXACTLY);
                 adjustedHeightSpec = MeasureSpec.makeMeasureSpec(
-                        MeasureSpec.getSize(heightSpec) - heightUsed, MeasureSpec.EXACTLY);
+                        virtualHeight, MeasureSpec.EXACTLY);
             }
             if (lp.maxWidth >= 0) {
                 adjustedWidthSpec = MeasureSpec.makeMeasureSpec(
@@ -331,6 +347,9 @@
             boolean adjustPadding) {
         final LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
+        final int heightUsed = padding.top + padding.bottom - getPaddingTop() - getPaddingBottom();
+        height = getVirtualHeight(lp, height, heightUsed);
+
         final int gravity = Gravity.getAbsoluteGravity(lp.gravity, getLayoutDirection());
 
         final boolean fixedLayoutSize = lp.centerWithinArea > 0;
@@ -382,8 +401,7 @@
                 }
                 break;
             case Gravity.CENTER_VERTICAL:
-                final int paddedHeight = height - padding.top - padding.bottom;
-                top = padding.top + (paddedHeight - childHeight) / 2;
+                top = padding.top + (height - childHeight) / 2;
                 bottom = top + childHeight;
                 break;
         }