Merge "Chooser filtering and caller direct share targets" into nyc-dev
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 96e1eaa..36e962e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4727,8 +4727,16 @@
         if (callbacks != null) {
             final int N = callbacks.size();
             for (int i=0; i<N; i++) {
-                performConfigurationChanged(callbacks.get(i), null, config, null,
-                        REPORT_TO_ACTIVITY);
+                ComponentCallbacks2 cb = callbacks.get(i);
+                if (cb instanceof Activity) {
+                    // If callback is an Activity - call corresponding method to consider override
+                    // config and avoid onConfigurationChanged if it hasn't changed.
+                    Activity a = (Activity) cb;
+                    performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
+                            config, REPORT_TO_ACTIVITY);
+                } else {
+                    performConfigurationChanged(cb, null, config, null, REPORT_TO_ACTIVITY);
+                }
             }
         }
     }
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 29a72fd..f1c8c7d 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -31,12 +31,7 @@
             throw new IllegalArgumentException("Path string can not be null.");
         }
         Path path = new Path();
-        boolean hasValidPathData = nParseStringForPath(path.mNativePath, pathString,
-                pathString.length());
-        if (!hasValidPathData) {
-            throw new IllegalArgumentException("Path string: " + pathString +
-                    " does not contain valid path data");
-        }
+        nParseStringForPath(path.mNativePath, pathString, pathString.length());
         return path;
     }
 
@@ -104,7 +99,6 @@
             }
             super.finalize();
         }
-
     }
 
     /**
@@ -123,7 +117,7 @@
     }
 
     // Native functions are defined below.
-    private static native boolean nParseStringForPath(long pathPtr, String pathString,
+    private static native void nParseStringForPath(long pathPtr, String pathString,
             int stringLength);
     private static native void nCreatePathFromPathData(long outPathPtr, long pathData);
     private static native long nCreateEmptyPathData();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c372c30..ddad23a 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1701,6 +1701,14 @@
          */
         public int accessibilityIdOfAnchor = -1;
 
+        /**
+         * The window title isn't kept in sync with what is displayed in the title bar, so we
+         * separately track the currently shown title to provide to accessibility.
+         *
+         * @hide
+         */
+        public CharSequence accessibilityTitle;
+
         public LayoutParams() {
             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             type = TYPE_APPLICATION;
@@ -1749,6 +1757,7 @@
                 title = "";
 
             mTitle = TextUtils.stringOrSpannedString(title);
+            accessibilityTitle = mTitle;
         }
 
         public final CharSequence getTitle() {
@@ -1808,6 +1817,7 @@
             out.writeInt(hasManualSurfaceInsets ? 1 : 0);
             out.writeInt(needsMenuKey);
             out.writeInt(accessibilityIdOfAnchor);
+            TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
         }
 
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1859,6 +1869,7 @@
             hasManualSurfaceInsets = in.readInt() != 0;
             needsMenuKey = in.readInt();
             accessibilityIdOfAnchor = in.readInt();
+            accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1900,6 +1911,8 @@
         /** {@hide} */
         public static final int ACCESSIBILITY_ANCHOR_CHANGED = 1 << 24;
         /** {@hide} */
+        public static final int ACCESSIBILITY_TITLE_CHANGED = 1 << 25;
+        /** {@hide} */
         public static final int EVERYTHING_CHANGED = 0xffffffff;
 
         // internal buffer to backup/restore parameters under compatibility mode.
@@ -2065,6 +2078,13 @@
                 changes |= ACCESSIBILITY_ANCHOR_CHANGED;
             }
 
+            if (!Objects.equals(accessibilityTitle, o.accessibilityTitle)
+                    && o.accessibilityTitle != null) {
+                // NOTE: accessibilityTitle only copied if the originator set one.
+                accessibilityTitle = o.accessibilityTitle;
+                changes |= ACCESSIBILITY_TITLE_CHANGED;
+            }
+
             return changes;
         }
 
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 0f257d7..c64328b 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -516,8 +516,8 @@
         }
         mTitle = title;
         WindowManager.LayoutParams params = getAttributes();
-        if (!TextUtils.equals(title, params.getTitle())) {
-            params.setTitle(title);
+        if (!TextUtils.equals(title, params.accessibilityTitle)) {
+            params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
             dispatchWindowAttributesChanged(getAttributes());
         }
     }
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 6dc251c..1c2d13d 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -53,36 +53,14 @@
     fontFamily->Unref();
 }
 
-static jboolean addSkTypeface(FontFamily* family, SkTypeface* face) {
-    MinikinFont* minikinFont = new MinikinFontSkia(face);
+static jboolean addSkTypeface(FontFamily* family, SkTypeface* face, const void* fontData,
+        size_t fontSize, int ttcIndex) {
+    MinikinFont* minikinFont = new MinikinFontSkia(face, fontData, fontSize, ttcIndex);
     bool result = family->addFont(minikinFont);
     minikinFont->Unref();
     return result;
 }
 
-static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path,
-        jint ttcIndex) {
-    NPE_CHECK_RETURN_ZERO(env, path);
-    ScopedUtfChars str(env, path);
-    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
-    if (face == NULL) {
-        ALOGE("addFont failed to create font %s", str.c_str());
-        return false;
-    }
-    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    return addSkTypeface(fontFamily, face);
-}
-
-static struct {
-    jmethodID mGet;
-    jmethodID mSize;
-} gListClassInfo;
-
-static struct {
-    jfieldID mTag;
-    jfieldID mStyleValue;
-} gAxisClassInfo;
-
 static void release_global_ref(const void* /*data*/, void* context) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     bool needToAttach = (env == NULL);
@@ -106,6 +84,47 @@
     }
 }
 
+static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jobject bytebuf,
+        jint ttcIndex) {
+    NPE_CHECK_RETURN_ZERO(env, bytebuf);
+    const void* fontPtr = env->GetDirectBufferAddress(bytebuf);
+    if (fontPtr == NULL) {
+        ALOGE("addFont failed to create font, buffer invalid");
+        return false;
+    }
+    jlong fontSize = env->GetDirectBufferCapacity(bytebuf);
+    if (fontSize < 0) {
+        ALOGE("addFont failed to create font, buffer size invalid");
+        return false;
+    }
+    jobject fontRef = MakeGlobalRefOrDie(env, bytebuf);
+    SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize,
+            release_global_ref, reinterpret_cast<void*>(fontRef)));
+    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data));
+
+    SkFontMgr::FontParameters params;
+    params.setCollectionIndex(ttcIndex);
+
+    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+    SkTypeface* face = fm->createFromStream(fontData.release(), params);
+    if (face == NULL) {
+        ALOGE("addFont failed to create font");
+        return false;
+    }
+    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
+    return addSkTypeface(fontFamily, face, fontPtr, (size_t)fontSize, ttcIndex);
+}
+
+static struct {
+    jmethodID mGet;
+    jmethodID mSize;
+} gListClassInfo;
+
+static struct {
+    jfieldID mTag;
+    jfieldID mStyleValue;
+} gAxisClassInfo;
+
 static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
         jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
     NPE_CHECK_RETURN_ZERO(env, font);
@@ -133,7 +152,7 @@
         }
     }
 
-    void* fontPtr = env->GetDirectBufferAddress(font);
+    const void* fontPtr = env->GetDirectBufferAddress(font);
     if (fontPtr == NULL) {
         ALOGE("addFont failed to create font, buffer invalid");
         return false;
@@ -159,7 +178,7 @@
         return false;
     }
     FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    MinikinFont* minikinFont = new MinikinFontSkia(face);
+    MinikinFont* minikinFont = new MinikinFontSkia(face, fontPtr, (size_t)fontSize, ttcIndex);
     fontFamily->addFont(minikinFont, FontStyle(weight / 100, isItalic));
     minikinFont->Unref();
     return true;
@@ -191,6 +210,7 @@
         return false;
     }
 
+    size_t bufSize = asset->getLength();
     SkAutoTUnref<SkData> data(SkData::NewWithProc(buf, asset->getLength(), releaseAsset, asset));
     SkMemoryStream* stream = new SkMemoryStream(data);
     // CreateFromStream takes ownership of stream.
@@ -200,7 +220,7 @@
         return false;
     }
     FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    return addSkTypeface(fontFamily, face);
+    return addSkTypeface(fontFamily, face, buf, bufSize, /* ttcIndex */ 0);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -208,7 +228,7 @@
 static const JNINativeMethod gFontFamilyMethods[] = {
     { "nCreateFamily",         "(Ljava/lang/String;I)J", (void*)FontFamily_create },
     { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
-    { "nAddFont",              "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
+    { "nAddFont",              "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont },
     { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
             (void*)FontFamily_addFontWeightStyle },
     { "nAddFontFromAsset",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index b04293e..e5c4a2d 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -176,6 +176,9 @@
     PathParser::ParseResult result;
     PathData data;
     PathParser::getPathDataFromString(&data, &result, pathString, stringLength);
+    if (result.failureOccurred) {
+        doThrowIAE(env, result.failureMessage.c_str());
+    }
     path->mutateStagingProperties()->setData(data);
     env->ReleaseStringUTFChars(inputStr, pathString);
 }
diff --git a/core/jni/android_util_PathParser.cpp b/core/jni/android_util_PathParser.cpp
index 0927120..0c867f1 100644
--- a/core/jni/android_util_PathParser.cpp
+++ b/core/jni/android_util_PathParser.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "jni.h"
+#include "GraphicsJNI.h"
 
 #include <PathParser.h>
 #include <SkPath.h>
@@ -27,7 +28,7 @@
 
 using namespace uirenderer;
 
-static bool parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
+static void parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
         jint strLength) {
     const char* pathString = env->GetStringUTFChars(inputPathStr, NULL);
     SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle);
@@ -36,9 +37,8 @@
     PathParser::parseStringForSkPath(skPath, &result, pathString, strLength);
     env->ReleaseStringUTFChars(inputPathStr, pathString);
     if (result.failureOccurred) {
-        ALOGE(result.failureMessage.c_str());
+        doThrowIAE(env, result.failureMessage.c_str());
     }
-    return !result.failureOccurred;
 }
 
 static long createEmptyPathData(JNIEnv*, jobject) {
@@ -62,7 +62,7 @@
         return reinterpret_cast<jlong>(pathData);
     } else {
         delete pathData;
-        ALOGE(result.failureMessage.c_str());
+        doThrowIAE(env, result.failureMessage.c_str());
         return NULL;
     }
 }
@@ -100,7 +100,7 @@
 }
 
 static const JNINativeMethod gMethods[] = {
-    {"nParseStringForPath", "(JLjava/lang/String;I)Z", (void*)parseStringForPath},
+    {"nParseStringForPath", "(JLjava/lang/String;I)V", (void*)parseStringForPath},
     {"nCreateEmptyPathData", "!()J", (void*)createEmptyPathData},
     {"nCreatePathData", "!(J)J", (void*)createPathData},
     {"nCreatePathDataFromString", "(Ljava/lang/String;I)J", (void*)createPathDataFromStringPath},
diff --git a/core/res/res/drawable/ic_corp_user_badge.xml b/core/res/res/drawable/ic_corp_user_badge.xml
new file mode 100644
index 0000000..23809d5
--- /dev/null
+++ b/core/res/res/drawable/ic_corp_user_badge.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M18,0C8.06,-0 0,8.06 0,18C0,27.94 8.06,36 18,36C27.94,36 36,27.94 36,18C36,8.06 27.94,0 18,0zM15.5,10.5L20.5,10.5L21.75,11.75L21.75,13L24.66,13C25.57,13 26.34,13.74 26.34,14.66L26.34,18C26.34,18.92 25.57,19.66 24.66,19.66L19.66,19.66L19.66,18.41L16.34,18.41L16.34,19.66L11.34,19.66C10.43,19.66 9.66,18.92 9.66,18L9.66,14.66C9.66,13.74 10.43,13 11.34,13L14.25,13L14.25,11.78L15.5,10.5zM15.5,11.75L15.5,13L20.5,13L20.5,11.75L15.5,11.75zM10.5,20.5L16.34,20.5L16.34,21.75L19.66,21.75L19.66,20.5L25.5,20.5L25.5,23.84C25.5,24.76 24.76,25.5 23.84,25.5L12.16,25.5C11.24,25.5 10.5,24.76 10.5,23.84L10.5,20.5z"/>
+</vector>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d0414c3..592904e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1265,6 +1265,7 @@
   <java-symbol type="drawable" name="ic_corp_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_off" />
   <java-symbol type="drawable" name="ic_corp_icon_badge" />
+  <java-symbol type="drawable" name="ic_corp_user_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_no_background" />
   <java-symbol type="drawable" name="ic_corp_icon" />
   <java-symbol type="drawable" name="ic_corp_statusbar_icon" />
diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk
index be2e6bf..702218c 100644
--- a/core/tests/notificationtests/Android.mk
+++ b/core/tests/notificationtests/Android.mk
@@ -11,6 +11,9 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := NotificationStressTests
 
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    ub-uiautomator
+
 include $(BUILD_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/notificationtests/src/android/app/NotificationStressTest.java b/core/tests/notificationtests/src/android/app/NotificationStressTest.java
index 4cb617e..6e86c37 100644
--- a/core/tests/notificationtests/src/android/app/NotificationStressTest.java
+++ b/core/tests/notificationtests/src/android/app/NotificationStressTest.java
@@ -16,15 +16,19 @@
 
 package android.app;
 
-
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
+import android.os.RemoteException;
 import android.os.SystemClock;
+import android.support.test.uiautomator.UiDevice;
 import android.test.InstrumentationTestCase;
 import android.test.RepetitiveTest;
-import android.test.TimedTest;
+import android.util.Log;
 
+import java.lang.InterruptedException;
+import java.lang.reflect.Method;
 import java.util.Random;
 
 /**
@@ -34,51 +38,78 @@
 public class NotificationStressTest extends InstrumentationTestCase {
 
     private static final int NUM_ITERATIONS = 200;
+    private static final int NUM_ITERATIONS_2 = 30;
+    private static final int LONG_TIMEOUT = 2000;
+    // 50 notifications per app: defined as Variable MAX_PACKAGE_NOTIFICATIONS in
+    // NotificationManagerService.java
+    private static final int MAX_NOTIFCATIONS = 50;
     private static final int[] ICONS = new int[] {
-        android.R.drawable.stat_notify_call_mute,
-        android.R.drawable.stat_notify_chat,
-        android.R.drawable.stat_notify_error,
-        android.R.drawable.stat_notify_missed_call,
-        android.R.drawable.stat_notify_more,
-        android.R.drawable.stat_notify_sdcard,
-        android.R.drawable.stat_notify_sdcard_prepare,
-        android.R.drawable.stat_notify_sdcard_usb,
-        android.R.drawable.stat_notify_sync,
-        android.R.drawable.stat_notify_sync_noanim,
-        android.R.drawable.stat_notify_voicemail,
+            android.R.drawable.stat_notify_call_mute,
+            android.R.drawable.stat_notify_chat,
+            android.R.drawable.stat_notify_error,
+            android.R.drawable.stat_notify_missed_call,
+            android.R.drawable.stat_notify_more,
+            android.R.drawable.stat_notify_sdcard,
+            android.R.drawable.stat_notify_sdcard_prepare,
+            android.R.drawable.stat_notify_sdcard_usb,
+            android.R.drawable.stat_notify_sync,
+            android.R.drawable.stat_notify_sync_noanim,
+            android.R.drawable.stat_notify_voicemail,
     };
 
     private final Random mRandom = new Random();
     private Context mContext;
     private NotificationManager mNotificationManager;
-    private int notifyId = 0;
+    private UiDevice mDevice = null;
+    private int mNotifyId = 0;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        mDevice = UiDevice.getInstance(getInstrumentation());
         mContext = getInstrumentation().getContext();
         mNotificationManager = (NotificationManager) mContext.getSystemService(
                 Context.NOTIFICATION_SERVICE);
+        mDevice.setOrientationNatural();
+        mNotificationManager.cancelAll();
     }
 
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
+        mDevice.unfreezeRotation();
         mNotificationManager.cancelAll();
     }
 
-    @RepetitiveTest(numIterations=NUM_ITERATIONS)
+    @RepetitiveTest(numIterations = NUM_ITERATIONS)
     public void testNotificationStress() {
         // Cancel one of every five notifications to vary load on notification manager
-        if (notifyId % 5 == 4) {
-            mNotificationManager.cancel(notifyId - 4);
+        if (mNotifyId % 5 == 4) {
+            mNotificationManager.cancel(mNotifyId - 4);
         }
-        sendNotification(notifyId++, "testNotificationStressNotify");
+        sendNotification(mNotifyId++, "testNotificationStressNotify");
+    }
+
+    @RepetitiveTest(numIterations = NUM_ITERATIONS_2)
+    public void testNotificationsWithShadeStress() throws Exception {
+        mDevice.openNotification();
+        Thread.sleep(LONG_TIMEOUT);
+        for (int j = 0; j < MAX_NOTIFCATIONS; j++) {
+            sendNotification(mNotifyId++, "testNotificationStressNotify");
+        }
+        Thread.sleep(500);
+        assertTrue(mNotificationManager.getActiveNotifications().length == MAX_NOTIFCATIONS);
+        for (int j = 0; j < MAX_NOTIFCATIONS; j++) {
+            mNotificationManager.cancel(--mNotifyId);
+        }
+        if (isLockScreen()) {
+            fail("Notification stress test failed, back to lockscreen");
+        }
     }
 
     private void sendNotification(int id, CharSequence text) {
         // Fill in arbitrary content
-        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
+        Intent intent = new Intent(Intent.ACTION_VIEW);
         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
         CharSequence title = text + " " + id;
         CharSequence subtitle = String.valueOf(System.currentTimeMillis());
@@ -90,8 +121,19 @@
                 .setContentTitle(title)
                 .setContentText(subtitle)
                 .setContentIntent(pendingIntent)
+                .setPriority(Notification.PRIORITY_HIGH)
                 .build();
         mNotificationManager.notify(id, notification);
         SystemClock.sleep(10);
     }
+
+    private boolean isLockScreen() {
+        KeyguardManager myKM = (KeyguardManager) mContext
+                .getSystemService(Context.KEYGUARD_SERVICE);
+        if (myKM.inKeyguardRestrictedInputMode()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
 }
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index f741e3c..e48bf79 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,8 +17,12 @@
 package android.graphics;
 
 import android.content.res.AssetManager;
+import android.util.Log;
 
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
 import java.util.List;
 
 /**
@@ -27,6 +31,9 @@
  * @hide
  */
 public class FontFamily {
+
+    private static String TAG = "FontFamily";
+
     /**
      * @hide
      */
@@ -62,7 +69,15 @@
     }
 
     public boolean addFont(String path, int ttcIndex) {
-        return nAddFont(mNativePtr, path, ttcIndex);
+        try (FileInputStream file = new FileInputStream(path)) {
+            FileChannel fileChannel = file.getChannel();
+            long fontSize = fileChannel.size();
+            ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+            return nAddFont(mNativePtr, fontBuffer, ttcIndex);
+        } catch (IOException e) {
+            Log.e(TAG, "Error mapping font file " + path);
+            return false;
+        }
     }
 
     public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontListParser.Axis> axes,
@@ -76,7 +91,7 @@
 
     private static native long nCreateFamily(String lang, int variant);
     private static native void nUnrefFamily(long nativePtr);
-    private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex);
+    private static native boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex);
     private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
             int ttcIndex, List<FontListParser.Axis> listOfAxis,
             int weight, boolean isItalic);
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index 4e9ac9c..7e85333 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -156,6 +156,12 @@
     return;
 }
 
+bool PathParser::isVerbValid(char verb) {
+    verb = tolower(verb);
+    return verb == 'a' || verb == 'c' || verb == 'h' || verb == 'l' || verb == 'm' || verb == 'q'
+            || verb == 's' || verb == 't' || verb == 'v' || verb == 'z';
+}
+
 void PathParser::getPathDataFromString(PathData* data, ParseResult* result,
         const char* pathStr, size_t strLen) {
     if (pathStr == NULL) {
@@ -171,6 +177,12 @@
         end = nextStart(pathStr, strLen, end);
         std::vector<float> points;
         getFloats(&points, result, pathStr, start, end);
+        if (!isVerbValid(pathStr[start])) {
+            result->failureOccurred = true;
+            result->failureMessage = "Invalid pathData. Failure occurred at position "
+                    + std::to_string(start) + " of path: " + pathStr;
+        }
+        // If either verb or points is not valid, return immediately.
         if (result->failureOccurred) {
             return;
         }
@@ -182,10 +194,15 @@
     }
 
     if ((end - start) == 1 && start < strLen) {
+        if (!isVerbValid(pathStr[start])) {
+            result->failureOccurred = true;
+            result->failureMessage = "Invalid pathData. Failure occurred at position "
+                    + std::to_string(start) + " of path: " + pathStr;
+            return;
+        }
         data->verbs.push_back(pathStr[start]);
         data->verbSizes.push_back(0);
     }
-    return;
 }
 
 void PathParser::dump(const PathData& data) {
@@ -218,7 +235,8 @@
     // Check if there is valid data coming out of parsing the string.
     if (pathData.verbs.size() == 0) {
         result->failureOccurred = true;
-        result->failureMessage = "No verbs found in the string for pathData";
+        result->failureMessage = "No verbs found in the string for pathData: ";
+        result->failureMessage += pathStr;
         return;
     }
     VectorDrawableUtils::verbsToPath(skPath, pathData);
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index c4bbb74..180a7a3 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -44,6 +44,7 @@
     ANDROID_API static void getPathDataFromString(PathData* outData, ParseResult* result,
             const char* pathStr, size_t strLength);
     static void dump(const PathData& data);
+    static bool isVerbValid(char verb);
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index b9e3358..b39de26 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -22,8 +22,9 @@
 
 namespace android {
 
-MinikinFontSkia::MinikinFontSkia(SkTypeface *typeface) :
-    mTypeface(typeface) {
+MinikinFontSkia::MinikinFontSkia(SkTypeface* typeface, const void* fontData, size_t fontSize,
+        int ttcIndex) :
+    mTypeface(typeface), mFontData(fontData), mFontSize(fontSize), mTtcIndex(ttcIndex) {
 }
 
 MinikinFontSkia::~MinikinFontSkia() {
@@ -66,22 +67,38 @@
     bounds->mBottom = skBounds.fBottom;
 }
 
-bool MinikinFontSkia::GetTable(uint32_t tag, uint8_t *buf, size_t *size) {
-    if (buf == NULL) {
-        const size_t tableSize = mTypeface->getTableSize(tag);
-        *size = tableSize;
-        return tableSize != 0;
-    } else {
-        const size_t actualSize = mTypeface->getTableData(tag, 0, *size, buf);
-        *size = actualSize;
-        return actualSize != 0;
+const void* MinikinFontSkia::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) {
+    // we don't have a buffer to the font data, copy to own buffer
+    const size_t tableSize = mTypeface->getTableSize(tag);
+    *size = tableSize;
+    if (tableSize == 0) {
+        return nullptr;
     }
+    void* buf = malloc(tableSize);
+    if (buf == nullptr) {
+        return nullptr;
+    }
+    mTypeface->getTableData(tag, 0, tableSize, buf);
+    *destroy = free;
+    return buf;
 }
 
 SkTypeface *MinikinFontSkia::GetSkTypeface() const {
     return mTypeface;
 }
 
+const void* MinikinFontSkia::GetFontData() const {
+    return mFontData;
+}
+
+size_t MinikinFontSkia::GetFontSize() const {
+    return mFontSize;
+}
+
+int MinikinFontSkia::GetFontIndex() const {
+    return mTtcIndex;
+}
+
 int32_t MinikinFontSkia::GetUniqueId() const {
     return mTypeface->uniqueID();
 }
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index 1d50168..dbc65f9 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -28,7 +28,8 @@
 class ANDROID_API MinikinFontSkia : public MinikinFont {
 public:
     // Note: this takes ownership of the reference (will unref on dtor)
-    explicit MinikinFontSkia(SkTypeface *typeface);
+    explicit MinikinFontSkia(SkTypeface *typeface, const void* fontData, size_t fontSize,
+        int ttcIndex);
 
     ~MinikinFontSkia();
 
@@ -38,20 +39,30 @@
     void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
         const MinikinPaint &paint) const;
 
-    // If buf is NULL, just update size
-    bool GetTable(uint32_t tag, uint8_t *buf, size_t *size);
+    const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
 
     int32_t GetUniqueId() const;
 
     SkTypeface* GetSkTypeface() const;
 
+    // Access to underlying raw font bytes
+    const void* GetFontData() const;
+    size_t GetFontSize() const;
+    int GetFontIndex() const;
+
     static uint32_t packPaintFlags(const SkPaint* paint);
     static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags);
 
     // set typeface and fake bold/italic parameters
     static void populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery);
 private:
-    SkTypeface *mTypeface;
+    SkTypeface* mTypeface;
+
+    // A raw pointer to the font data - it should be owned by some other object with
+    // lifetime at least as long as this object.
+    const void* mFontData;
+    size_t mFontSize;
+    int mTtcIndex;
 };
 
 }  // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index fa8ad5d..c583988 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -66,7 +66,10 @@
         ALOGD("makeFontCollection adding %s", fn);
         SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
         if (skFace != NULL) {
-            MinikinFont *font = new MinikinFontSkia(skFace);
+            // TODO: might be a nice optimization to get access to the underlying font
+            // data, but would require us opening the file ourselves and passing that
+            // to the appropriate Create method of SkTypeface.
+            MinikinFont *font = new MinikinFontSkia(skFace, NULL, 0, 0);
             family->addFont(font);
             font->Unref();
         } else {
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 7208547..796169e 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -231,11 +231,12 @@
 };
 
 const StringPath sStringPaths[] = {
-    {"3e...3", false},
-    {"L.M.F.A.O", false},
-    {"m 1 1", true},
-    {"z", true},
-    {"1-2e34567", false}
+    {"3e...3", false}, // Not starting with a verb and ill-formatted float
+    {"L.M.F.A.O", false}, // No floats following verbs
+    {"m 1 1", true}, // Valid path data
+    {"z", true}, // Valid path data
+    {"1-2e34567", false}, // Not starting with a verb and ill-formatted float
+    {"f 4 5", false} // Invalid verb
 };
 
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 586f269..ca0b86a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -15,7 +15,7 @@
 import android.os.BatteryManager;
 import android.os.UserManager;
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import java.text.NumberFormat;
 
@@ -73,21 +73,22 @@
     /**
      * Returns a circular icon for a user.
      */
-    public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
+    public static UserIconDrawable getUserIcon(Context context, UserManager um, UserInfo user) {
+        final int iconSize = UserIconDrawable.getSizeForList(context);
         if (user.isManagedProfile()) {
             // We use predefined values for managed profiles
             Bitmap b = BitmapFactory.decodeResource(context.getResources(),
                     com.android.internal.R.drawable.ic_corp_icon);
-            return CircleFramedDrawable.getInstance(context, b);
+            return new UserIconDrawable(iconSize).setIcon(b).bake();
         }
         if (user.iconPath != null) {
             Bitmap icon = um.getUserIcon(user.id);
             if (icon != null) {
-                return CircleFramedDrawable.getInstance(context, icon);
+                return new UserIconDrawable(iconSize).setIcon(icon).bake();
             }
         }
-        return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
-                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
+        return new UserIconDrawable(iconSize).setIconDrawable(
+                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)).bake();
     }
 
     /** Formats the ratio of amount/total as a percentage. */
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
new file mode 100644
index 0000000..32478a7
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.drawable;
+
+import android.annotation.NonNull;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.R;
+
+/**
+ * Converts the user avatar icon to a circularly clipped one with an optional badge and frame
+ */
+public class UserIconDrawable extends Drawable implements Drawable.Callback {
+
+    private Drawable mUserDrawable;
+    private Bitmap mUserIcon;
+    private Bitmap mBitmap; // baked representation. Required for transparent border around badge
+    private final Paint mIconPaint = new Paint();
+    private final Paint mPaint = new Paint();
+    private final Matrix mIconMatrix = new Matrix();
+    private float mIntrinsicRadius;
+    private float mDisplayRadius;
+    private float mPadding = 0;
+    private int mSize = 0; // custom "intrinsic" size for this drawable if non-zero
+    private boolean mInvalidated = true;
+    private ColorStateList mTintColor = null;
+    private PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_ATOP;
+
+    private float mFrameWidth;
+    private float mFramePadding;
+    private ColorStateList mFrameColor = null;
+    private Paint mFramePaint;
+
+    private Drawable mBadge;
+    private Paint mClearPaint;
+    private float mBadgeRadius;
+    private float mBadgeMargin;
+
+    /**
+     * Gets the system default managed-user badge as a drawable
+     * @param context
+     * @return drawable containing just the badge
+     */
+    public static Drawable getManagedUserBadgeDrawable(Context context) {
+        int displayDensity = context.getResources().getDisplayMetrics().densityDpi;
+        return context.getResources().getDrawableForDensity(
+                com.android.internal.R.drawable.ic_corp_user_badge,
+                displayDensity, context.getTheme());
+    }
+
+    /**
+     * Gets the preferred list-item size of this drawable.
+     * @param context
+     * @return size in pixels
+     */
+    public static int getSizeForList(Context context) {
+        return (int) context.getResources().getDimension(R.dimen.circle_avatar_size);
+    }
+
+    public UserIconDrawable() {
+        this(0);
+    }
+
+    /**
+     * Use this constructor if the drawable is intended to be placed in listviews
+     * @param intrinsicSize if 0, the intrinsic size will come from the icon itself
+     */
+    public UserIconDrawable(int intrinsicSize) {
+        super();
+        mIconPaint.setAntiAlias(true);
+        mIconPaint.setFilterBitmap(true);
+        mPaint.setFilterBitmap(true);
+        mPaint.setAntiAlias(true);
+        if (intrinsicSize > 0) {
+            setBounds(0, 0, intrinsicSize, intrinsicSize);
+            setIntrinsicSize(intrinsicSize);
+        }
+        setIcon(null);
+    }
+
+    public UserIconDrawable setIcon(Bitmap icon) {
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+            mUserDrawable = null;
+        }
+        mUserIcon = icon;
+        if (mUserIcon == null) {
+            mIconPaint.setShader(null);
+            mBitmap = null;
+        } else {
+            mIconPaint.setShader(new BitmapShader(icon, Shader.TileMode.CLAMP,
+                    Shader.TileMode.CLAMP));
+        }
+        onBoundsChange(getBounds());
+        return this;
+    }
+
+    public UserIconDrawable setIconDrawable(Drawable icon) {
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+        }
+        mUserIcon = null;
+        mUserDrawable = icon;
+        if (mUserDrawable == null) {
+            mBitmap = null;
+        } else {
+            mUserDrawable.setCallback(this);
+        }
+        onBoundsChange(getBounds());
+        return this;
+    }
+
+    public UserIconDrawable setBadge(Drawable badge) {
+        mBadge = badge;
+        if (mBadge != null) {
+            if (mClearPaint == null) {
+                mClearPaint = new Paint();
+                mClearPaint.setAntiAlias(true);
+                mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+                mClearPaint.setStyle(Paint.Style.FILL);
+            }
+            // update metrics
+            onBoundsChange(getBounds());
+        } else {
+            invalidateSelf();
+        }
+        return this;
+    }
+
+    public UserIconDrawable setBadgeIfManagedUser(Context context, int userId) {
+        Drawable badge = null;
+        boolean isManaged = context.getSystemService(DevicePolicyManager.class)
+                .getProfileOwnerAsUser(userId) != null;
+        if (isManaged) {
+            badge = getManagedUserBadgeDrawable(context);
+        }
+        return setBadge(badge);
+    }
+
+    public void setBadgeRadius(float radius) {
+        mBadgeRadius = radius;
+        onBoundsChange(getBounds());
+    }
+
+    public void setBadgeMargin(float margin) {
+        mBadgeMargin = margin;
+        onBoundsChange(getBounds());
+    }
+
+    /**
+     * Sets global padding of icon/frame. Doesn't effect the badge.
+     * @param padding
+     */
+    public void setPadding(float padding) {
+        mPadding = padding;
+        onBoundsChange(getBounds());
+    }
+
+    private void initFramePaint() {
+        if (mFramePaint == null) {
+            mFramePaint = new Paint();
+            mFramePaint.setStyle(Paint.Style.STROKE);
+            mFramePaint.setAntiAlias(true);
+        }
+    }
+
+    public void setFrameWidth(float width) {
+        initFramePaint();
+        mFrameWidth = width;
+        mFramePaint.setStrokeWidth(width);
+        onBoundsChange(getBounds());
+    }
+
+    public void setFramePadding(float padding) {
+        initFramePaint();
+        mFramePadding = padding;
+        onBoundsChange(getBounds());
+    }
+
+    public void setFrameColor(int color) {
+        initFramePaint();
+        mFramePaint.setColor(color);
+        invalidateSelf();
+    }
+
+    public void setFrameColor(ColorStateList colorList) {
+        initFramePaint();
+        mFrameColor = colorList;
+        invalidateSelf();
+    }
+
+    /**
+     * This sets the "intrinsic" size of this drawable. Useful for views which use the drawable's
+     * intrinsic size for layout. It is independent of the bounds.
+     * @param size if 0, the intrinsic size will be set to the displayed icon's size
+     */
+    public void setIntrinsicSize(int size) {
+        mSize = size;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mInvalidated) {
+            rebake();
+        }
+        if (mBitmap != null) {
+            if (mTintColor == null) {
+                mPaint.setColorFilter(null);
+            } else {
+                int color = mTintColor.getColorForState(getState(), mTintColor.getDefaultColor());
+                if (mPaint.getColorFilter() == null) {
+                    mPaint.setColorFilter(new PorterDuffColorFilter(color, mTintMode));
+                } else {
+                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setMode(mTintMode);
+                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setColor(color);
+                }
+            }
+
+            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+    }
+
+    @Override
+    public void setTintList(ColorStateList tintList) {
+        mTintColor = tintList;
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void setTintMode(@NonNull PorterDuff.Mode mode) {
+        mTintMode = mode;
+        super.invalidateSelf();
+    }
+
+    /**
+     * This 'bakes' the current state of this icon into a bitmap and removes/recycles the source
+     * bitmap/drawable. Use this when no more changes will be made and an intrinsic size is set.
+     * This effectively turns this into a static drawable.
+     */
+    public UserIconDrawable bake() {
+        if (mSize <= 0) {
+            throw new IllegalStateException("Baking requires an explicit intrinsic size");
+        }
+        onBoundsChange(new Rect(0, 0, mSize, mSize));
+        rebake();
+        mFrameColor = null;
+        mFramePaint = null;
+        mClearPaint = null;
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+            mUserDrawable = null;
+        } else if (mUserIcon != null) {
+            mUserIcon.recycle();
+            mUserIcon = null;
+        }
+        return this;
+    }
+
+    private void rebake() {
+        mInvalidated = false;
+
+        if (mBitmap == null || (mUserDrawable == null && mUserIcon == null)) {
+            return;
+        }
+
+        final Canvas canvas = new Canvas(mBitmap);
+        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+        if(mUserDrawable != null) {
+            mUserDrawable.draw(canvas);
+        } else if (mUserIcon != null) {
+            int saveId = canvas.save();
+            canvas.concat(mIconMatrix);
+            canvas.drawCircle(mUserIcon.getWidth() * 0.5f, mUserIcon.getHeight() * 0.5f,
+                    mIntrinsicRadius, mIconPaint);
+            canvas.restoreToCount(saveId);
+        }
+
+        if (mFrameColor != null) {
+            mFramePaint.setColor(mFrameColor.getColorForState(getState(), Color.TRANSPARENT));
+        }
+        if ((mFrameWidth + mFramePadding) > 0.001f) {
+            float radius = mDisplayRadius - mPadding - mFrameWidth * 0.5f;
+            canvas.drawCircle(getBounds().exactCenterX(), getBounds().exactCenterY(),
+                    radius, mFramePaint);
+        }
+
+        if ((mBadge != null) && (mBadgeRadius > 0.001f)) {
+            final float badgeDiameter = mBadgeRadius * 2f;
+            final float badgeTop = mBitmap.getHeight() - badgeDiameter;
+            float badgeLeft = mBitmap.getWidth() - badgeDiameter;
+
+            mBadge.setBounds((int) badgeLeft, (int) badgeTop,
+                    (int) (badgeLeft + badgeDiameter), (int) (badgeTop + badgeDiameter));
+
+            final float borderRadius = mBadge.getBounds().width() * 0.5f + mBadgeMargin;
+            canvas.drawCircle(badgeLeft + mBadgeRadius, badgeTop + mBadgeRadius,
+                    borderRadius, mClearPaint);
+
+            mBadge.draw(canvas);
+        }
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        if (bounds.isEmpty() || (mUserIcon == null && mUserDrawable == null)) {
+            return;
+        }
+
+        // re-create bitmap if applicable
+        float newDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
+        int size = (int) (newDisplayRadius * 2);
+        if (mBitmap == null || size != ((int) (mDisplayRadius * 2))) {
+            mDisplayRadius = newDisplayRadius;
+            if (mBitmap != null) {
+                mBitmap.recycle();
+            }
+            mBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        }
+
+        // update metrics
+        mDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
+        final float iconRadius = mDisplayRadius - mFrameWidth - mFramePadding - mPadding;
+        RectF dstRect = new RectF(bounds.exactCenterX() - iconRadius,
+                                  bounds.exactCenterY() - iconRadius,
+                                  bounds.exactCenterX() + iconRadius,
+                                  bounds.exactCenterY() + iconRadius);
+        if (mUserDrawable != null) {
+            Rect rounded = new Rect();
+            dstRect.round(rounded);
+            mIntrinsicRadius = Math.min(mUserDrawable.getIntrinsicWidth(),
+                                        mUserDrawable.getIntrinsicHeight()) * 0.5f;
+            mUserDrawable.setBounds(rounded);
+        } else if (mUserIcon != null) {
+            // Build square-to-square transformation matrix
+            final float iconCX = mUserIcon.getWidth() * 0.5f;
+            final float iconCY = mUserIcon.getHeight() * 0.5f;
+            mIntrinsicRadius = Math.min(iconCX, iconCY);
+            RectF srcRect = new RectF(iconCX - mIntrinsicRadius, iconCY - mIntrinsicRadius,
+                                      iconCX + mIntrinsicRadius, iconCY + mIntrinsicRadius);
+            mIconMatrix.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.FILL);
+        }
+
+        invalidateSelf();
+    }
+
+    @Override
+    public void invalidateSelf() {
+        super.invalidateSelf();
+        mInvalidated = true;
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mFrameColor != null && mFrameColor.isStateful();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return (mSize <= 0 ? (int) mIntrinsicRadius * 2 : mSize);
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return getIntrinsicWidth();
+    }
+
+    @Override
+    public void invalidateDrawable(@NonNull Drawable who) {
+        invalidateSelf();
+    }
+
+    @Override
+    public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+        scheduleSelf(what, when);
+    }
+
+    @Override
+    public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+        unscheduleSelf(what);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
index f9fa805f..750601d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
@@ -33,7 +33,7 @@
 import android.widget.SpinnerAdapter;
 import android.widget.TextView;
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import com.android.settingslib.R;
 
@@ -71,7 +71,8 @@
         }
 
         private static Drawable encircle(Context context, Drawable icon) {
-            return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(icon));
+            return new UserIconDrawable(UserIconDrawable.getSizeForList(context))
+                    .setIconDrawable(icon).bake();
         }
     }
     private ArrayList<UserDetails> data;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 61c9f2b..5621642 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1942,7 +1942,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 126;
+            private static final int SETTINGS_VERSION = 127;
 
             private final int mUserId;
 
@@ -2167,6 +2167,36 @@
                     currentVersion = 126;
                 }
 
+                if (currentVersion == 126) {
+                    // Version 126: copy the primary values of LOCK_SCREEN_SHOW_NOTIFICATIONS and
+                    // LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS into managed profile.
+                    if (mUserManager.isManagedProfile(userId)) {
+                        final SettingsState systemSecureSettings =
+                                getSecureSettingsLocked(UserHandle.USER_SYSTEM);
+
+                        final Setting showNotifications = systemSecureSettings.getSettingLocked(
+                                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+                        if (showNotifications != null) {
+                            final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                            secureSettings.insertSettingLocked(
+                                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                                    showNotifications.getValue(),
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+
+                        final Setting allowPrivate = systemSecureSettings.getSettingLocked(
+                                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+                        if (allowPrivate != null) {
+                            final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                            secureSettings.insertSettingLocked(
+                                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                                    allowPrivate.getValue(),
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+                    currentVersion = 127;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 // Return the current version.
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
new file mode 100644
index 0000000..20251c2
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:color="@color/current_user_border_color" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
new file mode 100644
index 0000000..2b75c36
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index c6e453a..d685528 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -33,14 +33,18 @@
     <TextView android:id="@+id/user_name"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="16dp"
+            android:layout_marginEnd="13dp"
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
             />
     <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
-            android:layout_width="@dimen/max_avatar_size"
-            android:layout_height="@dimen/max_avatar_size"
+            android:layout_width="@dimen/framed_avatar_size"
+            android:layout_height="@dimen/framed_avatar_size"
             android:contentDescription="@null"
+            android:backgroundTint="@color/qs_user_detail_avatar_tint"
+            android:backgroundTintMode="src_atop"
             sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
-            sysui:framePadding="6dp"
-            sysui:activeFrameColor="@color/current_user_border_color" />
+            sysui:framePadding="2.5dp"
+            sysui:badgeDiameter="18dp"
+            sysui:badgeMargin="1dp"
+            sysui:frameColor="@color/qs_user_detail_avatar_frame" />
 </com.android.systemui.qs.tiles.UserDetailItemView>
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 661d74a..58fc069 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -33,12 +33,16 @@
 
     <com.android.systemui.statusbar.phone.UserAvatarView
             android:id="@+id/user_picture"
-            android:layout_width="@dimen/max_avatar_size"
-            android:layout_height="@dimen/max_avatar_size"
-            android:layout_marginBottom="10dp"
+            android:layout_width="@dimen/framed_avatar_size"
+            android:layout_height="@dimen/framed_avatar_size"
+            android:layout_marginBottom="7dp"
+            android:backgroundTint="@color/qs_user_detail_avatar_tint"
+            android:backgroundTintMode="src_atop"
             systemui:frameWidth="2dp"
-            systemui:framePadding="6dp"
-            systemui:activeFrameColor="@color/current_user_border_color"/>
+            systemui:framePadding="2.5dp"
+            systemui:badgeDiameter="18dp"
+            systemui:badgeMargin="1dp"
+            systemui:frameColor="@color/qs_user_detail_avatar_frame"/>
 
     <LinearLayout
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6dd8c52..1543360 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -53,10 +53,14 @@
         <enum name="vertical" value="1" />
     </attr>
     <declare-styleable name="UserAvatarView">
+        <attr name="avatarPadding" format="dimension" />
         <attr name="frameWidth" format="dimension" />
         <attr name="framePadding" format="dimension" />
+        <!-- {@deprecated Use a statelist in frameColor instead.} -->
         <attr name="activeFrameColor" format="color" />
-        <attr name="frameColor" />
+        <attr name="frameColor" format="color" />
+        <attr name="badgeDiameter" format="dimension" />
+        <attr name="badgeMargin" format="dimension" />
     </declare-styleable>
     <declare-styleable name="UserDetailItemView">
         <attr name="regularFontFamily" format="string" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a4eadbf..cf2e338 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -388,6 +388,9 @@
          quick settings header -->
     <dimen name="max_avatar_size">48dp</dimen>
 
+    <!-- Size of user icon + frame in the qs/keyguard user picker (incl. frame) -->
+    <dimen name="framed_avatar_size">54dp</dimen>
+
     <!-- Margin on the left side of the carrier text on Keyguard -->
     <dimen name="keyguard_carrier_text_margin">16dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java b/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
deleted file mode 100644
index 1933bbc..0000000
--- a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.graphics.Shader;
-
-public class BitmapHelper {
-    /**
-     * Generate a new bitmap (width x height pixels, ARGB_8888) with the input bitmap scaled
-     * to fit and clipped to an inscribed circle.
-     * @param input Bitmap to resize and clip
-     * @param width Width of output bitmap (and diameter of circle)
-     * @param height Height of output bitmap
-     * @return A shiny new bitmap for you to use
-     */
-    public static Bitmap createCircularClip(Bitmap input, int width, int height) {
-        if (input == null) return null;
-
-        final int inWidth = input.getWidth();
-        final int inHeight = input.getHeight();
-        final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(output);
-        final Paint paint = new Paint();
-        paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
-        paint.setAntiAlias(true);
-        final RectF srcRect = new RectF(0, 0, inWidth, inHeight);
-        final RectF dstRect = new RectF(0, 0, width, height);
-        final Matrix m = new Matrix();
-        m.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER);
-        canvas.setMatrix(m);
-        canvas.drawCircle(inWidth / 2, inHeight / 2, inWidth / 2, paint);
-        return output;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index fcf758b..99eae02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -30,6 +30,7 @@
 import android.widget.TextView;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.UserAvatarView;
@@ -87,25 +88,29 @@
         return (UserDetailItemView) convertView;
     }
 
-    public void bind(String name, Bitmap picture) {
+    public void bind(String name, Bitmap picture, int userId) {
         mName.setText(name);
-        mAvatar.setBitmap(picture);
+        mAvatar.setAvatarWithBadge(picture, userId);
     }
 
-    public void bind(String name, Drawable picture) {
+    public void bind(String name, Drawable picture, int userId) {
         mName.setText(name);
-        mAvatar.setDrawable(picture);
+        mAvatar.setDrawableWithBadge(picture, userId);
+    }
+
+    public void setAvatarEnabled(boolean enabled) {
+        mAvatar.setEnabled(enabled);
     }
 
     public void setDisabledByAdmin(boolean disabled) {
         mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE);
         mName.setEnabled(!disabled);
-        mAvatar.setDisabled(disabled);
+        mAvatar.setEnabled(!disabled);
     }
 
     public void setEnabled(boolean enabled) {
         mName.setEnabled(enabled);
-        mAvatar.setDisabled(!enabled);
+        mAvatar.setEnabled(enabled);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index da98762..d4fa765 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -76,9 +76,9 @@
             }
             String name = getName(mContext, item);
             if (item.picture == null) {
-                v.bind(name, getDrawable(mContext, item));
+                v.bind(name, getDrawable(mContext, item), item.resolveId());
             } else {
-                v.bind(name, item.picture);
+                v.bind(name, item.picture, item.info.id);
             }
             v.setActivated(item.isCurrent);
             v.setDisabledByAdmin(item.isDisabledByAdmin);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
index 093a827..dc1b35d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -17,69 +17,57 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
 import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.R;
 
 /**
- * A view that displays a user image cropped to a circle with a frame.
+ * A view that displays a user image cropped to a circle with an optional frame.
  */
 public class UserAvatarView extends View {
 
-    private int mActiveFrameColor;
-    private int mFrameColor;
-    private float mFrameWidth;
-    private float mFramePadding;
-    private Bitmap mBitmap;
-    private Drawable mDrawable;
-    private boolean mIsDisabled;
-
-    private final Paint mFramePaint = new Paint();
-    private final Paint mBitmapPaint = new Paint();
-    private final Matrix mDrawMatrix = new Matrix();
-
-    private float mScale = 1;
+    private final UserIconDrawable mDrawable = new UserIconDrawable();
 
     public UserAvatarView(Context context, AttributeSet attrs,
             int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
         final int N = a.getIndexCount();
         for (int i = 0; i < N; i++) {
             int attr = a.getIndex(i);
             switch (attr) {
+                case R.styleable.UserAvatarView_avatarPadding:
+                    setAvatarPadding(a.getDimension(attr, 0));
+                    break;
                 case R.styleable.UserAvatarView_frameWidth:
                     setFrameWidth(a.getDimension(attr, 0));
                     break;
                 case R.styleable.UserAvatarView_framePadding:
                     setFramePadding(a.getDimension(attr, 0));
                     break;
-                case R.styleable.UserAvatarView_activeFrameColor:
-                    setActiveFrameColor(a.getColor(attr, 0));
-                    break;
                 case R.styleable.UserAvatarView_frameColor:
-                    setFrameColor(a.getColor(attr, 0));
+                    setFrameColor(a.getColorStateList(attr));
+                    break;
+                case R.styleable.UserAvatarView_badgeDiameter:
+                    setBadgeDiameter(a.getDimension(attr, 0));
+                    break;
+                case R.styleable.UserAvatarView_badgeMargin:
+                    setBadgeMargin(a.getDimension(attr, 0));
                     break;
             }
         }
         a.recycle();
-
-        mFramePaint.setAntiAlias(true);
-        mFramePaint.setStyle(Paint.Style.STROKE);
-        mBitmapPaint.setAntiAlias(true);
+        setBackground(mDrawable);
     }
 
     public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -94,180 +82,61 @@
         this(context, null);
     }
 
+    /**
+     * @deprecated use {@link #setAvatar(Bitmap)} instead.
+     */
+    @Deprecated
     public void setBitmap(Bitmap bitmap) {
-        setDrawable(null);
-        mBitmap = bitmap;
-        if (mBitmap != null) {
-            mBitmapPaint.setShader(new BitmapShader(
-                    bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
-        } else {
-            mBitmapPaint.setShader(null);
-        }
-        configureBounds();
-        invalidate();
+        setAvatar(bitmap);
     }
 
-    public void setFrameColor(int frameColor) {
-        mFrameColor = frameColor;
-        invalidate();
-    }
-
-    public void setActiveFrameColor(int activeFrameColor) {
-        mActiveFrameColor = activeFrameColor;
-        invalidate();
+    public void setFrameColor(ColorStateList color) {
+        mDrawable.setFrameColor(color);
     }
 
     public void setFrameWidth(float frameWidth) {
-        mFrameWidth = frameWidth;
-        invalidate();
+        mDrawable.setFrameWidth(frameWidth);
     }
 
     public void setFramePadding(float framePadding) {
-        mFramePadding = framePadding;
-        invalidate();
+        mDrawable.setFramePadding(framePadding);
     }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        configureBounds();
+    public void setAvatarPadding(float avatarPadding) {
+        mDrawable.setPadding(avatarPadding);
     }
 
-    public void configureBounds() {
-        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
-        int vheight = getHeight() - mPaddingTop - mPaddingBottom;
-
-        int dwidth;
-        int dheight;
-        if (mBitmap != null) {
-            dwidth = mBitmap.getWidth();
-            dheight = mBitmap.getHeight();
-        } else if (mDrawable != null) {
-            vwidth -= 2 * (mFrameWidth - 1);
-            vheight -= 2 * (mFrameWidth - 1);
-            dwidth = vwidth;
-            dheight = vheight;
-            mDrawable.setBounds(0, 0, dwidth, dheight);
-        } else {
-            return;
-        }
-
-        float scale;
-        float dx;
-        float dy;
-
-        scale = Math.min((float) vwidth / (float) dwidth,
-                (float) vheight / (float) dheight);
-
-        dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
-        dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
-
-        mDrawMatrix.setScale(scale, scale);
-        mDrawMatrix.postTranslate(dx, dy);
-        mScale = scale;
+    public void setBadgeDiameter(float diameter) {
+        mDrawable.setBadgeRadius(diameter * 0.5f);
     }
 
-    @Override
-    protected void onDraw(Canvas canvas) {
-        int frameColor = isActivated() ? mActiveFrameColor : mFrameColor;
-        float halfW = getWidth() / 2f;
-        float halfH = getHeight() / 2f;
-        float halfSW = Math.min(halfH, halfW);
-        updateDrawableIfDisabled();
-        if (mBitmap != null && mScale > 0) {
-            int saveCount = canvas.getSaveCount();
-            canvas.save();
-            canvas.translate(mPaddingLeft, mPaddingTop);
-            canvas.concat(mDrawMatrix);
-            float halfBW = mBitmap.getWidth() / 2f;
-            float halfBH = mBitmap.getHeight() / 2f;
-            float halfBSW = Math.min(halfBH, halfBW);
-            canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint);
-            canvas.restoreToCount(saveCount);
-        } else if (mDrawable != null && mScale > 0) {
-            int saveCount = canvas.getSaveCount();
-            canvas.save();
-            canvas.translate(mPaddingLeft, mPaddingTop);
-            canvas.translate(mFrameWidth - 1, mFrameWidth - 1);
-            canvas.concat(mDrawMatrix);
-            mDrawable.draw(canvas);
-            canvas.restoreToCount(saveCount);
-        }
-        if (frameColor != 0) {
-            mFramePaint.setColor(frameColor);
-            mFramePaint.setStrokeWidth(mFrameWidth);
-            canvas.drawCircle(halfW, halfH, halfSW + (mFramePadding - mFrameWidth) / 2f,
-                    mFramePaint);
-        }
+    public void setBadgeMargin(float margin) {
+        mDrawable.setBadgeMargin(margin);
+    }
+
+    public void setAvatar(Bitmap avatar) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadge(null);
+    }
+
+    public void setAvatarWithBadge(Bitmap avatar, int userId) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
     }
 
     public void setDrawable(Drawable d) {
-        if (mDrawable != null) {
-            mDrawable.setCallback(null);
-            unscheduleDrawable(mDrawable);
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
         }
-        mDrawable = d;
-        if (d != null) {
-            d.setCallback(this);
-            if (d.isStateful()) {
-                d.setState(getDrawableState());
-            }
-            d.setLayoutDirection(getLayoutDirection());
-            configureBounds();
-        }
-        if (d != null) {
-            mBitmap = null;
-        }
-        configureBounds();
-        invalidate();
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadge(null);
     }
 
-    @Override
-    public void invalidateDrawable(Drawable dr) {
-        if (dr == mDrawable) {
-            invalidate();
-        } else {
-            super.invalidateDrawable(dr);
+    public void setDrawableWithBadge(Drawable d, int userId) {
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
         }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return who == mDrawable || super.verifyDrawable(who);
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mDrawable != null && mDrawable.isStateful()) {
-            mDrawable.setState(getDrawableState());
-        }
-    }
-
-    public void setDisabled(boolean disabled) {
-        if (mIsDisabled == disabled) {
-            return;
-        }
-        mIsDisabled = disabled;
-        invalidate();
-    }
-
-    private void updateDrawableIfDisabled() {
-        int disabledColor = getContext().getColor(R.color.qs_tile_disabled_color);
-        PorterDuffColorFilter filter = new PorterDuffColorFilter(disabledColor,
-                PorterDuff.Mode.SRC_ATOP);
-        if (mBitmap != null) {
-            if (mIsDisabled) {
-                mBitmapPaint.setColorFilter(filter);
-            } else {
-                mBitmapPaint.setColorFilter(null);
-            }
-        } else if (mDrawable != null) {
-            if (mIsDisabled) {
-                mDrawable.setColorFilter(filter);
-            } else {
-                mDrawable.setColorFilter(null);
-            }
-        }
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
     }
 }
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 fb310a6..c39d718 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -253,17 +253,13 @@
             UserDetailItemView v = (UserDetailItemView) convertView;
 
             String name = getName(mContext, item);
-            Drawable drawable;
             if (item.picture == null) {
-                drawable = getDrawable(mContext, item).mutate();
+                v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
             } else {
-                drawable = new BitmapDrawable(mContext.getResources(), item.picture);
+                v.bind(name, item.picture, item.info.id);
             }
             // Disable the icon if switching is disabled
-            if (!item.isSwitchToEnabled) {
-                drawable.setTint(mContext.getColor(R.color.qs_tile_disabled_color));
-            }
-            v.bind(name, drawable);
+            v.setAvatarEnabled(item.isSwitchToEnabled);
             convertView.setActivated(item.isCurrent);
             convertView.setTag(item);
             return convertView;
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 85ac755..bae5bda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -26,7 +26,6 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.RemoteException;
@@ -37,7 +36,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.UserIcons;
-import com.android.systemui.BitmapHelper;
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.R;
 
 import java.util.ArrayList;
@@ -155,8 +154,8 @@
                 Drawable avatar = null;
                 Bitmap rawAvatar = um.getUserIcon(userId);
                 if (rawAvatar != null) {
-                    avatar = new BitmapDrawable(mContext.getResources(),
-                            BitmapHelper.createCircularClip(rawAvatar, avatarSize, avatarSize));
+                    avatar = new UserIconDrawable(avatarSize)
+                            .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
                 } else {
                     avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
                             /* light= */ true);
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 ea0bdf2..c82ba3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -50,7 +50,6 @@
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.RestrictedLockUtils;
-import com.android.systemui.BitmapHelper;
 import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.R;
 import com.android.systemui.SystemUISecondaryUserService;
@@ -197,8 +196,6 @@
                 boolean canSwitchUsers = mUserManager.canSwitchUsers();
                 UserInfo currentUserInfo = null;
                 UserRecord guestRecord = null;
-                int avatarSize = mContext.getResources()
-                        .getDimensionPixelSize(R.dimen.max_avatar_size);
 
                 for (UserInfo info : infos) {
                     boolean isCurrent = currentId == info.id;
@@ -219,8 +216,10 @@
                                 picture = mUserManager.getUserIcon(info.id);
 
                                 if (picture != null) {
-                                    picture = BitmapHelper.createCircularClip(
-                                            picture, avatarSize, avatarSize);
+                                    int avatarSize = mContext.getResources()
+                                            .getDimensionPixelSize(R.dimen.max_avatar_size);
+                                    picture = Bitmap.createScaledBitmap(
+                                            picture, avatarSize, avatarSize, true);
                                 }
                             }
                             int index = isCurrent ? 0 : records.size();
@@ -664,8 +663,7 @@
             if (item.isAddUser) {
                 return context.getDrawable(R.drawable.ic_add_circle_qs);
             }
-            return UserIcons.getDefaultUserIcon(item.isGuest ? UserHandle.USER_NULL : item.info.id,
-                    /* light= */ true);
+            return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
         }
 
         public void refresh() {
@@ -718,6 +716,13 @@
                     isSwitchToEnabled);
         }
 
+        public int resolveId() {
+            if (isGuest || info == null) {
+                return UserHandle.USER_NULL;
+            }
+            return info.id;
+        }
+
         public String toString() {
             StringBuilder sb = new StringBuilder();
             sb.append("UserRecord(");
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index b3613df..ea3cffe 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2135,6 +2135,9 @@
     // Suggestion -> Overflow -> Remove.
     ACTION_SETTINGS_DISMISS_SUGGESTION = 387;
 
+    // Settings > Apps > Gear > Special Access > Premium SMS access
+    PREMIUM_SMS_ACCESS = 388;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2741733..01d4ad6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1795,9 +1795,8 @@
                 }
                 userState.mSoftKeyboardShowMode = 0;
                 userState.mServiceChangingSoftKeyboardMode = null;
+                notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
             }
-
-            notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
         }
     }
 
@@ -4355,6 +4354,7 @@
                     }
                 } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)) {
                     if (readSoftKeyboardShowModeChangedLocked(userState)) {
+                        notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
                         onUserStateChangedLocked(userState);
                     }
                 }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4ec1f61..fdcc242 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1650,6 +1650,13 @@
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
                 if (r.finishing) {
+                    // Normally the screenshot will be taken in makeInvisible(). When an activity
+                    // is finishing, we no longer change its visibility, but we still need to take
+                    // the screenshots if startPausingLocked decided it should be taken.
+                    if (r.mUpdateTaskThumbnailWhenHidden) {
+                        r.updateThumbnailLocked(screenshotActivitiesLocked(r), null);
+                        r.mUpdateTaskThumbnailWhenHidden = false;
+                    }
                     continue;
                 }
                 final boolean isTop = r == top;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f6d2f7e..8368185 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -905,7 +905,10 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
 
         synchronized (mSessions) {
-            mSessions.get(sessionId).setPermissionsResult(accepted);
+            PackageInstallerSession session = mSessions.get(sessionId);
+            if (session != null) {
+                session.setPermissionsResult(accepted);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7a61e79..883b502 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8605,7 +8605,7 @@
                 if (abi32 >= 0) {
                     final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                     if (abi64 >= 0) {
-                        if (cpuAbiOverride == null && pkg.use32bitAbi) {
+                        if (pkg.use32bitAbi) {
                             pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
                             pkg.applicationInfo.primaryCpuAbi = abi;
                         } else {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 24eac19..83cd978 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -363,7 +363,7 @@
     // Packages that have been uninstalled and still need their external
     // storage data deleted.
     final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
-    
+
     // Packages that have been renamed since they were first installed.
     // Keys are the new names of the packages, values are the original
     // names.  The packages appear everwhere else under their original
@@ -578,7 +578,7 @@
         }
         PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
-                p.secondaryCpuAbiString, p.secondaryCpuAbiString,
+                p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
                 p.parentPackageName, p.childPackageNames);
         mDisabledSysPackages.remove(name);
@@ -3756,7 +3756,7 @@
                 }
 
                 String tagName = parser.getName();
-                // Legacy 
+                // Legacy
                 if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
                     readDisabledComponentsLPw(packageSetting, parser, 0);
                 } else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index bcafddc..5b9d139 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -93,6 +93,7 @@
 
     // Indicates whether we are rebooting into safe mode
     public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
+    public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode";
 
     // Indicates whether we should stay in safe mode until ro.build.date.utc is newer than this
     public static final String AUDIT_SAFEMODE_PROPERTY = "persist.sys.audit_safemode";
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index bae628a..3ef077b 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1214,7 +1214,10 @@
             window.type = windowState.mAttrs.type;
             window.layer = windowState.mLayer;
             window.token = windowState.mClient.asBinder();
-            window.title = windowState.mAttrs.getTitle();
+            window.title = windowState.mAttrs.accessibilityTitle;
+            if (window.title == null) {
+                window.title = windowState.mAttrs.getTitle();
+            }
             window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
 
             WindowState attachedWindow = windowState.mAttachedWindow;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e32c976..27126e6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7624,7 +7624,8 @@
         mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0
                 || volumeDownState > 0;
         try {
-            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0) {
+            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0
+                    || SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) != 0) {
                 int auditSafeMode = SystemProperties.getInt(ShutdownThread.AUDIT_SAFEMODE_PROPERTY, 0);
 
                 if (auditSafeMode == 0) {
@@ -7648,6 +7649,7 @@
         if (mSafeMode) {
             Log.i(TAG_WM, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
                     + " dpad=" + dpadState + " trackball=" + trackballState + ")");
+            SystemProperties.set(ShutdownThread.RO_SAFEMODE_PROPERTY, "1");
         } else {
             Log.i(TAG_WM, "SAFE MODE not enabled");
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index de485de..7f40079 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2355,6 +2355,10 @@
         return mDragResizing;
     }
 
+    boolean isDockedResizing() {
+        return mDragResizing && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+    }
+
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final TaskStack stack = getStack();
         pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9e3a4db..9c25f63 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1125,13 +1125,21 @@
         final int top = w.mYOffset + w.mFrame.top;
 
         // Initialize the decor rect to the entire frame.
-        if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER) {
+        if (w.isDockedResizing() ||
+                (w.isChildWindow() && w.mAttachedWindow.isDockedResizing())) {
 
             // If we are resizing with the divider, the task bounds might be smaller than the
             // stack bounds. The system decor is used to clip to the task bounds, which we don't
             // want in this case in order to avoid holes.
+            //
+            // We take care to not shrink the width, for surfaces which are larger than
+            // the display region. Of course this area will not eventually be visible
+            // but if we truncate the width now, we will calculate incorrectly
+            // when adjusting to the stack bounds.
             final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
-            mSystemDecorRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+            mSystemDecorRect.set(0, 0,
+                    Math.max(width, displayInfo.logicalWidth),
+                    Math.max(height, displayInfo.logicalHeight));
         } else {
             mSystemDecorRect.set(0, 0, width, height);
         }