am 5fff5f01: Merge "Make partially visible widget pages not important for accessibility." into jb-mr1-dev

* commit '5fff5f01f8af05164d5d7be72bdadee46360ba92':
  Make partially visible widget pages not important for accessibility.
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/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 5f598b1..fdaf39e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -254,4 +254,9 @@
      * Gets the infos for all visible windows.
      */
     void getVisibleWindowsForDisplay(int displayId, out List<WindowInfo> outInfos);
+
+    /**
+     * Device is in safe mode.
+     */
+    boolean isSafeModeEnabled();
 }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 8d83774..8d1be53 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -351,12 +351,12 @@
                     public void onClick(View v) {
                         // Insure that this view is a child of an AdapterView
                         View parent = (View) v.getParent();
-                        while (!(parent instanceof AdapterView<?>)
+                        while (parent != null && !(parent instanceof AdapterView<?>)
                                 && !(parent instanceof AppWidgetHostView)) {
                             parent = (View) parent.getParent();
                         }
 
-                        if (parent instanceof AppWidgetHostView) {
+                        if (parent instanceof AppWidgetHostView || parent == null) {
                             // Somehow they've managed to get this far without having
                             // and AdapterView as a parent.
                             Log.e("RemoteViews", "Collection item doesn't have AdapterView parent");
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e5e1a2b..47d501cd 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;
 
@@ -1112,6 +1114,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,
@@ -1325,5 +1346,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/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
index 8ca6d2a..6250e54 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
@@ -90,6 +90,7 @@
         if (mBiometricUnlock != null) {
             mBiometricUnlock.stop();
         }
+        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback);
     }
 
     @Override
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 b586d94..68783c3 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -37,6 +37,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -57,7 +59,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;
@@ -82,8 +84,17 @@
     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();
@@ -109,7 +120,26 @@
         mAppWidgetManager = AppWidgetManager.getInstance(mContext);
         mSecurityModel = new KeyguardSecurityModel(context);
 
-        mViewStateManager = new KeyguardViewStateManager();
+        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");
+        }
     }
 
     @Override
@@ -174,9 +204,10 @@
         }
 
         addDefaultWidgets();
-        addWidgetsFromSettings();
-        mSwitchPageRunnable.run();
 
+        addWidgetsFromSettings();
+        checkAppWidgetConsistency();
+        mSwitchPageRunnable.run();
         // This needs to be called after the pages are all added.
         mViewStateManager.showUsabilityHints();
 
@@ -184,6 +215,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++) {
@@ -818,15 +867,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;
         }
     }
@@ -840,7 +892,7 @@
 
             @Override
             public void onCameraLaunchedSuccessfully() {
-                if (isCameraPage(mAppWidgetContainer.getCurrentPage())) {
+                if (mAppWidgetContainer.isCameraPage(mAppWidgetContainer.getCurrentPage())) {
                     mAppWidgetContainer.scrollLeft();
                 }
                 setSliderHandleAlpha(1);
@@ -882,9 +934,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) {
@@ -892,19 +963,6 @@
             }
         }
 
-        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();
     }
@@ -929,7 +987,8 @@
             int lastWidget = mAppWidgetContainer.getChildCount() - 1;
             int position = 0; // handle no widget case
             if (lastWidget >= 0) {
-                position = isCameraPage(lastWidget) ? lastWidget : lastWidget + 1;
+                position = mAppWidgetContainer.isCameraPage(lastWidget) ?
+                        lastWidget : lastWidget + 1;
             }
             mAppWidgetContainer.addWidget(mTransportControl, position);
             mTransportControl.setVisibility(View.VISIBLE);
@@ -965,14 +1024,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) {
@@ -982,18 +1042,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();
@@ -1003,50 +1056,90 @@
         } 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() {
         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);
+            }
         }
     }
 
@@ -1143,30 +1236,42 @@
 
     private CameraWidgetFrame findCameraPage() {
         for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) {
-            if (isCameraPage(i)) {
+            if (mAppWidgetContainer.isCameraPage(i)) {
                 return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i);
             }
         }
         return null;
     }
 
-    private boolean isWidgetPage(int pageIndex) {
-        View v = mAppWidgetContainer.getChildAt(pageIndex);
-        if (v != null && v instanceof KeyguardWidgetFrame) {
-            KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v;
-            return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID;
+    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 false;
+        return mLocalStickyWidget;
     }
 
-    private boolean isCameraPage(int pageIndex) {
-        View v = mAppWidgetContainer.getChildAt(pageIndex);
-        return v != null && v instanceof CameraWidgetFrame;
+    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;
     }
 
-    private boolean isAddPage(int pageIndex) {
-        View v = mAppWidgetContainer.getChildAt(pageIndex);
-        return v != null && v.getId() == R.id.keyguard_add_widget;
+    boolean isMusicPage(int pageIndex) {
+        return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
     }
 
     private int getAppropriateWidgetPage(boolean isMusicPlaying) {
@@ -1179,18 +1284,18 @@
         }
 
         // if we have a valid sticky widget, show it
-        int stickyWidgetIndex = mLockPatternUtils.getStickyAppWidgetIndex();
+        int stickyWidgetIndex = getStickyWidget();
         if (stickyWidgetIndex > -1
                 && stickyWidgetIndex < mAppWidgetContainer.getChildCount()
-                && !isAddPage(stickyWidgetIndex)
-                && !isCameraPage(stickyWidgetIndex)) {
+                && !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 (isCameraPage(rightMost)) {
+        if (mAppWidgetContainer.isCameraPage(rightMost)) {
             rightMost--;
         }
         if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
@@ -1198,18 +1303,10 @@
     }
 
     private void saveStickyWidgetIndex() {
-        int stickyWidgetIndex = mAppWidgetContainer.getCurrentPage();
-        if (isAddPage(stickyWidgetIndex)) {
-            stickyWidgetIndex++;
+        if (DEBUG) Log.d(TAG, "saveStickyWidgetIndex: " + mLocalStickyWidget);
+        if (mPersitentStickyWidgetLoaded && mLocalStickyWidget >= 0) {
+            mLockPatternUtils.setStickyAppWidgetIndex(mLocalStickyWidget);
         }
-        if (isCameraPage(stickyWidgetIndex)) {
-            stickyWidgetIndex--;
-        }
-        if (stickyWidgetIndex < 0 || stickyWidgetIndex >= mAppWidgetContainer.getChildCount()) {
-            stickyWidgetIndex = -1;
-        }
-        if (DEBUG) Log.d(TAG, "saveStickyWidgetIndex: " + stickyWidgetIndex);
-        mLockPatternUtils.setStickyAppWidgetIndex(stickyWidgetIndex);
     }
 
     private void enableUserSelectorIfNecessary() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
index 3d4cb19..c92c791 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewFlipper.java
@@ -128,22 +128,24 @@
 
     @Override
     public void showBouncer(int duration) {
+        KeyguardSecurityView active = getSecurityView();
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
             if (child instanceof KeyguardSecurityView) {
                 KeyguardSecurityView ksv = (KeyguardSecurityView) child;
-                ksv.showBouncer(child.getVisibility() == View.VISIBLE ? duration : 0);
+                ksv.showBouncer(ksv == active ? duration : 0);
             }
         }
     }
 
     @Override
     public void hideBouncer(int duration) {
+        KeyguardSecurityView active = getSecurityView();
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
             if (child instanceof KeyguardSecurityView) {
                 KeyguardSecurityView ksv = (KeyguardSecurityView) child;
-                ksv.hideBouncer(child.getVisibility() == View.VISIBLE ? duration : 0);
+                ksv.hideBouncer(ksv == active ? duration : 0);
             }
         }
     }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewHelper.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewHelper.java
index 2ccdc1d..294bc3d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewHelper.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityViewHelper.java
@@ -42,7 +42,7 @@
         }
         if (bouncerFrame != null) {
             if (duration > 0) {
-                Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 255);
+                Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 0, 255);
                 anim.setDuration(duration);
                 anim.start();
             } else {
@@ -67,7 +67,7 @@
         }
         if (bouncerFrame != null) {
             if (duration > 0) {
-                Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 0);
+                Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 255, 0);
                 anim.setDuration(duration);
                 anim.start();
             } else {
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/KeyguardViewStateManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
index e53358b..922ced2 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
@@ -25,6 +25,7 @@
 
     private KeyguardWidgetPager mKeyguardWidgetPager;
     private ChallengeLayout mChallengeLayout;
+    private KeyguardHostView mKeyguardHostView;
     private int[] mTmpPoint = new int[2];
     private int[] mTmpLoc = new int[2];
 
@@ -49,7 +50,8 @@
 
     int mChallengeTop = 0;
 
-    public KeyguardViewStateManager() {
+    public KeyguardViewStateManager(KeyguardHostView hostView) {
+        mKeyguardHostView = hostView;
     }
 
     public void setPagedView(KeyguardWidgetPager pagedView) {
@@ -145,6 +147,10 @@
         // 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 9f0c5bd..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());
@@ -502,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();
     }
@@ -782,4 +771,19 @@
             mZoomInOutAnim.start();
         }
     }
+
+    boolean isAddPage(int pageIndex) {
+        View v = getChildAt(pageIndex);
+        return v != null && v.getId() == com.android.internal.R.id.keyguard_add_widget;
+    }
+
+    boolean isCameraPage(int pageIndex) {
+        View v = getChildAt(pageIndex);
+        return v != null && v instanceof CameraWidgetFrame;
+    }
+
+    @Override
+    protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
+        return !isCameraPage(childIndex) && super.shouldSetTopAlignedPivotForWidget(childIndex);
+    }
 }
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;
         }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
index 8f47578..2f25835 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/PagedView.java
@@ -34,6 +34,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.KeyEvent;
@@ -577,6 +578,10 @@
         computeScrollHelper();
     }
 
+    protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) {
+        return mTopAlignPageWhenShrinkingForBouncer;
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (!mIsDataReady || getChildCount() == 0) {
@@ -593,8 +598,10 @@
         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
         // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the
         // viewport, we can be at most one and a half screens offset once we scale down
-        int parentWidthSize = (int) (1.5f * parent.getMeasuredWidth());
-        int parentHeightSize = parent.getMeasuredHeight();
+        DisplayMetrics dm = getResources().getDisplayMetrics();
+        int maxSize = Math.max(dm.widthPixels, dm.heightPixels);
+        int parentWidthSize = (int) (1.5f * maxSize);
+        int parentHeightSize = maxSize;
         int scaledWidthSize = (int) (parentWidthSize / mMinScale);
         int scaledHeightSize = (int) (parentHeightSize / mMinScale);
         mViewport.set(0, 0, widthSize, heightSize);
@@ -651,7 +658,7 @@
                 MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode);
 
             child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
-            if (mTopAlignPageWhenShrinkingForBouncer) {
+            if (shouldSetTopAlignedPivotForWidget(i)) {
                 child.setPivotX(child.getWidth() / 2);
                 child.setPivotY(0f);
             }
@@ -1015,6 +1022,17 @@
         return  (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
     }
 
+    /** Returns whether x and y originated within the buffered/unbuffered viewport */
+    private boolean isTouchPointInViewport(int x, int y, boolean buffer) {
+        if (buffer) {
+            mTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top,
+                    mViewport.right + mViewport.width() / 2, mViewport.bottom);
+            return mTmpRect.contains(x, y);
+        } else {
+            return mViewport.contains(x, y);
+        }
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (DISABLE_TOUCH_INTERACTION) {
@@ -1093,7 +1111,11 @@
                     mTouchState = TOUCH_STATE_REST;
                     mScroller.abortAnimation();
                 } else {
-                    mTouchState = TOUCH_STATE_SCROLLING;
+                    if (isTouchPointInViewport((int) mDownMotionX, (int) mDownMotionY, true)) {
+                        mTouchState = TOUCH_STATE_SCROLLING;
+                    } else {
+                        mTouchState = TOUCH_STATE_REST;
+                    }
                 }
 
                 // check if this can be the beginning of a tap on the side of the pages
@@ -1115,6 +1137,10 @@
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
                 resetTouchState();
+                // Just intercept the touch event on up if we tap outside the strict viewport
+                if (!isTouchPointInViewport((int) mLastMotionX, (int) mLastMotionY, false)) {
+                    return true;
+                }
                 break;
 
             case MotionEvent.ACTION_POINTER_UP:
@@ -1139,24 +1165,19 @@
      * user moves their touch point too far.
      */
     protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
-        /*
-         * Locally do absolute value. mLastMotionX is set to the y value
-         * of the down event.
-         */
+        // Disallow scrolling if we don't have a valid pointer index
         final int pointerIndex = ev.findPointerIndex(mActivePointerId);
+        if (pointerIndex == -1) return;
 
-        if (pointerIndex == -1) {
-            return;
-        }
+        // Disallow scrolling if we started the gesture from outside the viewport
+        final float x = ev.getX(pointerIndex);
+        final float y = ev.getY(pointerIndex);
+        if (!isTouchPointInViewport((int) x, (int) y, true)) return;
 
         // If we're only allowing edge swipes, we break out early if the down event wasn't
         // at the edge.
-        if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) {
-            return;
-        }
+        if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return;
 
-        final float x = ev.getX(pointerIndex);
-        final float y = ev.getY(pointerIndex);
         final int xDiff = (int) Math.abs(x - mLastMotionX);
         final int yDiff = (int) Math.abs(y - mLastMotionY);
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
index 16ec8c5..c709a5f 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
@@ -26,8 +26,6 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.FloatProperty;
@@ -62,7 +60,6 @@
 
     // Drawn to show the drag handle in closed state; crossfades to the challenge view
     // when challenge is fully visible
-    private Drawable mFrameDrawable;
     private boolean mEdgeCaptured;
 
     private DisplayMetrics mDisplayMetrics;
@@ -76,6 +73,8 @@
     // Range: 0 (fully hidden) to 1 (fully visible)
     private float mChallengeOffset = 1.f;
     private boolean mChallengeShowing = true;
+    private boolean mChallengeShowingTargetState = true;
+    private boolean mWasChallengeShowing = true;
     private boolean mIsBouncing = false;
 
     private final Scroller mScroller;
@@ -127,8 +126,6 @@
     private ObjectAnimator mHandleAnimation;
     private ObjectAnimator mFrameAnimation;
 
-    private final Rect mTempRect = new Rect();
-
     private boolean mHasGlowpad;
 
     // We have an internal and external version, and we and them together.
@@ -149,23 +146,6 @@
         }
     };
 
-    static final Property<SlidingChallengeLayout, Float> FRAME_ALPHA =
-            new FloatProperty<SlidingChallengeLayout>("frameAlpha") {
-        @Override
-        public void setValue(SlidingChallengeLayout view, float value) {
-            if (view.mFrameDrawable != null) {
-                view.mFrameAlpha = value;
-                view.mFrameDrawable.setAlpha((int) (value * 0xFF));
-                view.mFrameDrawable.invalidateSelf();
-            }
-        }
-
-        @Override
-        public Float get(SlidingChallengeLayout view) {
-            return view.mFrameAlpha;
-        }
-    };
-
     // True if at least one layout pass has happened since the view was attached.
     private boolean mHasLayout;
 
@@ -310,46 +290,6 @@
         mHandleAnimation.start();
     }
 
-    void animateFrame(final boolean visible, final boolean full) {
-        if (mFrameDrawable == null) return;
-
-        final float targetAlpha = visible ? (full ? 1.f : 0.5f) : 0.f;
-        if (mFrameAnimation != null && targetAlpha != mFrameAnimationTarget) {
-            mFrameAnimation.cancel();
-            mFrameAnimationTarget = Float.MIN_VALUE;
-        }
-        if (targetAlpha == mFrameAlpha || targetAlpha == mFrameAnimationTarget) {
-            return;
-        }
-        mFrameAnimationTarget = targetAlpha;
-
-        mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA, targetAlpha);
-        mFrameAnimation.setInterpolator(sHandleFadeInterpolator);
-        mFrameAnimation.setDuration(HANDLE_ANIMATE_DURATION);
-        mFrameAnimation.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mFrameAnimationTarget = Float.MIN_VALUE;
-
-                if (!visible && full && mChallengeView != null) {
-                    // Mess with padding/margin to remove insets on the bouncer frame.
-                    mChallengeView.setPadding(0, 0, 0, 0);
-                    LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
-                    lp.leftMargin = lp.rightMargin = getChallengeMargin(true);
-                    mChallengeView.setLayoutParams(lp);
-                }
-                mFrameAnimation = null;
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mFrameAnimationTarget = Float.MIN_VALUE;
-                mFrameAnimation = null;
-            }
-        });
-        mFrameAnimation.start();
-    }
-
     private void sendInitialListenerUpdates() {
         if (mScrollListener != null) {
             int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
@@ -409,9 +349,6 @@
             mScrollState = state;
 
             animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
-            if (!mIsBouncing) {
-                animateFrame(false, false);
-            }
             if (mScrollListener != null) {
                 mScrollListener.onScrollStateChanged(state);
             }
@@ -419,7 +356,8 @@
     }
 
     void completeChallengeScroll() {
-        setChallengeShowing(mChallengeOffset != 0);
+        setChallengeShowing(mChallengeShowingTargetState);
+        mChallengeOffset = mChallengeShowing ? 1.f : 0.f;
         setScrollState(SCROLL_STATE_IDLE);
         mChallengeInteractiveInternal = true;
         mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
@@ -532,6 +470,7 @@
     @Override
     public void showBouncer() {
         if (mIsBouncing) return;
+        mWasChallengeShowing = mChallengeShowing;
         mIsBouncing = true;
         showChallenge(true);
         if (mScrimView != null) {
@@ -543,18 +482,11 @@
         // Mess with padding/margin to inset the bouncer frame.
         // We have more space available to us otherwise.
         if (mChallengeView != null) {
-            if (mFrameDrawable == null || !mFrameDrawable.getPadding(mTempRect)) {
-                mTempRect.set(0, 0, 0, 0);
-            }
-            mChallengeView.setPadding(mTempRect.left, mTempRect.top, mTempRect.right,
-                    mTempRect.bottom);
             final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
             lp.leftMargin = lp.rightMargin = getChallengeMargin(false);
             mChallengeView.setLayoutParams(lp);
         }
 
-        animateFrame(true, true);
-
         if (mBouncerListener != null) {
             mBouncerListener.onBouncerStateChanged(true);
         }
@@ -563,7 +495,7 @@
     @Override
     public void hideBouncer() {
         if (!mIsBouncing) return;
-        showChallenge(false);
+        if (!mWasChallengeShowing) showChallenge(false);
         mIsBouncing = false;
         if (mScrimView != null) {
             mScrimView.setVisibility(GONE);
@@ -571,7 +503,6 @@
         if (mChallengeView != null) {
             mChallengeView.hideBouncer(HANDLE_ANIMATE_DURATION);
         }
-        animateFrame(false, true);
         if (mBouncerListener != null) {
             mBouncerListener.onBouncerStateChanged(false);
         }
@@ -861,7 +792,6 @@
                     mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
                 }
                 // We're going to play silly games with the frame's background drawable later.
-                mFrameDrawable = mChallengeView.getBackground();
                 if (!mHasLayout) {
                     // Set up the margin correctly based on our content for the first run.
                     mHasGlowpad = child.findViewById(R.id.keyguard_selector_view) != null;
@@ -971,9 +901,6 @@
         }
 
         if (!mHasLayout) {
-            if (mFrameDrawable != null) {
-                mFrameDrawable.setAlpha(0);
-            }
             mHasLayout = true;
         }
     }
@@ -1187,6 +1114,7 @@
         }
 
         if (mHasLayout) {
+            mChallengeShowingTargetState = show;
             final int layoutBottom = getLayoutBottom();
             animateChallengeTo(show ? layoutBottom :
                     layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 06594090..4659c9d 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -10347,6 +10347,10 @@
     public void lockNow(Bundle options) {
         mPolicy.lockNow(options);
     }
+    
+    public boolean isSafeModeEnabled() {
+        return mSafeMode;
+    }
 
     void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
         pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index da736b7..fa2cb50 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -446,6 +446,11 @@
     public void lockNow(Bundle options) {
         // TODO Auto-generated method stub
     }
+    
+    @Override
+    public boolean isSafeModeEnabled() {
+        return false;
+    }
 
     @Override
     public IBinder getFocusedWindowToken() {