Merge "Move preloaded-classes out of framework.jar." into lmp-mr1-dev
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index f361695b..4b0cef6 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -20,8 +20,11 @@
 import android.util.Slog;
 
 import com.android.internal.telephony.TelephonyProperties;
+
 import dalvik.system.VMRuntime;
 
+import java.util.Objects;
+
 /**
  * Information about the current build, extracted from system properties.
  */
@@ -640,6 +643,32 @@
         }
     }
 
+    /**
+     * Check that device fingerprint is defined and that it matches across
+     * various partitions.
+     *
+     * @hide
+     */
+    public static boolean isFingerprintConsistent() {
+        final String system = SystemProperties.get("ro.build.fingerprint");
+        final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
+
+        if (TextUtils.isEmpty(system)) {
+            Slog.e(TAG, "Required ro.build.fingerprint is empty!");
+            return false;
+        }
+
+        if (!TextUtils.isEmpty(vendor)) {
+            if (!Objects.equals(system, vendor)) {
+                Slog.e(TAG, "Mismatched fingerprints; system reported " + system
+                        + " but vendor reported " + vendor);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     // The following properties only make sense for internal engineering builds.
     public static final long TIME = getLong("ro.build.date.utc") * 1000;
     public static final String USER = getString("ro.build.user");
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 19142b8..33ce517 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -53,6 +53,9 @@
 
     private static native void nativeAllocateBuffers(long nativeObject);
 
+    private static native int nativeGetWidth(long nativeObject);
+    private static native int nativeGetHeight(long nativeObject);
+
     public static final Parcelable.Creator<Surface> CREATOR =
             new Parcelable.Creator<Surface>() {
         @Override
@@ -327,7 +330,9 @@
             if (mHwuiContext == null) {
                 mHwuiContext = new HwuiContext();
             }
-            return mHwuiContext.lockCanvas();
+            return mHwuiContext.lockCanvas(
+                    nativeGetWidth(mNativeObject),
+                    nativeGetHeight(mNativeObject));
         }
     }
 
@@ -576,11 +581,11 @@
             mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject);
         }
 
-        Canvas lockCanvas() {
+        Canvas lockCanvas(int width, int height) {
             if (mCanvas != null) {
                 throw new IllegalStateException("Surface was already locked!");
             }
-            mCanvas = mRenderNode.start(0, 0);
+            mCanvas = mRenderNode.start(width, height);
             return mCanvas;
         }
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index a0b2ca8..a39ff8e 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -357,6 +357,22 @@
     parcel->writeStrongBinder( self != 0 ? self->getIGraphicBufferProducer()->asBinder() : NULL);
 }
 
+static jint nativeGetWidth(JNIEnv* env, jclass clazz, jlong nativeObject) {
+    Surface* surface = reinterpret_cast<Surface*>(nativeObject);
+    ANativeWindow* anw = static_cast<ANativeWindow*>(surface);
+    int value = 0;
+    anw->query(anw, NATIVE_WINDOW_WIDTH, &value);
+    return value;
+}
+
+static jint nativeGetHeight(JNIEnv* env, jclass clazz, jlong nativeObject) {
+    Surface* surface = reinterpret_cast<Surface*>(nativeObject);
+    ANativeWindow* anw = static_cast<ANativeWindow*>(surface);
+    int value = 0;
+    anw->query(anw, NATIVE_WINDOW_HEIGHT, &value);
+    return value;
+}
+
 namespace uirenderer {
 
 using namespace android::uirenderer::renderthread;
@@ -426,6 +442,8 @@
             (void*)nativeReadFromParcel },
     {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
             (void*)nativeWriteToParcel },
+    {"nativeGetWidth", "(J)I", (void*)nativeGetWidth },
+    {"nativeGetHeight", "(J)I", (void*)nativeGetHeight },
 
     // HWUI context
     {"nHwuiCreate", "(JJ)J", (void*) hwui::create },
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 981c576..c4b9c5f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5121,4 +5121,10 @@
 
     <!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
     <string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string>
+
+    <!-- Error message shown when there is a system error which can be solved by user performing factory reset. [CHAR LIMIT=NONE] -->
+    <string name="system_error_wipe_data">There\'s an internal problem with your device, and it may be unstable until you factory data reset.</string>
+    <!-- Error message shown when there is a system error which can be solved by the manufacturer. [CHAR LIMIT=NONE] -->
+    <string name="system_error_manufacturer">There\'s an internal problem with your device. Contact your manufacturer for details.</string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c592f49..f6d0836 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2105,4 +2105,9 @@
 
   <!-- From SignalStrength -->
   <java-symbol type="integer" name="config_LTE_RSRP_threshold_type" />
+
+  <java-symbol type="string" name="android_system_label" />
+  <java-symbol type="string" name="system_error_wipe_data" />
+  <java-symbol type="string" name="system_error_manufacturer" />
+
 </resources>
diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml
index 55139fb..ea2e1e1 100644
--- a/packages/SystemUI/res/layout/qs_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_detail_item.xml
@@ -31,7 +31,7 @@
     <LinearLayout
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginStart="20dp"
+        android:layout_marginStart="12dp"
         android:layout_weight="1"
         android:orientation="vertical" >
 
diff --git a/packages/SystemUI/res/layout/remember_permission_checkbox.xml b/packages/SystemUI/res/layout/remember_permission_checkbox.xml
index a21acb3..4985ff5 100644
--- a/packages/SystemUI/res/layout/remember_permission_checkbox.xml
+++ b/packages/SystemUI/res/layout/remember_permission_checkbox.xml
@@ -20,9 +20,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:paddingStart="8dp"
-    android:paddingEnd="8dp"
-    android:paddingTop="8dp">
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:paddingTop="16dp">
 
     <CheckBox
         android:id="@+id/remember"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 7ea9145..9912e89c 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -38,8 +38,8 @@
         android:layout_alignParentEnd="true"
         android:background="@drawable/ripple_drawable" >
         <ImageView android:id="@+id/multi_user_avatar"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
+            android:layout_width="@dimen/multi_user_avatar_expanded_size"
+            android:layout_height="@dimen/multi_user_avatar_expanded_size"
             android:layout_gravity="center"
             android:scaleType="centerInside"/>
     </com.android.systemui.statusbar.phone.MultiUserSwitch>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5caa866..3d9f723 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -321,7 +321,7 @@
     <dimen name="heads_up_window_height">250dp</dimen>
 
     <!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
-    <dimen name="keyguard_min_swipe_amount">90dp</dimen>
+    <dimen name="keyguard_min_swipe_amount">110dp</dimen>
 
     <!-- The minimum background radius when swiping to a side for the camera / phone affordances. -->
     <dimen name="keyguard_affordance_min_background_radius">30dp</dimen>
@@ -405,6 +405,9 @@
     <!-- The width of user avatar when on Keyguard -->
     <dimen name="multi_user_avatar_keyguard_size">22dp</dimen>
 
+    <!-- The width of user avatar when expanded -->
+    <dimen name="multi_user_avatar_expanded_size">24dp</dimen>
+
     <!-- The font size of the time when collapsed in QS -->
     <dimen name="qs_time_collapsed_size">14sp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 4857adc..0516768 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -174,7 +174,7 @@
 
         public void trimMemory(int level) {
             if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW &&
-                    mBackground != null && mIsHwAccelerated) {
+                    mBackground != null) {
                 if (DEBUG) {
                     Log.d(TAG, "trimMemory");
                 }
@@ -212,6 +212,7 @@
                 unregisterReceiver(mReceiver);
             }
             mBackground = null;
+            mWallpaperManager.forgetLoadedWallpaper();
         }
 
         void updateSurfaceSize(SurfaceHolder surfaceHolder) {
@@ -337,111 +338,116 @@
         }
 
         void drawFrame() {
-            int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)).
-                    getDefaultDisplay().getRotation();
+            try {
+                int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)).
+                        getDefaultDisplay().getRotation();
 
-            // Sometimes a wallpaper is not large enough to cover the screen in one dimension.
-            // Call updateSurfaceSize -- it will only actually do the update if the dimensions
-            // should change
-            if (newRotation != mLastRotation) {
-                // Update surface size (if necessary)
-                updateSurfaceSize(getSurfaceHolder());
-            }
-            SurfaceHolder sh = getSurfaceHolder();
-            final Rect frame = sh.getSurfaceFrame();
-            final int dw = frame.width();
-            final int dh = frame.height();
-            boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth || dh != mLastSurfaceHeight;
-
-            boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
-            if (!redrawNeeded && !mOffsetsChanged) {
-                if (DEBUG) {
-                    Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
-                            + "and offsets have not changed.");
+                // Sometimes a wallpaper is not large enough to cover the screen in one dimension.
+                // Call updateSurfaceSize -- it will only actually do the update if the dimensions
+                // should change
+                if (newRotation != mLastRotation) {
+                    // Update surface size (if necessary)
+                    updateSurfaceSize(getSurfaceHolder());
                 }
-                return;
-            }
-            mLastRotation = newRotation;
+                SurfaceHolder sh = getSurfaceHolder();
+                final Rect frame = sh.getSurfaceFrame();
+                final int dw = frame.width();
+                final int dh = frame.height();
+                boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth
+                        || dh != mLastSurfaceHeight;
 
-            // Load bitmap if it is not yet loaded or if it was loaded at a different size
-            if (mBackground == null || surfaceDimensionsChanged) {
-                if (DEBUG) {
-                    Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
-                            mBackground + ", " +
-                            ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
-                            ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
-                            dw + ", " + dh);
-                }
-                mWallpaperManager.forgetLoadedWallpaper();
-                updateWallpaperLocked();
-                if (mBackground == null) {
+                boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
+                if (!redrawNeeded && !mOffsetsChanged) {
                     if (DEBUG) {
-                        Log.d(TAG, "Unable to load bitmap");
+                        Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
+                                + "and offsets have not changed.");
                     }
                     return;
                 }
-                if (DEBUG) {
-                    if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) {
-                        Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " +
-                                dw + ", " + dh + ", " + mBackground.getWidth() + ", " +
-                                mBackground.getHeight());
+                mLastRotation = newRotation;
+
+                // Load bitmap if it is not yet loaded or if it was loaded at a different size
+                if (mBackground == null || surfaceDimensionsChanged) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
+                                mBackground + ", " +
+                                ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
+                                ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
+                                dw + ", " + dh);
+                    }
+                    mWallpaperManager.forgetLoadedWallpaper();
+                    updateWallpaperLocked();
+                    if (mBackground == null) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Unable to load bitmap");
+                        }
+                        return;
+                    }
+                    if (DEBUG) {
+                        if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) {
+                            Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " +
+                                    dw + ", " + dh + ", " + mBackground.getWidth() + ", " +
+                                    mBackground.getHeight());
+                        }
                     }
                 }
-            }
 
-            // Center the scaled image
-            mScale = Math.max(1f, Math.max(dw / (float) mBackground.getWidth(),
-                    dh / (float) mBackground.getHeight()));
-            final int availw = dw - (int) (mBackground.getWidth() * mScale);
-            final int availh = dh - (int) (mBackground.getHeight() * mScale);
-            int xPixels = availw / 2;
-            int yPixels = availh / 2;
+                // Center the scaled image
+                mScale = Math.max(1f, Math.max(dw / (float) mBackground.getWidth(),
+                        dh / (float) mBackground.getHeight()));
+                final int availw = dw - (int) (mBackground.getWidth() * mScale);
+                final int availh = dh - (int) (mBackground.getHeight() * mScale);
+                int xPixels = availw / 2;
+                int yPixels = availh / 2;
 
-            // Adjust the image for xOffset/yOffset values. If window manager is handling offsets,
-            // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels
-            // will remain unchanged
-            final int availwUnscaled = dw - mBackground.getWidth();
-            final int availhUnscaled = dh - mBackground.getHeight();
-            if (availwUnscaled < 0) xPixels += (int)(availwUnscaled * (mXOffset - .5f) + .5f);
-            if (availhUnscaled < 0) yPixels += (int)(availhUnscaled * (mYOffset - .5f) + .5f);
+                // Adjust the image for xOffset/yOffset values. If window manager is handling offsets,
+                // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels
+                // will remain unchanged
+                final int availwUnscaled = dw - mBackground.getWidth();
+                final int availhUnscaled = dh - mBackground.getHeight();
+                if (availwUnscaled < 0)
+                    xPixels += (int) (availwUnscaled * (mXOffset - .5f) + .5f);
+                if (availhUnscaled < 0)
+                    yPixels += (int) (availhUnscaled * (mYOffset - .5f) + .5f);
 
-            mOffsetsChanged = false;
-            mRedrawNeeded = false;
-            if (surfaceDimensionsChanged) {
-                mLastSurfaceWidth = dw;
-                mLastSurfaceHeight = dh;
-            }
-            if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
-                if (DEBUG) {
-                    Log.d(TAG, "Suppressed drawFrame since the image has not "
-                            + "actually moved an integral number of pixels.");
+                mOffsetsChanged = false;
+                mRedrawNeeded = false;
+                if (surfaceDimensionsChanged) {
+                    mLastSurfaceWidth = dw;
+                    mLastSurfaceHeight = dh;
                 }
-                return;
-            }
-            mLastXTranslation = xPixels;
-            mLastYTranslation = yPixels;
+                if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Suppressed drawFrame since the image has not "
+                                + "actually moved an integral number of pixels.");
+                    }
+                    return;
+                }
+                mLastXTranslation = xPixels;
+                mLastYTranslation = yPixels;
 
-            if (DEBUG) {
-                Log.d(TAG, "Redrawing wallpaper");
-            }
+                if (DEBUG) {
+                    Log.d(TAG, "Redrawing wallpaper");
+                }
 
-            if (mIsHwAccelerated) {
-                if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
+                if (mIsHwAccelerated) {
+                    if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
+                        drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
+                    }
+                } else {
                     drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
                 }
-            } else {
-                drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
-                if (FIXED_SIZED_SURFACE) {
+            } finally {
+                if (FIXED_SIZED_SURFACE && !mIsHwAccelerated) {
                     // If the surface is fixed-size, we should only need to
                     // draw it once and then we'll let the window manager
                     // position it appropriately.  As such, we no longer needed
                     // the loaded bitmap.  Yay!
-                    // hw-accelerated path retains bitmap for faster rotation
+                    // hw-accelerated renderer retains bitmap for faster rotation
                     mBackground = null;
                     mWallpaperManager.forgetLoadedWallpaper();
                 }
             }
-
         }
 
         private void updateWallpaperLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 4667d56..58a2d41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -46,7 +46,6 @@
 
     private FlingAnimationUtils mFlingAnimationUtils;
     private Callback mCallback;
-    private int mTrackingPointer;
     private VelocityTracker mVelocityTracker;
     private boolean mSwipingInProgress;
     private float mInitialTouchX;
@@ -65,6 +64,7 @@
     private Animator mSwipeAnimator;
     private int mMinBackgroundRadius;
     private boolean mMotionPerformedByUser;
+    private boolean mMotionCancelled;
     private AnimatorListenerAdapter mFlingEndListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
@@ -117,13 +117,11 @@
     }
 
     public boolean onTouchEvent(MotionEvent event) {
-        int pointerIndex = event.findPointerIndex(mTrackingPointer);
-        if (pointerIndex < 0) {
-            pointerIndex = 0;
-            mTrackingPointer = event.getPointerId(pointerIndex);
+        if (mMotionCancelled && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
+            return false;
         }
-        final float y = event.getY(pointerIndex);
-        final float x = event.getX(pointerIndex);
+        final float y = event.getY();
+        final float x = event.getX();
 
         boolean isUp = false;
         switch (event.getActionMasked()) {
@@ -137,22 +135,12 @@
                 initVelocityTracker();
                 trackMovement(event);
                 mMotionPerformedByUser = false;
+                mMotionCancelled = false;
                 break;
-
-            case MotionEvent.ACTION_POINTER_UP:
-                final int upPointer = event.getPointerId(event.getActionIndex());
-                if (mTrackingPointer == upPointer) {
-                    // gesture is ongoing, find a new pointer to track
-                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                    final float newY = event.getY(newIndex);
-                    final float newX = event.getX(newIndex);
-                    mTrackingPointer = event.getPointerId(newIndex);
-                    mInitialTouchY = newY;
-                    mInitialTouchX = newX;
-                    mTranslationOnDown = mTranslation;
-                }
+            case MotionEvent.ACTION_POINTER_DOWN:
+                mMotionCancelled = true;
+                endMotion(event, true /* forceSnapBack */);
                 break;
-
             case MotionEvent.ACTION_MOVE:
                 final float w = x - mInitialTouchX;
                 trackMovement(event);
@@ -174,20 +162,23 @@
             case MotionEvent.ACTION_UP:
                 isUp = true;
             case MotionEvent.ACTION_CANCEL:
-                mTrackingPointer = -1;
                 trackMovement(event);
-                if (mSwipingInProgress) {
-                    flingWithCurrentVelocity(!isUp);
-                }
-                if (mVelocityTracker != null) {
-                    mVelocityTracker.recycle();
-                    mVelocityTracker = null;
-                }
+                endMotion(event, !isUp);
                 break;
         }
         return true;
     }
 
+    private void endMotion(MotionEvent event, boolean forceSnapBack) {
+        if (mSwipingInProgress) {
+            flingWithCurrentVelocity(forceSnapBack);
+        }
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
     private void setSwipingInProgress(boolean inProgress) {
         mSwipingInProgress = inProgress;
         if (inProgress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 4715d0a..82f5a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -67,15 +67,18 @@
 
     @Override
     public void onClick(View v) {
-        if (opensUserSwitcherWhenClicked()) {
+        if (UserSwitcherController.isUserSwitcherAvailable(mUserManager)) {
             if (mKeyguardMode) {
                 if (mKeyguardUserSwitcher != null) {
                     mKeyguardUserSwitcher.show(true /* animate */);
                 }
             } else {
                 if (mQsPanel != null) {
-                    mQsPanel.showDetailAdapter(true,
-                            mQsPanel.getHost().getUserSwitcherController().userDetailAdapter);
+                    UserSwitcherController userSwitcherController =
+                            mQsPanel.getHost().getUserSwitcherController();
+                    if (userSwitcherController != null) {
+                        mQsPanel.showDetailAdapter(true, userSwitcherController.userDetailAdapter);
+                    }
                 }
             }
         } else {
@@ -92,12 +95,14 @@
 
         if (isClickable()) {
             String text;
-            if (opensUserSwitcherWhenClicked()) {
+            if (UserSwitcherController.isUserSwitcherAvailable(mUserManager)) {
                 String currentUser = null;
                 if (mQsPanel != null) {
                     UserSwitcherController controller = mQsPanel.getHost()
                             .getUserSwitcherController();
-                    currentUser = controller.getCurrentUserName(mContext);
+                    if (controller != null) {
+                        currentUser = controller.getCurrentUserName(mContext);
+                    }
                 }
                 if (TextUtils.isEmpty(currentUser)) {
                     text = mContext.getString(R.string.accessibility_multi_user_switch_switcher);
@@ -121,8 +126,4 @@
         return false;
     }
 
-    private boolean opensUserSwitcherWhenClicked() {
-        UserManager um = UserManager.get(getContext());
-        return UserManager.supportsMultipleUsers() && um.isUserSwitcherEnabled();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index d94f122..f5c994a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -74,6 +74,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
@@ -853,11 +854,16 @@
         mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
         mNextAlarmController = new NextAlarmController(mContext);
         mKeyguardMonitor = new KeyguardMonitor();
-        mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor);
-
+        if (UserSwitcherController.isUserSwitcherAvailable(UserManager.get(mContext))) {
+            mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor);
+        }
         mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
                 (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
                 mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);
+        if (mUserSwitcherController != null) {
+            mUserSwitcherController.setKeyguardUserSwitcherAvailable(
+                    mKeyguardUserSwitcher.isEnabled());
+        }
 
 
         // Set up the quick settings tile panel
@@ -1510,7 +1516,8 @@
         // If the user switcher is simple then disable QS during setup because
         // the user intends to use the lock screen user switcher, QS in not needed.
         mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
-                && (!mUserSwitcherController.isSimpleUserSwitcher() || mUserSetup));
+                && (mUserSetup || mUserSwitcherController == null
+                        || !mUserSwitcherController.isSimpleUserSwitcher()));
         mShadeUpdates.check();
     }
 
@@ -2123,6 +2130,9 @@
 
     public void setQsExpanded(boolean expanded) {
         mStatusBarWindowManager.setQsExpanded(expanded);
+        if (mUserSwitcherController != null) {
+            mUserSwitcherController.setQsExpanded(expanded);
+        }
     }
 
     public boolean isGoingToNotificationShade() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index c71bccd..0392e0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -58,7 +58,9 @@
     public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
             KeyguardStatusBarView statusBarView, NotificationPanelView panelView,
             UserSwitcherController userSwitcherController) {
-        if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON) {
+        boolean keyguardUserSwitcherEnabled =
+                context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON;
+        if (userSwitcherController != null && keyguardUserSwitcherEnabled) {
             mUserSwitcherContainer = (Container) userSwitcher.inflate();
             mUserSwitcher = (ViewGroup)
                     mUserSwitcherContainer.findViewById(R.id.keyguard_user_switcher_inner);
@@ -225,6 +227,10 @@
         }
     };
 
+    public boolean isEnabled() {
+        return mUserSwitcherContainer != null;
+    }
+
     public static class Adapter extends UserSwitcherController.BaseUserAdapter implements
             View.OnClickListener {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index d50e39f..a8d4f13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -23,6 +23,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
@@ -131,8 +132,11 @@
         final int userId = userInfo.id;
         final boolean isGuest = userInfo.isGuest();
         final String userName = userInfo.name;
-        final int avatarSize
-                = mContext.getResources().getDimensionPixelSize(R.dimen.max_avatar_size);
+
+        final Resources res = mContext.getResources();
+        final int avatarSize = Math.max(
+                res.getDimensionPixelSize(R.dimen.multi_user_avatar_expanded_size),
+                res.getDimensionPixelSize(R.dimen.multi_user_avatar_keyguard_size));
 
         final Context context = currentUserContext;
         mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
@@ -160,8 +164,9 @@
                     // Try and read the display name from the local profile
                     final Cursor cursor = context.getContentResolver().query(
                             ContactsContract.Profile.CONTENT_URI, new String[] {
-                            ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME},
-                            null, null, null);
+                                    ContactsContract.CommonDataKinds.Phone._ID,
+                                    ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
+                            }, null, null, null);
                     if (cursor != null) {
                         try {
                             if (cursor.moveToFirst()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 5c7909a..5f6c399 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -78,6 +78,9 @@
     private boolean mSimpleUserSwitcher;
     private boolean mAddUsersWhenLocked;
 
+    private boolean mKeyguardUserSwitcherAvailable;
+    private boolean mQsExpanded;
+
     public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor) {
         mContext = context;
         mGuestResumeSessionReceiver.register(context);
@@ -116,16 +119,18 @@
      */
     @SuppressWarnings("unchecked")
     private void refreshUsers(int forcePictureLoadForId) {
-
-        SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
-        final int N = mUsers.size();
-        for (int i = 0; i < N; i++) {
-            UserRecord r = mUsers.get(i);
-            if (r == null || r.info == null
-                    || r.info.id == forcePictureLoadForId || r.picture == null) {
-                continue;
+        SparseArray<Bitmap> bitmaps = null;
+        if (allowCachingOfBitmaps()) {
+            bitmaps = new SparseArray<>(mUsers.size());
+            final int N = mUsers.size();
+            for (int i = 0; i < N; i++) {
+                UserRecord r = mUsers.get(i);
+                if (r == null || r.info == null
+                        || r.info.id == forcePictureLoadForId || r.picture == null) {
+                    continue;
+                }
+                bitmaps.put(r.info.id, r.picture);
             }
-            bitmaps.put(r.info.id, r.picture);
         }
 
         final boolean addUsersWhenLocked = mAddUsersWhenLocked;
@@ -151,13 +156,15 @@
                                 true /* isGuest */, isCurrent, false /* isAddUser */,
                                 false /* isRestricted */);
                     } else if (info.supportsSwitchTo()) {
-                        Bitmap picture = bitmaps.get(info.id);
-                        if (picture == null) {
-                            picture = mUserManager.getUserIcon(info.id);
-                        }
-                        if (picture != null) {
-                            picture = BitmapHelper.createCircularClip(
-                                    picture, avatarSize, avatarSize);
+                        Bitmap picture = bitmaps != null ? bitmaps.get(info.id) : null;
+                        if (picture == null && allowCachingOfBitmaps()) {
+                            Bitmap loadedPicture = mUserManager.getUserIcon(info.id);
+
+                            if (loadedPicture != null) {
+                                picture = BitmapHelper.createCircularClip(
+                                        loadedPicture, avatarSize, avatarSize);
+                                loadedPicture.recycle();
+                            }
                         }
                         int index = isCurrent ? 0 : records.size();
                         records.add(index, new UserRecord(info, picture, false /* isGuest */,
@@ -552,6 +559,32 @@
         }
     }
 
+    /**
+     * Notify if the keyguard user switcher is available.
+     */
+    public void setKeyguardUserSwitcherAvailable(boolean available) {
+        boolean oldShouldCacheBitmaps = allowCachingOfBitmaps();
+        mKeyguardUserSwitcherAvailable = available;
+        if (allowCachingOfBitmaps() != oldShouldCacheBitmaps) {
+            refreshUsers(UserHandle.USER_NULL);
+        }
+    }
+
+    /**
+     * Notify if the quick settings are expanded.
+     */
+    public void setQsExpanded(boolean qsExpanded) {
+        boolean oldShouldCacheBitmaps = allowCachingOfBitmaps();
+        mQsExpanded = qsExpanded;
+        if (allowCachingOfBitmaps() != oldShouldCacheBitmaps) {
+            refreshUsers(UserHandle.USER_NULL);
+        }
+    }
+
+    private boolean allowCachingOfBitmaps() {
+        return mQsExpanded || mKeyguardUserSwitcherAvailable;
+    }
+
     private final class AddUserDialog extends SystemUIDialog implements
             DialogInterface.OnClickListener {
 
@@ -589,4 +622,9 @@
             }
         }
     }
+
+    public static boolean isUserSwitcherAvailable(UserManager um) {
+        return UserManager.supportsMultipleUsers() && um.isUserSwitcherEnabled();
+    }
+
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d311ad9..6d9f0c5 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1183,7 +1183,7 @@
     static final int SERVICE_TIMEOUT_MSG = 12;
     static final int UPDATE_TIME_ZONE = 13;
     static final int SHOW_UID_ERROR_MSG = 14;
-    static final int IM_FEELING_LUCKY_MSG = 15;
+    static final int SHOW_FINGERPRINT_ERROR_MSG = 15;
     static final int PROC_START_TIMEOUT_MSG = 20;
     static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
     static final int KILL_APPLICATION_MSG = 22;
@@ -1212,13 +1212,13 @@
     static final int FINISH_BOOTING_MSG = 45;
     static final int START_USER_SWITCH_MSG = 46;
     static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47;
+    static final int DISMISS_DIALOG_MSG = 48;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
     static final int FIRST_COMPAT_MODE_MSG = 300;
     static final int FIRST_SUPERVISOR_STACK_MSG = 100;
 
-    AlertDialog mUidAlert;
     CompatModeDialog mCompatModeDialog;
     long mLastMemUsageReportTime = 0;
 
@@ -1447,27 +1447,27 @@
                 }
             } break;
             case SHOW_UID_ERROR_MSG: {
-                String title = "System UIDs Inconsistent";
-                String text = "UIDs on the system are inconsistent, you need to wipe your"
-                        + " data partition or your device will be unstable.";
-                Log.e(TAG, title + ": " + text);
                 if (mShowDialogs) {
-                    // XXX This is a temporary dialog, no need to localize.
                     AlertDialog d = new BaseErrorDialog(mContext);
                     d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
                     d.setCancelable(false);
-                    d.setTitle(title);
-                    d.setMessage(text);
-                    d.setButton(DialogInterface.BUTTON_POSITIVE, "I'm Feeling Lucky",
-                            mHandler.obtainMessage(IM_FEELING_LUCKY_MSG));
-                    mUidAlert = d;
+                    d.setTitle(mContext.getText(R.string.android_system_label));
+                    d.setMessage(mContext.getText(R.string.system_error_wipe_data));
+                    d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok),
+                            mHandler.obtainMessage(DISMISS_DIALOG_MSG, d));
                     d.show();
                 }
             } break;
-            case IM_FEELING_LUCKY_MSG: {
-                if (mUidAlert != null) {
-                    mUidAlert.dismiss();
-                    mUidAlert = null;
+            case SHOW_FINGERPRINT_ERROR_MSG: {
+                if (mShowDialogs) {
+                    AlertDialog d = new BaseErrorDialog(mContext);
+                    d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+                    d.setCancelable(false);
+                    d.setTitle(mContext.getText(R.string.android_system_label));
+                    d.setMessage(mContext.getText(R.string.system_error_manufacturer));
+                    d.setButton(DialogInterface.BUTTON_POSITIVE, mContext.getText(R.string.ok),
+                            mHandler.obtainMessage(DISMISS_DIALOG_MSG, d));
+                    d.show();
                 }
             } break;
             case PROC_START_TIMEOUT_MSG: {
@@ -1727,6 +1727,11 @@
                 }
                 break;
             }
+            case DISMISS_DIALOG_MSG: {
+                final Dialog d = (Dialog) msg.obj;
+                d.dismiss();
+                break;
+            }
             }
         }
     };
@@ -1776,7 +1781,8 @@
                     }
                 }
 
-                int i=0, num=0;
+                int i = 0;
+                int num = 0;
                 long[] tmp = new long[1];
                 do {
                     ProcessRecord proc;
@@ -11250,13 +11256,18 @@
 
             try {
                 if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
-                    Message msg = Message.obtain();
-                    msg.what = SHOW_UID_ERROR_MSG;
-                    mHandler.sendMessage(msg);
+                    Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
+                            + " data partition or your device will be unstable.");
+                    mHandler.obtainMessage(SHOW_UID_ERROR_MSG).sendToTarget();
                 }
             } catch (RemoteException e) {
             }
 
+            if (!Build.isFingerprintConsistent()) {
+                Slog.e(TAG, "Build fingerprint is not consistent, warning user");
+                mHandler.obtainMessage(SHOW_FINGERPRINT_ERROR_MSG).sendToTarget();
+            }
+
             long ident = Binder.clearCallingIdentity();
             try {
                 Intent intent = new Intent(Intent.ACTION_USER_STARTED);
@@ -13962,7 +13973,9 @@
 
         ArrayList<MemItem> procMems = new ArrayList<MemItem>();
         final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
-        long nativePss=0, dalvikPss=0, otherPss=0;
+        long nativePss = 0;
+        long dalvikPss = 0;
+        long otherPss = 0;
         long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
 
         long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0cf22493..db0f53b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1298,7 +1298,12 @@
                 if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
                     return false;
                 }
+
+                // We remember deleted user IDs to prevent them from being
+                // reused during the current boot; they can still be reused
+                // after a reboot.
                 mRemovingUserIds.put(userHandle, true);
+
                 try {
                     mAppOpsService.removeUser(userHandle);
                 } catch (RemoteException e) {
@@ -1387,18 +1392,6 @@
         // Remove this user from the list
         mUsers.remove(userHandle);
 
-        // Have user ID linger for several seconds to let external storage VFS
-        // cache entries expire. This must be greater than the 'entry_valid'
-        // timeout used by the FUSE daemon.
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                synchronized (mPackagesLock) {
-                    mRemovingUserIds.delete(userHandle);
-                }
-            }
-        }, MINUTE_IN_MILLIS);
-
         mRestrictionsPinStates.remove(userHandle);
         // Remove user file
         AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));