Merge "Fixed a measuring bug when the panel was collapsed" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 7db6d5dd..8716381 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4496,6 +4496,7 @@
 
   public abstract class FragmentHostCallback extends android.app.FragmentContainer {
     ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onAttachFragment(android.app.Fragment);
     method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public android.view.View onFindViewById(int);
     method public abstract E onGetHost();
@@ -9828,6 +9829,7 @@
   public final class Resources.Theme {
     method public void applyStyle(int, boolean);
     method public void dump(int, java.lang.String, java.lang.String);
+    method public int getChangingConfigurations();
     method public android.graphics.drawable.Drawable getDrawable(int) throws android.content.res.Resources.NotFoundException;
     method public android.content.res.Resources getResources();
     method public android.content.res.TypedArray obtainStyledAttributes(int[]);
@@ -15607,6 +15609,16 @@
     field public static final int HEVCMainTierLevel62 = 16777216; // 0x1000000
     field public static final int HEVCProfileMain = 1; // 0x1
     field public static final int HEVCProfileMain10 = 2; // 0x2
+    field public static final int MPEG2LevelH14 = 2; // 0x2
+    field public static final int MPEG2LevelHL = 3; // 0x3
+    field public static final int MPEG2LevelLL = 0; // 0x0
+    field public static final int MPEG2LevelML = 1; // 0x1
+    field public static final int MPEG2Profile422 = 2; // 0x2
+    field public static final int MPEG2ProfileHigh = 5; // 0x5
+    field public static final int MPEG2ProfileMain = 1; // 0x1
+    field public static final int MPEG2ProfileSNR = 3; // 0x3
+    field public static final int MPEG2ProfileSimple = 0; // 0x0
+    field public static final int MPEG2ProfileSpatial = 4; // 0x4
     field public static final int MPEG4Level0 = 1; // 0x1
     field public static final int MPEG4Level0b = 2; // 0x2
     field public static final int MPEG4Level1 = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index 3431a29..f11e113 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -110,6 +110,7 @@
     field public static final java.lang.String INTERNET = "android.permission.INTERNET";
     field public static final java.lang.String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP";
     field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
+    field public static final java.lang.String KILL_UID = "android.permission.KILL_UID";
     field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
     field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
@@ -3671,6 +3672,7 @@
     method public static boolean isRunningInTestHarness();
     method public static boolean isUserAMonkey();
     method public void killBackgroundProcesses(java.lang.String);
+    method public void killUid(int, java.lang.String);
     method public void moveTaskToFront(int, int);
     method public void moveTaskToFront(int, int, android.os.Bundle);
     method public deprecated void restartPackage(java.lang.String);
@@ -4586,6 +4588,7 @@
 
   public abstract class FragmentHostCallback extends android.app.FragmentContainer {
     ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
+    method public void onAttachFragment(android.app.Fragment);
     method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public android.view.View onFindViewById(int);
     method public abstract E onGetHost();
@@ -10120,6 +10123,7 @@
   public final class Resources.Theme {
     method public void applyStyle(int, boolean);
     method public void dump(int, java.lang.String, java.lang.String);
+    method public int getChangingConfigurations();
     method public android.graphics.drawable.Drawable getDrawable(int) throws android.content.res.Resources.NotFoundException;
     method public android.content.res.Resources getResources();
     method public android.content.res.TypedArray obtainStyledAttributes(int[]);
@@ -16827,6 +16831,16 @@
     field public static final int HEVCMainTierLevel62 = 16777216; // 0x1000000
     field public static final int HEVCProfileMain = 1; // 0x1
     field public static final int HEVCProfileMain10 = 2; // 0x2
+    field public static final int MPEG2LevelH14 = 2; // 0x2
+    field public static final int MPEG2LevelHL = 3; // 0x3
+    field public static final int MPEG2LevelLL = 0; // 0x0
+    field public static final int MPEG2LevelML = 1; // 0x1
+    field public static final int MPEG2Profile422 = 2; // 0x2
+    field public static final int MPEG2ProfileHigh = 5; // 0x5
+    field public static final int MPEG2ProfileMain = 1; // 0x1
+    field public static final int MPEG2ProfileSNR = 3; // 0x3
+    field public static final int MPEG2ProfileSimple = 0; // 0x0
+    field public static final int MPEG2ProfileSpatial = 4; // 0x4
     field public static final int MPEG4Level0 = 1; // 0x1
     field public static final int MPEG4Level0b = 2; // 0x2
     field public static final int MPEG4Level1 = 4; // 0x4
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index afbddd9..7260d102 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6498,6 +6498,11 @@
             return (w == null) ? 0 : w.getAttributes().windowAnimations;
         }
 
+        @Override
+        public void onAttachFragment(Fragment fragment) {
+            Activity.this.onAttachFragment(fragment);
+        }
+
         @Nullable
         @Override
         public View onFindViewById(int id) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 576a046..9bbb4be 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,8 +16,10 @@
 
 package android.app;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
@@ -26,6 +28,7 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 
+import android.util.Log;
 import com.android.internal.app.ProcessStats;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.FastPrintWriter;
@@ -2396,7 +2399,24 @@
         } catch (RemoteException e) {
         }
     }
-    
+
+    /**
+     * Kills the specified UID.
+     * @param uid The UID to kill.
+     * @param reason The reason for the kill.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.KILL_UID)
+    public void killUid(int uid, String reason) {
+        try {
+            ActivityManagerNative.getDefault().killUid(uid, reason);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't kill uid:" + uid, e);
+        }
+    }
+
     /**
      * Have the system perform a force stop of everything associated with
      * the given application package.  All processes that share its uid
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6bbbf9e..5aa399b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -941,7 +941,8 @@
      * @hide
      */
     public static int permissionToOpCode(String permission) {
-        return sPermToOp.get(permission);
+        Integer boxedOpCode = sPermToOp.get(permission);
+        return boxedOpCode != null ? boxedOpCode : OP_NONE;
     }
 
     /**
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 91d810e..40c5c64 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1314,6 +1314,12 @@
                     com.android.internal.R.styleable.Fragment_fragmentAllowReturnTransitionOverlap, true);
         }
         a.recycle();
+
+        final Activity hostActivity = mHost == null ? null : mHost.getActivity();
+        if (hostActivity != null) {
+            mCalled = false;
+            onInflate(hostActivity, attrs, savedInstanceState);
+        }
     }
 
     /**
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index dad2c79..3e753f0 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -23,7 +23,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.ArrayMap;
-import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -140,6 +139,14 @@
         return mWindowAnimations;
     }
 
+    /**
+     * Called when a {@link Fragment} is being attached to this host, immediately
+     * after the call to its {@link Fragment#onAttach(Context)} method and before
+     * {@link Fragment#onCreate(Bundle)}.
+     */
+    public void onAttachFragment(Fragment fragment) {
+    }
+
     @Nullable
     @Override
     public View onFindViewById(int id) {
@@ -187,14 +194,6 @@
         }
     }
 
-    void onFragmentInflate(Fragment fragment, AttributeSet attrs, Bundle savedInstanceState) {
-        fragment.onInflate(mContext, attrs, savedInstanceState);
-    }
-
-    void onFragmentAttach(Fragment fragment) {
-        fragment.onAttach(mContext);
-    }
-
     void doLoaderStart() {
         if (mLoadersStarted) {
             return;
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 62436e9..6b5239d 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -844,13 +844,13 @@
                     f.mFragmentManager = mParent != null
                             ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
                     f.mCalled = false;
-                    mHost.onFragmentAttach(f);
+                    f.onAttach(mHost.getContext());
                     if (!f.mCalled) {
                         throw new SuperNotCalledException("Fragment " + f
                                 + " did not call through to super.onAttach()");
                     }
                     if (f.mParentFragment == null) {
-                        mHost.onFragmentAttach(f);
+                        mHost.onAttachFragment(f);
                     }
 
                     if (!f.mRetaining) {
@@ -2107,7 +2107,7 @@
             fragment.mTag = tag;
             fragment.mInLayout = true;
             fragment.mFragmentManager = this;
-            mHost.onFragmentInflate(fragment, attrs, fragment.mSavedFragmentState);
+            fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
             addFragment(fragment, true);
         } else if (fragment.mInLayout) {
             // A fragment already exists and it is not one we restored from
@@ -2124,7 +2124,7 @@
             // from last saved state), then give it the attributes to
             // initialize itself.
             if (!fragment.mRetaining) {
-                mHost.onFragmentInflate(fragment, attrs, fragment.mSavedFragmentState);
+                fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
             }
         }
 
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index aea413d..470804d 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -231,7 +231,7 @@
      * device owner app.
      *
      * <p>The broadcast will be limited to the {@link DeviceAdminReceiver} component specified in
-     * the (@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME) field
+     * the {@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME} field
      * of the original intent or NFC bump that started the provisioning process. You will generally
      * handle this in {@link DeviceAdminReceiver#onReadyForUserInitialization}.
      *
@@ -450,9 +450,9 @@
      *
      * <p>It is not assumed that the device initializer is finished when it returns from
      * this call, as it may do additional setup asynchronously. The device initializer must call
-     * {DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any additional
-     * setup (such as adding an account by using the {@link AccountManager}) in order for the user
-     * to be functional.
+     * {@link DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any
+     * additional setup (such as adding an account by using the {@link AccountManager}) in order for
+     * the user to be functional.
      *
      * @param context The running context as per {@link #onReceive}.
      * @param intent The received intent as per {@link #onReceive}.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9f71ea5..4b72dc3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -412,7 +412,7 @@
         = "android.app.action.MANAGED_PROFILE_PROVISIONED";
 
     /**
-     * A boolean extra indicating whether device encryption is required as part of Device Owner
+     * A boolean extra indicating whether device encryption can be skipped as part of Device Owner
      * provisioning.
      *
      * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 17cff5c..32951d9 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -32,4 +32,5 @@
     void reportKeyguardShowingChanged();
     boolean isDeviceLocked(int userId);
     boolean isDeviceSecure(int userId);
+    boolean hasUserAuthenticatedSinceBoot(int userId);
 }
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index b5c5317..8cab565 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -147,6 +147,23 @@
         }
     }
 
+    /**
+     * Checks whether the specified user has been authenticated since the last boot.
+     *
+     * @param userId the user id of the user to check for
+     * @return true if the user has authenticated since boot, false otherwise
+     *
+     * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+     */
+    public boolean hasUserAuthenticatedSinceBoot(int userId) {
+        try {
+            return mService.hasUserAuthenticatedSinceBoot(userId);
+        } catch (RemoteException e) {
+            onError(e);
+            return false;
+        }
+    }
+
     private void onError(Exception e) {
         Log.e(TAG, "Error while calling TrustManagerService", e);
     }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 16f6b1e..43cc63b 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -570,13 +570,16 @@
         Configuration.NATIVE_CONFIG_DENSITY,                // DENSITY
         Configuration.NATIVE_CONFIG_LAYOUTDIR,              // LAYOUT DIRECTION
     };
-    /** @hide
+
+    /**
      * Convert Java change bits to native.
+     *
+     * @hide
      */
     public static int activityInfoConfigToNative(int input) {
         int output = 0;
-        for (int i=0; i<CONFIG_NATIVE_BITS.length; i++) {
-            if ((input&(1<<i)) != 0) {
+        for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
+            if ((input & (1 << i)) != 0) {
                 output |= CONFIG_NATIVE_BITS[i];
             }
         }
@@ -584,6 +587,21 @@
     }
 
     /**
+     * Convert native change bits to Java.
+     *
+     * @hide
+     */
+    public static int activityInfoConfigNativeToJava(int input) {
+        int output = 0;
+        for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
+            if ((input & CONFIG_NATIVE_BITS[i]) != 0) {
+                output |= (1 << i);
+            }
+        }
+        return output;
+    }
+
+    /**
      * @hide
      * Unfortunately some developers (OpenFeint I am looking at you) have
      * compared the configChanges bit field against absolute values, so if we
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 0b24594..94b0223 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -96,9 +96,9 @@
 
     void removePermission(String name);
 
-    boolean grantPermission(String packageName, String permissionName, int userId);
+    void grantPermission(String packageName, String permissionName, int userId);
 
-    boolean revokePermission(String packageName, String permissionName, int userId);
+    void revokePermission(String packageName, String permissionName, int userId);
 
     boolean isProtectedBroadcast(String actionName);
 
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index a176593..525059f 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -789,6 +789,7 @@
                                                                 TypedValue outValue,
                                                                 boolean resolve);
     /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix);
+    /*package*/ native static final int getThemeChangingConfigurations(long theme);
 
     private native final long openXmlAssetNative(int cookie, String fileName);
 
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 6e77e33..ae41b69 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1731,6 +1731,19 @@
         }
 
         /**
+         * Returns a bit mask of configuration changes that will impact this
+         * theme (and thus require completely reloading it).
+         *
+         * @return a bit mask of configuration changes, as defined by
+         *         {@link ActivityInfo}
+         * @see ActivityInfo
+         */
+        public int getChangingConfigurations() {
+            final int nativeChangingConfig = AssetManager.getThemeChangingConfigurations(mTheme);
+            return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
+        }
+
+        /**
          * Print contents of this theme out to the log.  For debugging only.
          * 
          * @param priority The log priority to use.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f09f4d2..968cd72 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -692,7 +692,6 @@
                 .authority(SYSTEM_AUTHORITY)
                 .appendPath(EVENT_PATH)
                 .appendQueryParameter("calendar", Long.toString(event.calendar))
-                .appendQueryParameter("attendance", Integer.toString(event.attendance))
                 .appendQueryParameter("reply", Integer.toString(event.reply))
                 .build();
     }
@@ -710,22 +709,16 @@
         if (!isEvent) return null;
         final EventInfo rt = new EventInfo();
         rt.calendar = tryParseLong(conditionId.getQueryParameter("calendar"), 0L);
-        rt.attendance = tryParseInt(conditionId.getQueryParameter("attendance"), 0);
         rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
         return rt;
     }
 
     public static class EventInfo {
-        public static final int ATTENDANCE_REQUIRED_OR_OPTIONAL = 0;
-        public static final int ATTENDANCE_REQUIRED = 1;
-        public static final int ATTENDANCE_OPTIONAL = 2;
-
-        public static final int REPLY_ANY = 0;
-        public static final int REPLY_ANY_EXCEPT_NO = 1;
+        public static final int REPLY_ANY_EXCEPT_NO = 0;
+        public static final int REPLY_YES_OR_MAYBE = 1;
         public static final int REPLY_YES = 2;
 
         public long calendar;  // CalendarContract.Calendars._ID, or 0 for any
-        public int attendance;
         public int reply;
 
         @Override
@@ -738,14 +731,12 @@
             if (!(o instanceof EventInfo)) return false;
             final EventInfo other = (EventInfo) o;
             return calendar == other.calendar
-                    && attendance == other.attendance
                     && reply == other.reply;
         }
 
         public EventInfo copy() {
             final EventInfo rt = new EventInfo();
             rt.calendar = calendar;
-            rt.attendance = attendance;
             rt.reply = reply;
             return rt;
         }
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 7bebbfb..a55a08c 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -278,7 +278,7 @@
                 if (style[j] instanceof TypefaceSpan) {
                     String s = ((TypefaceSpan) style[j]).getFamily();
 
-                    if (s.equals("monospace")) {
+                    if ("monospace".equals(s)) {
                         out.append("<tt>");
                     }
                 }
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 48167c8..bb761f0 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -93,12 +93,6 @@
 
     private static native long nCreateDisplayListCanvas();
 
-    public static void setProperty(String name, String value) {
-        nSetProperty(name, value);
-    }
-
-    private static native void nSetProperty(String name, String value);
-
     ///////////////////////////////////////////////////////////////////////////
     // Canvas management
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 91e6d68..1fd7109 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -412,6 +413,13 @@
         nTrimMemory(level);
     }
 
+    public static void overrideProperty(@NonNull String name, @NonNull String value) {
+        if (name == null || value == null) {
+            throw new IllegalArgumentException("name and value must be non-null");
+        }
+        nOverrideProperty(name, value);
+    }
+
     public static void dumpProfileData(byte[] data, FileDescriptor fd) {
         nDumpProfileData(data, fd);
     }
@@ -510,6 +518,7 @@
 
     private static native void nDestroyHardwareResources(long nativeProxy);
     private static native void nTrimMemory(int level);
+    private static native void nOverrideProperty(String name, String value);
 
     private static native void nFence(long nativeProxy);
     private static native void nStopDrawing(long nativeProxy);
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 240d9df..d77b998 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -20,6 +20,8 @@
 import android.os.SystemClock;
 import android.util.EventLog;
 
+import dalvik.system.VMRuntime;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
@@ -96,7 +98,7 @@
     
     public static void forceGc(String reason) {
         EventLog.writeEvent(2741, reason);
-        Runtime.getRuntime().gc();
+        VMRuntime.getRuntime().requestConcurrentGC();
     }
     
     static void forceBinderGc() {
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 5c95f8a..8ae2e3b 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,3 +1,7 @@
+#define LOG_TAG "Bitmap"
+
+#include "Bitmap.h"
+
 #include "Paint.h"
 #include "SkBitmap.h"
 #include "SkPixelRef.h"
@@ -14,11 +18,322 @@
 #include "android_util_Binder.h"
 #include "android_nio_utils.h"
 #include "CreateJavaOutputStreamAdaptor.h"
+#include <Caches.h>
 
 #include "core_jni_helpers.h"
 
 #include <jni.h>
 
+namespace android {
+
+class WrappedPixelRef : public SkPixelRef {
+public:
+    WrappedPixelRef(Bitmap* wrapper, void* storage,
+            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+            : SkPixelRef(info)
+            , mBitmap(*wrapper)
+            , mStorage(storage) {
+        reconfigure(info, rowBytes, ctable);
+    }
+
+    ~WrappedPixelRef() {
+        // Tell SkRefCnt that everything is as it expects by forcing
+        // the refcnt to 1
+        internal_dispose_restore_refcnt_to_1();
+        SkSafeUnref(mColorTable);
+    }
+
+    void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable) {
+        if (kIndex_8_SkColorType != info.colorType()) {
+            ctable = nullptr;
+        }
+        mRowBytes = rowBytes;
+        if (mColorTable != ctable) {
+            SkSafeUnref(mColorTable);
+            mColorTable = ctable;
+            SkSafeRef(mColorTable);
+        }
+        // Dirty hack is dirty
+        // TODO: Figure something out here, Skia's current design makes this
+        // really hard to work with. Skia really, really wants immutable objects,
+        // but with the nested-ref-count hackery going on that's just not
+        // feasible without going insane trying to figure it out
+        SkImageInfo* myInfo = const_cast<SkImageInfo*>(&this->info());
+        *myInfo = info;
+
+        // Docs say to only call this in the ctor, but we're going to call
+        // it anyway even if this isn't always the ctor.
+        // TODO: Fix this too as part of the above TODO
+        setPreLocked(mStorage, mRowBytes, mColorTable);
+    }
+
+    // Can't mark as override since SkPixelRef::rowBytes isn't virtual
+    // but that's OK since we just want BitmapWrapper to be able to rely
+    // on calling rowBytes() on an unlocked pixelref, which it will be
+    // doing on a WrappedPixelRef type, not a SkPixelRef, so static
+    // dispatching will do what we want.
+    size_t rowBytes() const { return mRowBytes; }
+    SkColorTable* colorTable() const { return mColorTable; }
+
+    bool hasHardwareMipMap() const {
+        return mHasHardwareMipMap;
+    }
+
+    void setHasHardwareMipMap(bool hasMipMap) {
+        mHasHardwareMipMap = hasMipMap;
+    }
+
+protected:
+    virtual bool onNewLockPixels(LockRec* rec) override {
+        rec->fPixels = mStorage;
+        rec->fRowBytes = mRowBytes;
+        rec->fColorTable = mColorTable;
+        return true;
+    }
+
+    virtual void onUnlockPixels() override {
+        // nothing
+    }
+
+    virtual size_t getAllocatedSizeInBytes() const override {
+        return info().getSafeSize(mRowBytes);
+    }
+
+private:
+    Bitmap& mBitmap;
+    void* mStorage;
+    size_t mRowBytes = 0;
+    SkColorTable* mColorTable = nullptr;
+    bool mHasHardwareMipMap = false;
+
+    virtual void internal_dispose() const override {
+        mBitmap.onStrongRefDestroyed();
+    }
+};
+
+Bitmap::Bitmap(JNIEnv* env, jbyteArray storageObj, void* address,
+            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+        : mPixelStorageType(PixelStorageType::Java) {
+    env->GetJavaVM(&mPixelStorage.java.jvm);
+    mPixelStorage.java.jweakRef = env->NewWeakGlobalRef(storageObj);
+    mPixelStorage.java.jstrongRef = nullptr;
+    mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
+    // Note: this will trigger a call to onStrongRefDestroyed(), but
+    // we want the pixel ref to have a ref count of 0 at this point
+    mPixelRef->unref();
+}
+
+Bitmap::Bitmap(void* address, void* context, FreeFunc freeFunc,
+            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+        : mPixelStorageType(PixelStorageType::External) {
+    mPixelStorage.external.address = address;
+    mPixelStorage.external.context = context;
+    mPixelStorage.external.freeFunc = freeFunc;
+    mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
+    // Note: this will trigger a call to onStrongRefDestroyed(), but
+    // we want the pixel ref to have a ref count of 0 at this point
+    mPixelRef->unref();
+}
+
+Bitmap::~Bitmap() {
+    doFreePixels();
+}
+
+void Bitmap::freePixels() {
+    AutoMutex _lock(mLock);
+    if (mPinnedRefCount == 0) {
+        doFreePixels();
+        mPixelStorageType = PixelStorageType::Invalid;
+    }
+}
+
+void Bitmap::doFreePixels() {
+    switch (mPixelStorageType) {
+    case PixelStorageType::Invalid:
+        // already free'd, nothing to do
+        break;
+    case PixelStorageType::External:
+        mPixelStorage.external.freeFunc(mPixelStorage.external.address,
+                mPixelStorage.external.context);
+        break;
+    case PixelStorageType::Java:
+        JNIEnv* env = jniEnv();
+        LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef,
+                "Deleting a bitmap wrapper while there are outstanding strong "
+                "references! mPinnedRefCount = %d", mPinnedRefCount);
+        env->DeleteWeakGlobalRef(mPixelStorage.java.jweakRef);
+        break;
+    }
+
+    if (android::uirenderer::Caches::hasInstance()) {
+        android::uirenderer::Caches::getInstance().textureCache.releaseTexture(
+                mPixelRef->getStableID());
+    }
+}
+
+bool Bitmap::hasHardwareMipMap() {
+    return mPixelRef->hasHardwareMipMap();
+}
+
+void Bitmap::setHasHardwareMipMap(bool hasMipMap) {
+    mPixelRef->setHasHardwareMipMap(hasMipMap);
+}
+
+const SkImageInfo& Bitmap::info() const {
+    assertValid();
+    return mPixelRef->info();
+}
+
+size_t Bitmap::rowBytes() const {
+    return mPixelRef->rowBytes();
+}
+
+SkPixelRef* Bitmap::pixelRef() const {
+    assertValid();
+    return mPixelRef.get();
+}
+
+void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes,
+        SkColorTable* ctable) {
+    mPixelRef->reconfigure(info, rowBytes, ctable);
+}
+
+void Bitmap::reconfigure(const SkImageInfo& info) {
+    mPixelRef->reconfigure(info, mPixelRef->rowBytes(), mPixelRef->colorTable());
+}
+
+void Bitmap::detachFromJava() {
+    bool disposeSelf;
+    {
+        android::AutoMutex _lock(mLock);
+        mAttachedToJava = false;
+        disposeSelf = shouldDisposeSelfLocked();
+    }
+    if (disposeSelf) {
+        delete this;
+    }
+}
+
+bool Bitmap::shouldDisposeSelfLocked() {
+    return mPinnedRefCount == 0 && !mAttachedToJava;
+}
+
+JNIEnv* Bitmap::jniEnv() {
+    JNIEnv* env;
+    auto success = mPixelStorage.java.jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
+    LOG_ALWAYS_FATAL_IF(success != JNI_OK,
+        "Failed to get JNIEnv* from JVM: %p", mPixelStorage.java.jvm);
+    return env;
+}
+
+void Bitmap::onStrongRefDestroyed() {
+    bool disposeSelf = false;
+    {
+        android::AutoMutex _lock(mLock);
+        if (mPinnedRefCount > 0) {
+            mPinnedRefCount--;
+            if (mPinnedRefCount == 0) {
+                unpinPixelsLocked();
+                disposeSelf = shouldDisposeSelfLocked();
+            }
+        }
+    }
+    if (disposeSelf) {
+        delete this;
+    }
+}
+
+void Bitmap::pinPixelsLocked() {
+    switch (mPixelStorageType) {
+    case PixelStorageType::Invalid:
+        LOG_ALWAYS_FATAL("Cannot pin invalid pixels!");
+        break;
+    case PixelStorageType::External:
+        // Nothing to do
+        break;
+    case PixelStorageType::Java: {
+        JNIEnv* env = jniEnv();
+        if (!mPixelStorage.java.jstrongRef) {
+            mPixelStorage.java.jstrongRef = reinterpret_cast<jbyteArray>(
+                    env->NewGlobalRef(mPixelStorage.java.jweakRef));
+            if (!mPixelStorage.java.jstrongRef) {
+                LOG_ALWAYS_FATAL("Failed to acquire strong reference to pixels");
+            }
+        }
+        break;
+    }
+    }
+}
+
+void Bitmap::unpinPixelsLocked() {
+    switch (mPixelStorageType) {
+    case PixelStorageType::Invalid:
+        LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!");
+        break;
+    case PixelStorageType::External:
+        // Don't need to do anything
+        break;
+    case PixelStorageType::Java: {
+        JNIEnv* env = jniEnv();
+        if (mPixelStorage.java.jstrongRef) {
+            env->DeleteGlobalRef(mPixelStorage.java.jstrongRef);
+            mPixelStorage.java.jstrongRef = nullptr;
+        }
+        break;
+    }
+    }
+}
+
+void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
+    assertValid();
+    android::AutoMutex _lock(mLock);
+    mPixelRef->ref();
+    if (mPixelRef->unique()) {
+        // We just restored this from 0, pin the pixels and inc the strong count
+        // Note that there *might be* an incoming onStrongRefDestroyed from whatever
+        // last unref'd
+        pinPixelsLocked();
+        mPinnedRefCount++;
+    }
+    // Safe because mPixelRef is a WrappedPixelRef type, otherwise rowBytes()
+    // would require locking the pixels first.
+    outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes());
+    outBitmap->setPixelRef(mPixelRef.get())->unref();
+    outBitmap->setHasHardwareMipMap(hasHardwareMipMap());
+}
+
+void Bitmap::assertValid() const {
+    LOG_ALWAYS_FATAL_IF(mPixelStorageType == PixelStorageType::Invalid,
+            "Error, cannot access an invalid/free'd bitmap here!");
+}
+
+} // namespace android
+
+using namespace android;
+
+// Convenience class that does not take a global ref on the pixels, relying
+// on the caller already having a local JNI ref
+class LocalScopedBitmap {
+public:
+    LocalScopedBitmap(jlong bitmapHandle)
+            : mBitmap(reinterpret_cast<Bitmap*>(bitmapHandle)) {}
+
+    Bitmap* operator->() {
+        return mBitmap;
+    }
+
+    void* pixels() {
+        return mBitmap->pixelRef()->pixels();
+    }
+
+    bool valid() {
+        return mBitmap && mBitmap->valid();
+    }
+
+private:
+    Bitmap* mBitmap;
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 // Conversions to/from SkColor, for get/setPixels, and the create method, which
 // is basically like setPixels
@@ -328,8 +643,8 @@
     SkBitmap bitmap;
     bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType));
 
-    jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
-    if (NULL == buff) {
+    Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
+    if (!nativeBitmap) {
         return NULL;
     }
 
@@ -338,39 +653,41 @@
                 0, 0, width, height, bitmap);
     }
 
-    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
-            getPremulBitmapCreateFlags(isMutable), NULL, NULL);
+    return GraphicsJNI::createBitmap(env, nativeBitmap,
+            getPremulBitmapCreateFlags(isMutable));
 }
 
 static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
                            jint dstConfigHandle, jboolean isMutable) {
-    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
+    SkBitmap src;
+    reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
     SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
     SkBitmap            result;
     JavaPixelAllocator  allocator(env);
 
-    if (!src->copyTo(&result, dstCT, &allocator)) {
+    if (!src.copyTo(&result, dstCT, &allocator)) {
         return NULL;
     }
-    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
-            getPremulBitmapCreateFlags(isMutable), NULL, NULL);
+    Bitmap* bitmap = allocator.getStorageObjAndReset();
+    return GraphicsJNI::createBitmap(env, bitmap,
+            getPremulBitmapCreateFlags(isMutable));
 }
 
 static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    delete bitmap;
+    LocalScopedBitmap bitmap(bitmapHandle);
+    bitmap->detachFromJava();
 }
 
 static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    bitmap->setPixels(NULL, NULL);
+    LocalScopedBitmap bitmap(bitmapHandle);
+    bitmap->freePixels();
     return JNI_TRUE;
 }
 
 static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
         jint width, jint height, jint configHandle, jint allocSize,
         jboolean requestPremul) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    LocalScopedBitmap bitmap(bitmapHandle);
     SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
 
     // ARGB_4444 is a deprecated format, convert automatically to 8888
@@ -383,11 +700,9 @@
         doThrowIAE(env, "Bitmap not large enough to support new configuration");
         return;
     }
-    SkPixelRef* ref = bitmap->pixelRef();
-    ref->ref();
     SkAlphaType alphaType;
-    if (bitmap->colorType() != kRGB_565_SkColorType
-            && bitmap->alphaType() == kOpaque_SkAlphaType) {
+    if (bitmap->info().colorType() != kRGB_565_SkColorType
+            && bitmap->info().alphaType() == kOpaque_SkAlphaType) {
         // If the original bitmap was set to opaque, keep that setting, unless it
         // was 565, which is required to be opaque.
         alphaType = kOpaque_SkAlphaType;
@@ -395,22 +710,7 @@
         // Otherwise respect the premultiplied request.
         alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
     }
-    bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType));
-    // FIXME: Skia thinks of an SkPixelRef as having a constant SkImageInfo (except for
-    // its alphatype), so it would make more sense from Skia's perspective to create a
-    // new SkPixelRef. That said, libhwui uses the pointer to the SkPixelRef as a key
-    // for its cache, so it won't realize this is the same Java Bitmap.
-    SkImageInfo& info = const_cast<SkImageInfo&>(ref->info());
-    // Use the updated from the SkBitmap, which may have corrected an invalid alphatype.
-    // (e.g. 565 non-opaque)
-    info = bitmap->info();
-    bitmap->setPixelRef(ref);
-
-    // notifyPixelsChanged will increment the generation ID even though the actual pixel data
-    // hasn't been touched. This signals the renderer that the bitmap (including width, height,
-    // colortype and alphatype) has changed.
-    ref->notifyPixelsChanged();
-    ref->unref();
+    bitmap->reconfigure(SkImageInfo::Make(width, height, colorType, alphaType));
 }
 
 // These must match the int values in Bitmap.java
@@ -423,7 +723,8 @@
 static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
                                 jint format, jint quality,
                                 jobject jstream, jbyteArray jstorage) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+
+    LocalScopedBitmap bitmap(bitmapHandle);
     SkImageEncoder::Type fm;
 
     switch (format) {
@@ -440,92 +741,92 @@
         return JNI_FALSE;
     }
 
+    if (!bitmap.valid()) {
+        return JNI_FALSE;
+    }
+
     bool success = false;
-    if (NULL != bitmap) {
-        SkAutoLockPixels alp(*bitmap);
 
-        if (NULL == bitmap->getPixels()) {
-            return JNI_FALSE;
-        }
+    std::unique_ptr<SkWStream> strm(CreateJavaOutputStreamAdaptor(env, jstream, jstorage));
+    if (!strm.get()) {
+        return JNI_FALSE;
+    }
 
-        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
-        if (NULL == strm) {
-            return JNI_FALSE;
-        }
-
-        SkImageEncoder* encoder = SkImageEncoder::Create(fm);
-        if (NULL != encoder) {
-            success = encoder->encodeStream(strm, *bitmap, quality);
-            delete encoder;
-        }
-        delete strm;
+    std::unique_ptr<SkImageEncoder> encoder(SkImageEncoder::Create(fm));
+    if (encoder.get()) {
+        SkBitmap skbitmap;
+        bitmap->getSkBitmap(&skbitmap);
+        success = encoder->encodeStream(strm.get(), skbitmap, quality);
     }
     return success ? JNI_TRUE : JNI_FALSE;
 }
 
 static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    bitmap->eraseColor(color);
+    LocalScopedBitmap bitmap(bitmapHandle);
+    SkBitmap skBitmap;
+    bitmap->getSkBitmap(&skBitmap);
+    skBitmap.eraseColor(color);
 }
 
 static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    LocalScopedBitmap bitmap(bitmapHandle);
     return static_cast<jint>(bitmap->rowBytes());
 }
 
 static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->colorType());
+    LocalScopedBitmap bitmap(bitmapHandle);
+    return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType());
 }
 
 static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    return static_cast<jint>(bitmap->getGenerationID());
+    LocalScopedBitmap bitmap(bitmapHandle);
+    return static_cast<jint>(bitmap->pixelRef()->getGenerationID());
 }
 
 static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    if (bitmap->alphaType() == kPremul_SkAlphaType) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    if (bitmap->info().alphaType() == kPremul_SkAlphaType) {
         return JNI_TRUE;
     }
     return JNI_FALSE;
 }
 
 static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
+    LocalScopedBitmap bitmap(bitmapHandle);
+    return !bitmap->info().isOpaque() ? JNI_TRUE : JNI_FALSE;
 }
 
 static void Bitmap_setHasAlpha(JNIEnv* env, jobject, jlong bitmapHandle,
         jboolean hasAlpha, jboolean requestPremul) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    LocalScopedBitmap bitmap(bitmapHandle);
     if (hasAlpha) {
-        bitmap->setAlphaType(requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
+        bitmap->pixelRef()->changeAlphaType(
+                requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
     } else {
-        bitmap->setAlphaType(kOpaque_SkAlphaType);
+        bitmap->pixelRef()->changeAlphaType(kOpaque_SkAlphaType);
     }
 }
 
 static void Bitmap_setPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
         jboolean isPremul) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    if (!bitmap->isOpaque()) {
+    LocalScopedBitmap bitmap(bitmapHandle);
+    if (!bitmap->info().isOpaque()) {
         if (isPremul) {
-            bitmap->setAlphaType(kPremul_SkAlphaType);
+            bitmap->pixelRef()->changeAlphaType(kPremul_SkAlphaType);
         } else {
-            bitmap->setAlphaType(kUnpremul_SkAlphaType);
+            bitmap->pixelRef()->changeAlphaType(kUnpremul_SkAlphaType);
         }
     }
 }
 
 static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    LocalScopedBitmap bitmap(bitmapHandle);
     return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;
 }
 
 static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
                                 jboolean hasMipMap) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    LocalScopedBitmap bitmap(bitmapHandle);
     bitmap->setHasHardwareMipMap(hasMipMap);
 }
 
@@ -580,8 +881,8 @@
         }
     }
 
-    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable);
-    if (NULL == buffer) {
+    android::Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable);
+    if (!nativeBitmap) {
         SkSafeUnref(ctable);
         return NULL;
     }
@@ -593,6 +894,7 @@
     android::Parcel::ReadableBlob blob;
     android::status_t status = p->readBlob(size, &blob);
     if (status) {
+        nativeBitmap->detachFromJava();
         doThrowRE(env, "Could not read bitmap from parcel blob.");
         return NULL;
     }
@@ -603,7 +905,7 @@
 
     blob.release();
 
-    return GraphicsJNI::createBitmap(env, bitmap.release(), buffer,
+    return GraphicsJNI::createBitmap(env, nativeBitmap,
             getPremulBitmapCreateFlags(isMutable), NULL, NULL, density);
 }
 
@@ -611,24 +913,25 @@
                                      jlong bitmapHandle,
                                      jboolean isMutable, jint density,
                                      jobject parcel) {
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
     if (parcel == NULL) {
         SkDebugf("------- writeToParcel null parcel\n");
         return JNI_FALSE;
     }
 
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
+    SkBitmap bitmap;
+    reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
 
     p->writeInt32(isMutable);
-    p->writeInt32(bitmap->colorType());
-    p->writeInt32(bitmap->alphaType());
-    p->writeInt32(bitmap->width());
-    p->writeInt32(bitmap->height());
-    p->writeInt32(bitmap->rowBytes());
+    p->writeInt32(bitmap.colorType());
+    p->writeInt32(bitmap.alphaType());
+    p->writeInt32(bitmap.width());
+    p->writeInt32(bitmap.height());
+    p->writeInt32(bitmap.rowBytes());
     p->writeInt32(density);
 
-    if (bitmap->colorType() == kIndex_8_SkColorType) {
-        SkColorTable* ctable = bitmap->getColorTable();
+    if (bitmap.colorType() == kIndex_8_SkColorType) {
+        SkColorTable* ctable = bitmap.getColorTable();
         if (ctable != NULL) {
             int count = ctable->count();
             p->writeInt32(count);
@@ -639,7 +942,7 @@
         }
     }
 
-    size_t size = bitmap->getSize();
+    size_t size = bitmap.getSize();
 
     android::Parcel::WritableBlob blob;
     android::status_t status = p->writeBlob(size, &blob);
@@ -648,14 +951,14 @@
         return JNI_FALSE;
     }
 
-    bitmap->lockPixels();
-    const void* pSrc =  bitmap->getPixels();
+    bitmap.lockPixels();
+    const void* pSrc =  bitmap.getPixels();
     if (pSrc == NULL) {
         memset(blob.data(), 0, size);
     } else {
         memcpy(blob.data(), pSrc, size);
     }
-    bitmap->unlockPixels();
+    bitmap.unlockPixels();
 
     blob.release();
     return JNI_TRUE;
@@ -664,17 +967,17 @@
 static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
                                    jlong srcHandle, jlong paintHandle,
                                    jintArray offsetXY) {
-    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
+    SkBitmap src;
+    reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
     const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle);
     SkIPoint  offset;
-    SkBitmap* dst = new SkBitmap;
+    SkBitmap dst;
     JavaPixelAllocator allocator(env);
 
-    src->extractAlpha(dst, paint, &allocator, &offset);
+    src.extractAlpha(&dst, paint, &allocator, &offset);
     // If Skia can't allocate pixels for destination bitmap, it resets
     // it, that is set its pixels buffer to NULL, and zero width and height.
-    if (dst->getPixels() == NULL && src->getPixels() != NULL) {
-        delete dst;
+    if (dst.getPixels() == NULL && src.getPixels() != NULL) {
         doThrowOOME(env, "failed to allocate pixels for alpha");
         return NULL;
     }
@@ -685,53 +988,55 @@
         env->ReleaseIntArrayElements(offsetXY, array, 0);
     }
 
-    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
-            getPremulBitmapCreateFlags(true), NULL, NULL);
+    return GraphicsJNI::createBitmap(env, allocator.getStorageObjAndReset(),
+            getPremulBitmapCreateFlags(true));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
         jint x, jint y) {
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    SkAutoLockPixels alp(*bitmap);
+    SkBitmap bitmap;
+    reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+    SkAutoLockPixels alp(bitmap);
 
-    ToColorProc proc = ChooseToColorProc(*bitmap);
+    ToColorProc proc = ChooseToColorProc(bitmap);
     if (NULL == proc) {
         return 0;
     }
-    const void* src = bitmap->getAddr(x, y);
+    const void* src = bitmap.getAddr(x, y);
     if (NULL == src) {
         return 0;
     }
 
     SkColor dst[1];
-    proc(dst, src, 1, bitmap->getColorTable());
+    proc(dst, src, 1, bitmap.getColorTable());
     return static_cast<jint>(dst[0]);
 }
 
 static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
         jintArray pixelArray, jint offset, jint stride,
         jint x, jint y, jint width, jint height) {
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    SkAutoLockPixels alp(*bitmap);
+    SkBitmap bitmap;
+    reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+    SkAutoLockPixels alp(bitmap);
 
-    ToColorProc proc = ChooseToColorProc(*bitmap);
+    ToColorProc proc = ChooseToColorProc(bitmap);
     if (NULL == proc) {
         return;
     }
-    const void* src = bitmap->getAddr(x, y);
+    const void* src = bitmap.getAddr(x, y);
     if (NULL == src) {
         return;
     }
 
-    SkColorTable* ctable = bitmap->getColorTable();
+    SkColorTable* ctable = bitmap.getColorTable();
     jint* dst = env->GetIntArrayElements(pixelArray, NULL);
     SkColor* d = (SkColor*)dst + offset;
     while (--height >= 0) {
         proc(d, src, width, ctable);
         d += stride;
-        src = (void*)((const char*)src + bitmap->rowBytes());
+        src = (void*)((const char*)src + bitmap.rowBytes());
     }
     env->ReleaseIntArrayElements(pixelArray, dst, 0);
 }
@@ -740,79 +1045,85 @@
 
 static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
         jint x, jint y, jint colorHandle) {
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkBitmap bitmap;
+    reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
     SkColor color = static_cast<SkColor>(colorHandle);
-    SkAutoLockPixels alp(*bitmap);
-    if (NULL == bitmap->getPixels()) {
+    SkAutoLockPixels alp(bitmap);
+    if (NULL == bitmap.getPixels()) {
         return;
     }
 
-    FromColorProc proc = ChooseFromColorProc(*bitmap);
+    FromColorProc proc = ChooseFromColorProc(bitmap);
     if (NULL == proc) {
         return;
     }
 
-    proc(bitmap->getAddr(x, y), &color, 1, x, y);
-    bitmap->notifyPixelsChanged();
+    proc(bitmap.getAddr(x, y), &color, 1, x, y);
+    bitmap.notifyPixelsChanged();
 }
 
 static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
         jintArray pixelArray, jint offset, jint stride,
         jint x, jint y, jint width, jint height) {
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkBitmap bitmap;
+    reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
     GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
-            x, y, width, height, *bitmap);
+            x, y, width, height, bitmap);
 }
 
 static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
                                       jlong bitmapHandle, jobject jbuffer) {
-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    SkAutoLockPixels alp(*bitmap);
-    const void* src = bitmap->getPixels();
+    SkBitmap bitmap;
+    reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+    SkAutoLockPixels alp(bitmap);
+    const void* src = bitmap.getPixels();
 
     if (NULL != src) {
         android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
 
         // the java side has already checked that buffer is large enough
-        memcpy(abp.pointer(), src, bitmap->getSize());
+        memcpy(abp.pointer(), src, bitmap.getSize());
     }
 }
 
 static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
                                         jlong bitmapHandle, jobject jbuffer) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    SkAutoLockPixels alp(*bitmap);
-    void* dst = bitmap->getPixels();
+    SkBitmap bitmap;
+    reinterpret_cast<Bitmap*>(bitmapHandle)->getSkBitmap(&bitmap);
+    SkAutoLockPixels alp(bitmap);
+    void* dst = bitmap.getPixels();
 
     if (NULL != dst) {
         android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
         // the java side has already checked that buffer is large enough
-        memcpy(dst, abp.pointer(), bitmap->getSize());
-        bitmap->notifyPixelsChanged();
+        memcpy(dst, abp.pointer(), bitmap.getSize());
+        bitmap.notifyPixelsChanged();
     }
 }
 
 static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
                               jlong bm1Handle) {
-    const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);
-    const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
-    if (bm0->width() != bm1->width() ||
-        bm0->height() != bm1->height() ||
-        bm0->colorType() != bm1->colorType()) {
+    SkBitmap bm0;
+    SkBitmap bm1;
+    reinterpret_cast<Bitmap*>(bm0Handle)->getSkBitmap(&bm0);
+    reinterpret_cast<Bitmap*>(bm1Handle)->getSkBitmap(&bm1);
+    if (bm0.width() != bm1.width() ||
+        bm0.height() != bm1.height() ||
+        bm0.colorType() != bm1.colorType()) {
         return JNI_FALSE;
     }
 
-    SkAutoLockPixels alp0(*bm0);
-    SkAutoLockPixels alp1(*bm1);
+    SkAutoLockPixels alp0(bm0);
+    SkAutoLockPixels alp1(bm1);
 
     // if we can't load the pixels, return false
-    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
+    if (NULL == bm0.getPixels() || NULL == bm1.getPixels()) {
         return JNI_FALSE;
     }
 
-    if (bm0->colorType() == kIndex_8_SkColorType) {
-        SkColorTable* ct0 = bm0->getColorTable();
-        SkColorTable* ct1 = bm1->getColorTable();
+    if (bm0.colorType() == kIndex_8_SkColorType) {
+        SkColorTable* ct0 = bm0.getColorTable();
+        SkColorTable* ct1 = bm1.getColorTable();
         if (NULL == ct0 || NULL == ct1) {
             return JNI_FALSE;
         }
@@ -829,16 +1140,16 @@
     // now compare each scanline. We can't do the entire buffer at once,
     // since we don't care about the pixel values that might extend beyond
     // the width (since the scanline might be larger than the logical width)
-    const int h = bm0->height();
-    const size_t size = bm0->width() * bm0->bytesPerPixel();
+    const int h = bm0.height();
+    const size_t size = bm0.width() * bm0.bytesPerPixel();
     for (int y = 0; y < h; y++) {
         // SkBitmap::getAddr(int, int) may return NULL due to unrecognized config
         // (ex: kRLE_Index8_Config). This will cause memcmp method to crash. Since bm0
         // and bm1 both have pixel data() (have passed NULL == getPixels() check),
         // those 2 bitmaps should be valid (only unrecognized), we return JNI_FALSE
         // to warn user those 2 unrecognized config bitmaps may be different.
-        void *bm0Addr = bm0->getAddr(0, y);
-        void *bm1Addr = bm1->getAddr(0, y);
+        void *bm0Addr = bm0.getAddr(0, y);
+        void *bm1Addr = bm1.getAddr(0, y);
 
         if(bm0Addr == NULL || bm1Addr == NULL) {
             return JNI_FALSE;
@@ -851,15 +1162,9 @@
     return JNI_TRUE;
 }
 
-static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    bitmap->lockPixels();
-    bitmap->unlockPixels();
-}
-
 static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) {
-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    SkPixelRef* pixelRef = bitmap ? bitmap->pixelRef() : nullptr;
+    LocalScopedBitmap bitmap(bitmapHandle);
+    SkPixelRef* pixelRef = bitmap.valid() ? bitmap->pixelRef() : nullptr;
     SkSafeRef(pixelRef);
     return reinterpret_cast<jlong>(pixelRef);
 }
@@ -902,7 +1207,6 @@
     {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
                                             (void*)Bitmap_copyPixelsFromBuffer },
     {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
-    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
     {   "nativeRefPixelRef",        "(J)J", (void*)Bitmap_refPixelRef },
 };
 
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
new file mode 100644
index 0000000..d6e5c61
--- /dev/null
+++ b/core/jni/android/graphics/Bitmap.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+#ifndef BITMAP_H_
+#define BITMAP_H_
+
+#include <jni.h>
+#include <SkBitmap.h>
+#include <SkColorTable.h>
+#include <SkImageInfo.h>
+#include <utils/Mutex.h>
+#include <memory>
+
+namespace android {
+
+enum class PixelStorageType {
+    Invalid,
+    External,
+    Java,
+};
+
+class WrappedPixelRef;
+
+typedef void (*FreeFunc)(void* addr, void* context);
+
+/**
+ * Glue-thingy that deals with managing the interaction between the Java
+ * Bitmap object & SkBitmap along with trying to map a notion of strong/weak
+ * lifecycles onto SkPixelRef which only has strong counts to avoid requiring
+ * two GC passes to free the byte[] that backs a Bitmap.
+ *
+ * Since not all Bitmaps are byte[]-backed it also supports external allocations,
+ * which currently is used by screenshots to wrap a gralloc buffer.
+ */
+class Bitmap {
+public:
+    Bitmap(JNIEnv* env, jbyteArray storageObj, void* address,
+            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+    Bitmap(void* address, void* context, FreeFunc freeFunc,
+            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+
+    const SkImageInfo& info() const;
+
+    // Returns nullptr if it is not backed by a jbyteArray
+    jbyteArray javaByteArray() const {
+        return mPixelStorageType == PixelStorageType::Java
+                ? mPixelStorage.java.jstrongRef : nullptr;
+    }
+
+    int width() const { return info().width(); }
+    int height() const { return info().height(); }
+    size_t rowBytes() const;
+    SkPixelRef* pixelRef() const;
+    bool valid() const { return mPixelStorageType != PixelStorageType::Invalid; }
+
+    void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+    void reconfigure(const SkImageInfo& info);
+
+    void getSkBitmap(SkBitmap* outBitmap);
+    void detachFromJava();
+
+    void freePixels();
+
+    bool hasHardwareMipMap();
+    void setHasHardwareMipMap(bool hasMipMap);
+
+private:
+    friend class WrappedPixelRef;
+
+    ~Bitmap();
+    void doFreePixels();
+    void onStrongRefDestroyed();
+
+    void pinPixelsLocked();
+    void unpinPixelsLocked();
+    JNIEnv* jniEnv();
+    bool shouldDisposeSelfLocked();
+    void assertValid() const;
+
+    android::Mutex mLock;
+    int mPinnedRefCount = 0;
+    std::unique_ptr<WrappedPixelRef> mPixelRef;
+    PixelStorageType mPixelStorageType;
+    bool mAttachedToJava = true;
+
+    union {
+        struct {
+            void* address;
+            void* context;
+            FreeFunc freeFunc;
+        } external;
+        struct {
+            JavaVM* jvm;
+            jweak jweakRef;
+            jbyteArray jstrongRef;
+        } java;
+    } mPixelStorage;
+};
+
+} // namespace android
+
+#endif /* BITMAP_H_ */
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index d4069a1..cdd397d 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -156,13 +156,11 @@
 
 class RecyclingPixelAllocator : public SkBitmap::Allocator {
 public:
-    RecyclingPixelAllocator(SkPixelRef* pixelRef, unsigned int size)
-            : mPixelRef(pixelRef), mSize(size) {
-        SkSafeRef(mPixelRef);
+    RecyclingPixelAllocator(android::Bitmap* bitmap, unsigned int size)
+            : mBitmap(bitmap), mSize(size) {
     }
 
     ~RecyclingPixelAllocator() {
-        SkSafeUnref(mPixelRef);
     }
 
     virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
@@ -185,11 +183,9 @@
             return false;
         }
 
-        // Create a new pixelref with the new ctable that wraps the previous pixelref
-        SkPixelRef* pr = new AndroidPixelRef(*static_cast<AndroidPixelRef*>(mPixelRef),
-                info, bitmap->rowBytes(), ctable);
+        mBitmap->reconfigure(info, bitmap->rowBytes(), ctable);
+        bitmap->setPixelRef(mBitmap->pixelRef());
 
-        bitmap->setPixelRef(pr)->unref();
         // since we're already allocated, we lockPixels right away
         // HeapAllocator/JavaPixelAllocator behaves this way too
         bitmap->lockPixels();
@@ -197,7 +193,7 @@
     }
 
 private:
-    SkPixelRef* const mPixelRef;
+    android::Bitmap* const mBitmap;
     const unsigned int mSize;
 };
 
@@ -258,27 +254,24 @@
     decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
     decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
 
-    SkBitmap* outputBitmap = NULL;
+    android::Bitmap* reuseBitmap = nullptr;
     unsigned int existingBufferSize = 0;
     if (javaBitmap != NULL) {
-        outputBitmap = GraphicsJNI::getSkBitmapDeprecated(env, javaBitmap);
-        if (outputBitmap->isImmutable()) {
+        reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
+        if (reuseBitmap->pixelRef()->isImmutable()) {
             ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
             javaBitmap = NULL;
-            outputBitmap = NULL;
+            reuseBitmap = nullptr;
         } else {
             existingBufferSize = GraphicsJNI::getBitmapAllocationByteCount(env, javaBitmap);
         }
     }
 
-    SkAutoTDelete<SkBitmap> adb(outputBitmap == NULL ? new SkBitmap : NULL);
-    if (outputBitmap == NULL) outputBitmap = adb.get();
-
     NinePatchPeeker peeker(decoder);
     decoder->setPeeker(&peeker);
 
     JavaPixelAllocator javaAllocator(env);
-    RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
+    RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize);
     ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
     SkBitmap::Allocator* outputAllocator = (javaBitmap != NULL) ?
             (SkBitmap::Allocator*)&recyclingAllocator : (SkBitmap::Allocator*)&javaAllocator;
@@ -374,6 +367,7 @@
         }
     }
 
+    SkBitmap outputBitmap;
     if (willScale) {
         // This is weird so let me explain: we could use the scale parameter
         // directly, but for historical reasons this is how the corresponding
@@ -388,26 +382,27 @@
         // FIXME: If the alphaType is kUnpremul and the image has alpha, the
         // colors may not be correct, since Skia does not yet support drawing
         // to/from unpremultiplied bitmaps.
-        outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
+        outputBitmap.setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
                 colorType, decodingBitmap.alphaType()));
-        if (!outputBitmap->tryAllocPixels(outputAllocator, NULL)) {
+        if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) {
             return nullObjectReturn("allocation failed for scaled bitmap");
         }
 
         // If outputBitmap's pixels are newly allocated by Java, there is no need
         // to erase to 0, since the pixels were initialized to 0.
         if (outputAllocator != &javaAllocator) {
-            outputBitmap->eraseColor(0);
+            outputBitmap.eraseColor(0);
         }
 
         SkPaint paint;
         paint.setFilterQuality(kLow_SkFilterQuality);
 
-        SkCanvas canvas(*outputBitmap);
+        SkCanvas canvas(outputBitmap);
         canvas.scale(sx, sy);
+        canvas.drawARGB(0x00, 0x00, 0x00, 0x00);
         canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
     } else {
-        outputBitmap->swap(decodingBitmap);
+        outputBitmap.swap(decodingBitmap);
     }
 
     if (padding) {
@@ -422,22 +417,19 @@
 
     // if we get here, we're in kDecodePixels_Mode and will therefore
     // already have a pixelref installed.
-    if (outputBitmap->pixelRef() == NULL) {
+    if (outputBitmap.pixelRef() == NULL) {
         return nullObjectReturn("Got null SkPixelRef");
     }
 
     if (!isMutable && javaBitmap == NULL) {
         // promise we will never change our pixels (great for sharing and pictures)
-        outputBitmap->setImmutable();
+        outputBitmap.setImmutable();
     }
 
-    // detach bitmap from its autodeleter, since we want to own it now
-    adb.detach();
-
     if (javaBitmap != NULL) {
         bool isPremultiplied = !requireUnpremultiplied;
-        GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied);
-        outputBitmap->notifyPixelsChanged();
+        GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap.info(), isPremultiplied);
+        outputBitmap.notifyPixelsChanged();
         // If a java bitmap was passed in for reuse, pass it back
         return javaBitmap;
     }
@@ -447,7 +439,7 @@
     if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
 
     // now create the java bitmap
-    return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
+    return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(),
             bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
 }
 
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index aeea808..08a3f6f 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -212,26 +212,21 @@
     region.fTop = start_y;
     region.fRight = start_x + width;
     region.fBottom = start_y + height;
-    SkBitmap* bitmap = NULL;
-    SkAutoTDelete<SkBitmap> adb;
+    SkBitmap bitmap;
 
     if (tileBitmap != NULL) {
         // Re-use bitmap.
-        bitmap = GraphicsJNI::getSkBitmapDeprecated(env, tileBitmap);
-    }
-    if (bitmap == NULL) {
-        bitmap = new SkBitmap;
-        adb.reset(bitmap);
+        GraphicsJNI::getSkBitmap(env, tileBitmap, &bitmap);
     }
 
-    if (!brd->decodeRegion(bitmap, region, prefColorType, sampleSize)) {
+    if (!brd->decodeRegion(&bitmap, region, prefColorType, sampleSize)) {
         return nullObjectReturn("decoder->decodeRegion returned false");
     }
 
     // update options (if any)
     if (NULL != options) {
-        env->SetIntField(options, gOptions_widthFieldID, bitmap->width());
-        env->SetIntField(options, gOptions_heightFieldID, bitmap->height());
+        env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
+        env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
         // TODO: set the mimeType field with the data from the codec.
         // but how to reuse a set of strings, rather than allocating new one
         // each time?
@@ -240,19 +235,16 @@
     }
 
     if (tileBitmap != NULL) {
-        bitmap->notifyPixelsChanged();
+        bitmap.notifyPixelsChanged();
         return tileBitmap;
     }
 
-    // detach bitmap from its autodeleter, since we want to own it now
-    adb.detach();
-
     JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
-    jbyteArray buff = allocator->getStorageObjAndReset();
 
     int bitmapCreateFlags = 0;
     if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
-    return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1);
+    return GraphicsJNI::createBitmap(env, allocator->getStorageObjAndReset(),
+            bitmapCreateFlags);
 }
 
 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index f793df1..0deb8cc 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -154,7 +154,7 @@
 static jfieldID gPointF_yFieldID;
 
 static jclass   gBitmap_class;
-static jfieldID gBitmap_skBitmapPtr;
+static jfieldID gBitmap_nativePtr;
 static jmethodID gBitmap_constructorMethodID;
 static jmethodID gBitmap_reinitMethodID;
 static jmethodID gBitmap_getAllocationByteCountMethodID;
@@ -338,27 +338,22 @@
     return static_cast<SkColorType>(gConfig2ColorType[legacyConfig]);
 }
 
-SkBitmap* GraphicsJNI::getSkBitmapDeprecated(JNIEnv* env, jobject bitmap) {
+android::Bitmap* GraphicsJNI::getBitmap(JNIEnv* env, jobject bitmap) {
     SkASSERT(env);
     SkASSERT(bitmap);
     SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
-    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_skBitmapPtr);
-    SkBitmap* b = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
+    android::Bitmap* b = reinterpret_cast<android::Bitmap*>(bitmapHandle);
     SkASSERT(b);
     return b;
 }
 
 void GraphicsJNI::getSkBitmap(JNIEnv* env, jobject bitmap, SkBitmap* outBitmap) {
-    // TODO: We have to copy from the existing bitmap due to rowBytes not
-    // being updated on the SkPixelRef at reconfigure time. This is a short term
-    // problem that will be fixed with the specialized wrapper
-    *outBitmap = *getSkBitmapDeprecated(env, bitmap);
+    getBitmap(env, bitmap)->getSkBitmap(outBitmap);
 }
 
 SkPixelRef* GraphicsJNI::getSkPixelRef(JNIEnv* env, jobject bitmap) {
-    jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_skBitmapPtr);
-    SkBitmap* b = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    return b->pixelRef();
+    return getBitmap(env, bitmap)->pixelRef();
 }
 
 SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) {
@@ -396,47 +391,43 @@
 ///////////////////////////////////////////////////////////////////////////////////////////
 
 // Assert that bitmap's SkAlphaType is consistent with isPremultiplied.
-static void assert_premultiplied(const SkBitmap& bitmap, bool isPremultiplied) {
+static void assert_premultiplied(const SkImageInfo& info, bool isPremultiplied) {
     // kOpaque_SkAlphaType and kIgnore_SkAlphaType mean that isPremultiplied is
     // irrelevant. This just tests to ensure that the SkAlphaType is not
     // opposite of isPremultiplied.
     if (isPremultiplied) {
-        SkASSERT(bitmap.alphaType() != kUnpremul_SkAlphaType);
+        SkASSERT(info.alphaType() != kUnpremul_SkAlphaType);
     } else {
-        SkASSERT(bitmap.alphaType() != kPremul_SkAlphaType);
+        SkASSERT(info.alphaType() != kPremul_SkAlphaType);
     }
 }
 
-jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
-        int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, int density)
-{
-    SkASSERT(bitmap);
-    SkASSERT(bitmap->pixelRef());
-    SkASSERT(!env->ExceptionCheck());
+jobject GraphicsJNI::createBitmap(JNIEnv* env, android::Bitmap* bitmap,
+        int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets,
+        int density) {
     bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable;
     bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied;
-
     // The caller needs to have already set the alpha type properly, so the
     // native SkBitmap stays in sync with the Java Bitmap.
-    assert_premultiplied(*bitmap, isPremultiplied);
+    assert_premultiplied(bitmap->info(), isPremultiplied);
 
     jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
-            reinterpret_cast<jlong>(bitmap), buffer,
+            reinterpret_cast<jlong>(bitmap), bitmap->javaByteArray(),
             bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied,
             ninePatchChunk, ninePatchInsets);
     hasException(env); // For the side effect of logging.
     return obj;
 }
 
-void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap,
+void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info,
         bool isPremultiplied)
 {
     // The caller needs to have already set the alpha type properly, so the
     // native SkBitmap stays in sync with the Java Bitmap.
-    assert_premultiplied(*bitmap, isPremultiplied);
+    assert_premultiplied(info, isPremultiplied);
 
     env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID,
-            bitmap->width(), bitmap->height(), isPremultiplied);
+            info.width(), info.height(), isPremultiplied);
 }
 
 int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap)
@@ -477,51 +468,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-AndroidPixelRef::AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* storage,
-        size_t rowBytes, jbyteArray storageObj, SkColorTable* ctable) :
-        SkMallocPixelRef(info, storage, rowBytes, ctable, (storageObj == NULL)),
-        fWrappedPixelRef(NULL) {
-    SkASSERT(storage);
-    SkASSERT(storageObj);
-    SkASSERT(env);
-
-    if (env->GetJavaVM(&fVM) != JNI_OK) {
-        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
-        sk_throw();
-    }
-
-    fStorageObj = (jbyteArray) env->NewGlobalRef(storageObj);
-}
-
-AndroidPixelRef::AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info,
-        size_t rowBytes, SkColorTable* ctable) :
-        SkMallocPixelRef(info, wrappedPixelRef.getAddr(), rowBytes, ctable, false),
-        fWrappedPixelRef(wrappedPixelRef.fWrappedPixelRef ?
-                wrappedPixelRef.fWrappedPixelRef : &wrappedPixelRef)
-{
-    SkASSERT(fWrappedPixelRef);
-    SkSafeRef(fWrappedPixelRef);
-
-    // don't need to initialize this, as all the relevant logic delegates to the wrapped ref
-    fStorageObj = NULL;
-}
-
-AndroidPixelRef::~AndroidPixelRef() {
-    if (fWrappedPixelRef) {
-        SkSafeUnref(fWrappedPixelRef);
-    } else {
-        SkASSERT(fStorageObj);
-        JNIEnv* env = vm2env(fVM);
-        env->DeleteGlobalRef(fStorageObj);
-    }
-
-    if (android::uirenderer::Caches::hasInstance()) {
-        android::uirenderer::Caches::getInstance().textureCache.releaseTexture(getStableID());
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
 static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) {
     int32_t rowBytes32 = SkToS32(bitmap.rowBytes());
     int64_t bigSize = (int64_t)bitmap.height() * rowBytes32;
@@ -533,7 +479,7 @@
     return true;
 }
 
-jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
+android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
                                              SkColorTable* ctable) {
     const SkImageInfo& info = bitmap->info();
     if (info.fColorType == kUnknown_SkColorType) {
@@ -562,13 +508,14 @@
         return NULL;
     }
     SkASSERT(addr);
-    SkPixelRef* pr = new AndroidPixelRef(env, info, (void*) addr, rowBytes, arrayObj, ctable);
-    bitmap->setPixelRef(pr)->unref();
+    android::Bitmap* wrapper = new android::Bitmap(env, arrayObj, (void*) addr,
+            info, rowBytes, ctable);
+    wrapper->getSkBitmap(bitmap);
     // since we're already allocated, we lockPixels right away
     // HeapAllocator behaves this way too
     bitmap->lockPixels();
 
-    return arrayObj;
+    return wrapper;
 }
 
 struct AndroidPixelRefContext {
@@ -627,21 +574,22 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env)
-    : fStorageObj(NULL),
-      fAllocCount(0) {
-    if (env->GetJavaVM(&fVM) != JNI_OK) {
-        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
-        sk_throw();
+JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) {
+    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK,
+            "env->GetJavaVM failed");
+}
+
+JavaPixelAllocator::~JavaPixelAllocator() {
+    if (mStorage) {
+        mStorage->detachFromJava();
     }
 }
 
 bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
-    JNIEnv* env = vm2env(fVM);
+    JNIEnv* env = vm2env(mJavaVM);
 
-    fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
-    fAllocCount += 1;
-    return fStorageObj != NULL;
+    mStorage = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+    return mStorage != nullptr;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -687,7 +635,7 @@
     gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F");
 
     gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
-    gBitmap_skBitmapPtr = getFieldIDCheck(env, gBitmap_class, "mSkBitmapPtr", "J");
+    gBitmap_nativePtr = getFieldIDCheck(env, gBitmap_class, "mNativePtr", "J");
     gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(J[BIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
     gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V");
     gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 8eb43f8..e748bac 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -1,6 +1,7 @@
 #ifndef _ANDROID_GRAPHICS_GRAPHICS_JNI_H_
 #define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_
 
+#include "Bitmap.h"
 #include "SkBitmap.h"
 #include "SkDevice.h"
 #include "SkPixelRef.h"
@@ -49,7 +50,7 @@
     static void point_to_jpointf(const SkPoint& point, JNIEnv*, jobject jpointf);
 
     static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas);
-    static SkBitmap* getSkBitmapDeprecated(JNIEnv*, jobject bitmap);
+    static android::Bitmap* getBitmap(JNIEnv*, jobject bitmap);
     static void getSkBitmap(JNIEnv*, jobject bitmap, SkBitmap* outBitmap);
     static SkPixelRef* getSkPixelRef(JNIEnv*, jobject bitmap);
     static SkRegion* getNativeRegion(JNIEnv*, jobject region);
@@ -71,22 +72,18 @@
     */
     static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig);
 
-    /** Create a java Bitmap object given the native bitmap (required) and optional
-        storage array (may be null).
-        bitmap's SkAlphaType must already be in sync with bitmapCreateFlags.
+    /*
+     * Create a java Bitmap object given the native bitmap
+     * bitmap's SkAlphaType must already be in sync with bitmapCreateFlags.
     */
-    static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
-            int bitmapCreateFlags, jbyteArray ninePatch, jobject ninePatchInsets, int density = -1);
-
-    static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, int bitmapCreateFlags,
-            jbyteArray ninePatch, int density = -1) {
-        return createBitmap(env, bitmap, NULL, bitmapCreateFlags, ninePatch, NULL, density);
-    }
+    static jobject createBitmap(JNIEnv* env, android::Bitmap* bitmap,
+            int bitmapCreateFlags, jbyteArray ninePatchChunk = NULL,
+            jobject ninePatchInsets = NULL, int density = -1);
 
     /** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in
         sync with isPremultiplied
     */
-    static void reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap,
+    static void reinitBitmap(JNIEnv* env, jobject javaBitmap, const SkImageInfo& info,
             bool isPremultiplied);
 
     static int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap);
@@ -95,7 +92,7 @@
 
     static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
 
-    static jbyteArray allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
+    static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
             SkColorTable* ctable);
 
     /**
@@ -113,30 +110,6 @@
     static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset,
             int srcStride, int x, int y, int width, int height,
             const SkBitmap& dstBitmap);
-
-    static jbyteArray getBitmapStorageObj(SkPixelRef *pixref);
-};
-
-class AndroidPixelRef : public SkMallocPixelRef {
-public:
-    AndroidPixelRef(JNIEnv* env, const SkImageInfo& info, void* storage, size_t rowBytes,
-            jbyteArray storageObj, SkColorTable* ctable);
-
-    /**
-     * Creates an AndroidPixelRef that wraps (and refs) another to reuse/share
-     * the same storage and java byte array refcounting, yet have a different
-     * color table.
-     */
-    AndroidPixelRef(AndroidPixelRef& wrappedPixelRef, const SkImageInfo& info,
-            size_t rowBytes, SkColorTable* ctable);
-
-    virtual ~AndroidPixelRef();
-
-private:
-    AndroidPixelRef* const fWrappedPixelRef; // if set, delegate memory management calls to this
-
-    JavaVM* fVM;
-    jbyteArray fStorageObj; // The Java byte[] object used as the bitmap backing store
 };
 
 /** Allocator which allocates the backing buffer in the Java heap.
@@ -147,30 +120,22 @@
 class JavaPixelAllocator : public SkBitmap::Allocator {
 public:
     JavaPixelAllocator(JNIEnv* env);
-    // overrides
-    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
+    ~JavaPixelAllocator();
 
-    /** Return the Java array object created for the last allocation.
-     *  This returns a local JNI reference which the caller is responsible
-     *  for storing appropriately (usually by passing it to the Bitmap
-     *  constructor).
-     */
-    jbyteArray getStorageObj() { return fStorageObj; }
+    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override;
 
-    /** Same as getStorageObj(), but also resets the allocator so that it
-     *  can allocate again.
+    /**
+     * Fetches the backing allocation object. Must be called!
      */
-    jbyteArray getStorageObjAndReset() {
-        jbyteArray result = fStorageObj;
-        fStorageObj = NULL;
-        fAllocCount = 0;
+    android::Bitmap* getStorageObjAndReset() {
+        android::Bitmap* result = mStorage;
+        mStorage = NULL;
         return result;
     };
 
 private:
-    JavaVM* fVM;
-    jbyteArray fStorageObj;
-    int fAllocCount;
+    JavaVM* mJavaVM;
+    android::Bitmap* mStorage = nullptr;
 };
 
 enum JNIAccess {
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 6afb226..8b65fd1 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -252,6 +252,7 @@
         sessionId,
         AudioRecord::TRANSFER_DEFAULT,
         flags,
+        -1, -1,        // default uid, pid
         paa);
 
     if (status != NO_ERROR) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index daf5a61..db495dd 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -999,6 +999,13 @@
     return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
 }
 
+static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz,
+                                                                        jlong themeHandle)
+{
+    ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+    return theme->getChangingConfigurations();
+}
+
 static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
                                                    jlong themeHandle, jint pri,
                                                    jstring tag, jstring prefix)
@@ -2103,6 +2110,8 @@
         (void*) android_content_AssetManager_copyTheme },
     { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
         (void*) android_content_AssetManager_loadThemeAttributeValue },
+    { "getThemeChangingConfigurations", "(J)I",
+        (void*) android_content_AssetManager_getThemeChangingConfigurations },
     { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
         (void*) android_content_AssetManager_dumpTheme },
     { "applyStyle","(JIIJ[I[I[I)Z",
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 39449b0..bb8ef83 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -86,24 +86,6 @@
     renderer->finish();
 }
 
-static void android_view_DisplayListCanvas_setProperty(JNIEnv* env,
-        jobject clazz, jstring name, jstring value) {
-    if (!Caches::hasInstance()) {
-        ALOGW("can't set property, no Caches instance");
-        return;
-    }
-
-    if (name == NULL || value == NULL) {
-        ALOGW("can't set prop, null passed");
-    }
-
-    const char* nameCharArray = env->GetStringUTFChars(name, NULL);
-    const char* valueCharArray = env->GetStringUTFChars(value, NULL);
-    Caches::getInstance().setTempProperty(nameCharArray, valueCharArray);
-    env->ReleaseStringUTFChars(name, nameCharArray);
-    env->ReleaseStringUTFChars(name, valueCharArray);
-}
-
 // ----------------------------------------------------------------------------
 // Functor
 // ----------------------------------------------------------------------------
@@ -268,8 +250,6 @@
     { "nPrepare",           "(J)V",            (void*) android_view_DisplayListCanvas_prepare },
     { "nPrepareDirty",      "(JIIII)V",        (void*) android_view_DisplayListCanvas_prepareDirty },
     { "nFinish",            "(J)V",            (void*) android_view_DisplayListCanvas_finish },
-    { "nSetProperty",       "(Ljava/lang/String;Ljava/lang/String;)V",
-            (void*) android_view_DisplayListCanvas_setProperty },
 
     { "nCallDrawGLFunction", "(JJ)V",          (void*) android_view_DisplayListCanvas_callDrawGLFunction },
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a8355c2..1965cd3 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -23,6 +23,7 @@
 
 #include "android_os_Parcel.h"
 #include "android_util_Binder.h"
+#include "android/graphics/Bitmap.h"
 #include "android/graphics/GraphicsJNI.h"
 #include "android/graphics/Region.h"
 
@@ -168,22 +169,19 @@
         }
     }
 
-    const ssize_t rowBytes =
+    const size_t rowBytes =
             screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
 
-    SkBitmap* bitmap = new SkBitmap();
-    bitmap->setInfo(screenshotInfo, (size_t)rowBytes);
-    if (screenshotInfo.fWidth > 0 && screenshotInfo.fHeight > 0) {
-        // takes ownership of ScreenshotClient
-        SkMallocPixelRef* pixels = SkMallocPixelRef::NewWithProc(screenshotInfo,
-                (size_t) rowBytes, NULL, (void*) screenshot->getPixels(), &DeleteScreenshot,
-                (void*) (screenshot.get()));
-        screenshot.detach();
-        pixels->setImmutable();
-        bitmap->setPixelRef(pixels)->unref();
-        bitmap->lockPixels();
+    if (!screenshotInfo.fWidth || !screenshotInfo.fHeight) {
+        return NULL;
     }
 
+    Bitmap* bitmap = new Bitmap(
+            (void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot,
+            screenshotInfo, rowBytes, nullptr);
+    screenshot.detach();
+    bitmap->pixelRef()->setImmutable();
+
     return GraphicsJNI::createBitmap(env, bitmap,
             GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
 }
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 4ccbb41..5d5465b 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -388,6 +388,15 @@
     RenderProxy::trimMemory(level);
 }
 
+static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz,
+        jstring name, jstring value) {
+    const char* nameCharArray = env->GetStringUTFChars(name, NULL);
+    const char* valueCharArray = env->GetStringUTFChars(value, NULL);
+    RenderProxy::overrideProperty(nameCharArray, valueCharArray);
+    env->ReleaseStringUTFChars(name, nameCharArray);
+    env->ReleaseStringUTFChars(name, valueCharArray);
+}
+
 static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz,
         jlong proxyPtr) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -466,6 +475,7 @@
     { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture },
     { "nDestroyHardwareResources", "(J)V", (void*) android_view_ThreadedRenderer_destroyHardwareResources },
     { "nTrimMemory", "(I)V", (void*) android_view_ThreadedRenderer_trimMemory },
+    { "nOverrideProperty", "(Ljava/lang/String;Ljava/lang/String;)V",  (void*) android_view_ThreadedRenderer_overrideProperty },
     { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
     { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing },
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 942e6a6..a162b4a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2433,6 +2433,12 @@
     <permission android:name="android.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT"
                 android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows applications to kill UIDs.
+        <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.KILL_UID"
+                android:protectionLevel="signature" />
+
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 39ea2bf..a986b75 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -46,7 +46,7 @@
     <!-- Flag specifying whether VT should be available for carrier: independent of
          carrier provisioning. If false: hard disabled. If true: then depends on carrier
          provisioning, availability etc -->
-    <bool name="config_carrier_vt_available">true</bool>
+    <bool name="config_carrier_vt_available">false</bool>
 
     <!-- Flag specifying whether VoLTE availability is based on provisioning -->
     <bool name="config_carrier_volte_provisioned">true</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f36d448..55b32e1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -255,6 +255,8 @@
     <string-array name="wfcOperatorErrorNotificationMessages" />
     <!-- Template for showing cellular network operator name while WFC is active -->
     <string name="wfcSpnFormat">%s</string>
+    <!-- Template for showing operator name for data connection while WFC is active -->
+    <string name="wfcDataSpnFormat">%s</string>
     <!-- WFC, summary for Disabled -->
     <string name="wifi_calling_off_summary">Off</string>
     <!-- WFC, summary for Wi-Fi Preferred -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f8d276f..a57b3b8 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -773,6 +773,7 @@
   <java-symbol type="array" name="wfcOperatorErrorAlertMessages" />
   <java-symbol type="array" name="wfcOperatorErrorNotificationMessages" />
   <java-symbol type="string" name="wfcSpnFormat" />
+  <java-symbol type="string" name="wfcDataSpnFormat" />
   <java-symbol type="string" name="wifi_calling_off_summary" />
   <java-symbol type="string" name="wfc_mode_wifi_preferred_summary" />
   <java-symbol type="string" name="wfc_mode_cellular_preferred_summary" />
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index be5c52b..c850b07 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -41,13 +41,13 @@
      */
     public static final int DENSITY_NONE = 0;
 
-    private final long mSkBitmapPtr;
-
     /**
      * Backing buffer for the Bitmap.
      */
     private byte[] mBuffer;
 
+    // Convenience for JNI access
+    private final long mNativePtr;
     private final BitmapFinalizer mFinalizer;
 
     private final boolean mIsMutable;
@@ -115,17 +115,16 @@
         mRequestPremultiplied = requestPremultiplied;
         mBuffer = buffer;
 
-        // we delete this in our finalizer
-        mSkBitmapPtr = nativeBitmap;
-
         mNinePatchChunk = ninePatchChunk;
         mNinePatchInsets = ninePatchInsets;
         if (density >= 0) {
             mDensity = density;
         }
 
-        int nativeAllocationByteCount = buffer == null ? getByteCount() : 0;
-        mFinalizer = new BitmapFinalizer(nativeBitmap, nativeAllocationByteCount);
+        mNativePtr = nativeBitmap;
+        mFinalizer = new BitmapFinalizer(nativeBitmap);
+        int nativeAllocationByteCount = (buffer == null ? getByteCount() : 0);
+        mFinalizer.setNativeAllocationByteCount(nativeAllocationByteCount);
     }
 
     /**
@@ -223,8 +222,8 @@
             throw new IllegalStateException("native-backed bitmaps may not be reconfigured");
         }
 
-        nativeReconfigure(mSkBitmapPtr, width, height, config.nativeInt, mBuffer.length,
-                mRequestPremultiplied);
+        nativeReconfigure(mFinalizer.mNativeBitmap, width, height, config.nativeInt,
+                mBuffer.length, mRequestPremultiplied);
         mWidth = width;
         mHeight = height;
     }
@@ -301,7 +300,7 @@
      */
     public void recycle() {
         if (!mRecycled && mFinalizer.mNativeBitmap != 0) {
-            if (nativeRecycle(mSkBitmapPtr)) {
+            if (nativeRecycle(mFinalizer.mNativeBitmap)) {
                 // return value indicates whether native pixel object was actually recycled.
                 // false indicates that it is still in use at the native level and these
                 // objects should not be collected now. They will be collected later when the
@@ -331,7 +330,7 @@
      * @return The current generation ID for this bitmap.
      */
     public int getGenerationId() {
-        return nativeGenerationId(mSkBitmapPtr);
+        return nativeGenerationId(mFinalizer.mNativeBitmap);
     }
 
     /**
@@ -487,7 +486,7 @@
             throw new RuntimeException("Buffer not large enough for pixels");
         }
 
-        nativeCopyPixelsToBuffer(mSkBitmapPtr, dst);
+        nativeCopyPixelsToBuffer(mFinalizer.mNativeBitmap, dst);
 
         // now update the buffer's position
         int position = dst.position();
@@ -527,7 +526,7 @@
             throw new RuntimeException("Buffer not large enough for pixels");
         }
 
-        nativeCopyPixelsFromBuffer(mSkBitmapPtr, src);
+        nativeCopyPixelsFromBuffer(mFinalizer.mNativeBitmap, src);
 
         // now update the buffer's position
         int position = src.position();
@@ -549,7 +548,7 @@
      */
     public Bitmap copy(Config config, boolean isMutable) {
         checkRecycled("Can't copy a recycled bitmap");
-        Bitmap b = nativeCopy(mSkBitmapPtr, config.nativeInt, isMutable);
+        Bitmap b = nativeCopy(mFinalizer.mNativeBitmap, config.nativeInt, isMutable);
         if (b != null) {
             b.setPremultiplied(mRequestPremultiplied);
             b.mDensity = mDensity;
@@ -810,7 +809,7 @@
         }
         bm.setHasAlpha(hasAlpha);
         if (config == Config.ARGB_8888 && !hasAlpha) {
-            nativeErase(bm.mSkBitmapPtr, 0xff000000);
+            nativeErase(bm.mFinalizer.mNativeBitmap, 0xff000000);
         }
         // No need to initialize the bitmap to zeroes with other configs;
         // it is backed by a VM byte array which is by definition preinitialized
@@ -1000,8 +999,8 @@
             throw new IllegalArgumentException("quality must be 0..100");
         }
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
-        boolean result = nativeCompress(mSkBitmapPtr, format.nativeInt, quality,
-                              stream, new byte[WORKING_COMPRESS_STORAGE]);
+        boolean result = nativeCompress(mFinalizer.mNativeBitmap, format.nativeInt,
+                quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
         Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         return result;
     }
@@ -1041,7 +1040,7 @@
      * @see BitmapFactory.Options#inPremultiplied
      */
     public final boolean isPremultiplied() {
-        return nativeIsPremultiplied(mSkBitmapPtr);
+        return nativeIsPremultiplied(mFinalizer.mNativeBitmap);
     }
 
     /**
@@ -1066,7 +1065,7 @@
      */
     public final void setPremultiplied(boolean premultiplied) {
         mRequestPremultiplied = premultiplied;
-        nativeSetPremultiplied(mSkBitmapPtr, premultiplied);
+        nativeSetPremultiplied(mFinalizer.mNativeBitmap, premultiplied);
     }
 
     /** Returns the bitmap's width */
@@ -1158,7 +1157,7 @@
      * @return number of bytes between rows of the native bitmap pixels.
      */
     public final int getRowBytes() {
-        return nativeRowBytes(mSkBitmapPtr);
+        return nativeRowBytes(mFinalizer.mNativeBitmap);
     }
 
     /**
@@ -1201,7 +1200,7 @@
      * that config, otherwise return null.
      */
     public final Config getConfig() {
-        return Config.nativeToConfig(nativeConfig(mSkBitmapPtr));
+        return Config.nativeToConfig(nativeConfig(mFinalizer.mNativeBitmap));
     }
 
     /** Returns true if the bitmap's config supports per-pixel alpha, and
@@ -1213,7 +1212,7 @@
      * it will return true by default.
      */
     public final boolean hasAlpha() {
-        return nativeHasAlpha(mSkBitmapPtr);
+        return nativeHasAlpha(mFinalizer.mNativeBitmap);
     }
 
     /**
@@ -1227,7 +1226,7 @@
      * non-opaque per-pixel alpha values.
      */
     public void setHasAlpha(boolean hasAlpha) {
-        nativeSetHasAlpha(mSkBitmapPtr, hasAlpha, mRequestPremultiplied);
+        nativeSetHasAlpha(mFinalizer.mNativeBitmap, hasAlpha, mRequestPremultiplied);
     }
 
     /**
@@ -1248,7 +1247,7 @@
      * @see #setHasMipMap(boolean)
      */
     public final boolean hasMipMap() {
-        return nativeHasMipMap(mSkBitmapPtr);
+        return nativeHasMipMap(mFinalizer.mNativeBitmap);
     }
 
     /**
@@ -1272,7 +1271,7 @@
      * @see #hasMipMap()
      */
     public final void setHasMipMap(boolean hasMipMap) {
-        nativeSetHasMipMap(mSkBitmapPtr, hasMipMap);
+        nativeSetHasMipMap(mFinalizer.mNativeBitmap, hasMipMap);
     }
 
     /**
@@ -1285,7 +1284,7 @@
         if (!isMutable()) {
             throw new IllegalStateException("cannot erase immutable bitmaps");
         }
-        nativeErase(mSkBitmapPtr, c);
+        nativeErase(mFinalizer.mNativeBitmap, c);
     }
 
     /**
@@ -1302,7 +1301,7 @@
     public int getPixel(int x, int y) {
         checkRecycled("Can't call getPixel() on a recycled bitmap");
         checkPixelAccess(x, y);
-        return nativeGetPixel(mSkBitmapPtr, x, y);
+        return nativeGetPixel(mFinalizer.mNativeBitmap, x, y);
     }
 
     /**
@@ -1335,7 +1334,7 @@
             return; // nothing to do
         }
         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
-        nativeGetPixels(mSkBitmapPtr, pixels, offset, stride,
+        nativeGetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride,
                         x, y, width, height);
     }
 
@@ -1416,7 +1415,7 @@
             throw new IllegalStateException();
         }
         checkPixelAccess(x, y);
-        nativeSetPixel(mSkBitmapPtr, x, y, color);
+        nativeSetPixel(mFinalizer.mNativeBitmap, x, y, color);
     }
 
     /**
@@ -1452,7 +1451,7 @@
             return; // nothing to do
         }
         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
-        nativeSetPixels(mSkBitmapPtr, pixels, offset, stride,
+        nativeSetPixels(mFinalizer.mNativeBitmap, pixels, offset, stride,
                         x, y, width, height);
     }
 
@@ -1490,7 +1489,7 @@
      */
     public void writeToParcel(Parcel p, int flags) {
         checkRecycled("Can't parcel a recycled bitmap");
-        if (!nativeWriteToParcel(mSkBitmapPtr, mIsMutable, mDensity, p)) {
+        if (!nativeWriteToParcel(mFinalizer.mNativeBitmap, mIsMutable, mDensity, p)) {
             throw new RuntimeException("native writeToParcel failed");
         }
     }
@@ -1536,7 +1535,7 @@
     public Bitmap extractAlpha(Paint paint, int[] offsetXY) {
         checkRecycled("Can't extractAlpha on a recycled bitmap");
         long nativePaint = paint != null ? paint.getNativeInstance() : 0;
-        Bitmap bm = nativeExtractAlpha(mSkBitmapPtr, nativePaint, offsetXY);
+        Bitmap bm = nativeExtractAlpha(mFinalizer.mNativeBitmap, nativePaint, offsetXY);
         if (bm == null) {
             throw new RuntimeException("Failed to extractAlpha on Bitmap");
         }
@@ -1550,7 +1549,8 @@
      *  If other is null, return false.
      */
     public boolean sameAs(Bitmap other) {
-        return this == other || (other != null && nativeSameAs(mSkBitmapPtr, other.mSkBitmapPtr));
+        return this == other || (other != null
+                && nativeSameAs(mFinalizer.mNativeBitmap, other.mFinalizer.mNativeBitmap));
     }
 
     /**
@@ -1565,7 +1565,9 @@
      * and therefore is harmless.
      */
     public void prepareToDraw() {
-        nativePrepareToDraw(mSkBitmapPtr);
+        // TODO: Consider having this start an async upload?
+        // With inPurgeable no-op'd there's currently no use for this
+        // method, but it could have interesting future uses.
     }
 
     /**
@@ -1574,7 +1576,7 @@
      * @hide
      * */
     public final long refSkPixelRef() {
-        return nativeRefPixelRef(mSkBitmapPtr);
+        return nativeRefPixelRef(mNativePtr);
     }
 
     private static class BitmapFinalizer {
@@ -1582,12 +1584,17 @@
 
         // Native memory allocated for the duration of the Bitmap,
         // if pixel data allocated into native memory, instead of java byte[]
-        private final int mNativeAllocationByteCount;
+        private int mNativeAllocationByteCount;
 
-        BitmapFinalizer(long nativeBitmap, int nativeAllocationByteCount) {
+        BitmapFinalizer(long nativeBitmap) {
             mNativeBitmap = nativeBitmap;
-            mNativeAllocationByteCount = nativeAllocationByteCount;
+        }
 
+        public void setNativeAllocationByteCount(int nativeByteCount) {
+            if (mNativeAllocationByteCount != 0) {
+                VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount);
+            }
+            mNativeAllocationByteCount = nativeByteCount;
             if (mNativeAllocationByteCount != 0) {
                 VMRuntime.getRuntime().registerNativeAllocation(mNativeAllocationByteCount);
             }
@@ -1600,9 +1607,7 @@
             } catch (Throwable t) {
                 // Ignore
             } finally {
-                if (mNativeAllocationByteCount != 0) {
-                    VMRuntime.getRuntime().registerNativeFree(mNativeAllocationByteCount);
-                }
+                setNativeAllocationByteCount(0);
                 nativeDestructor(mNativeBitmap);
                 mNativeBitmap = 0;
             }
@@ -1654,7 +1659,6 @@
                                                     long nativePaint,
                                                     int[] offsetXY);
 
-    private static native void nativePrepareToDraw(long nativeBitmap);
     private static native boolean nativeHasAlpha(long nativeBitmap);
     private static native boolean nativeIsPremultiplied(long nativeBitmap);
     private static native void nativeSetPremultiplied(long nativeBitmap,
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index df278c8..587e7fa 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1662,6 +1662,12 @@
                 uint32_t* inoutTypeSpecFlags = NULL,
                 ResTable_config* inoutConfig = NULL) const;
 
+        /**
+         * Returns a bit mask of configuration changes that will impact this
+         * theme (and thus require completely reloading it).
+         */
+        uint32_t getChangingConfigurations() const;
+
         void dumpToLog() const;
         
     private:
@@ -1688,6 +1694,7 @@
 
         const ResTable& mTable;
         package_info*   mPackages[Res_MAXPACKAGE];
+        uint32_t        mTypeSpecFlags;
     };
 
     void setParameters(const ResTable_config* params);
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 04ebe70..19a5beb 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3147,6 +3147,7 @@
 
 ResTable::Theme::Theme(const ResTable& table)
     : mTable(table)
+    , mTypeSpecFlags(0)
 {
     memset(mPackages, 0, sizeof(mPackages));
 }
@@ -3205,6 +3206,8 @@
         return N;
     }
 
+    mTypeSpecFlags |= bagTypeSpecFlags;
+
     uint32_t curPackage = 0xffffffff;
     ssize_t curPackageIndex = 0;
     package_info* curPI = NULL;
@@ -3323,6 +3326,8 @@
         }
     }
 
+    mTypeSpecFlags = other.mTypeSpecFlags;
+
     if (kDebugTableTheme) {
         ALOGI("Final theme:");
         dumpToLog();
@@ -3417,6 +3422,11 @@
             inoutTypeSpecFlags, inoutConfig);
 }
 
+uint32_t ResTable::Theme::getChangingConfigurations() const
+{
+    return mTypeSpecFlags;
+}
+
 void ResTable::Theme::dumpToLog() const
 {
     ALOGI("Theme %p:\n", this);
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index 836f868..8a4e609 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -63,6 +63,7 @@
     PixelBuffer.cpp \
     Program.cpp \
     ProgramCache.cpp \
+    Properties.cpp \
     RenderBufferCache.cpp \
     RenderNode.cpp \
     RenderProperties.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index fd5a2ce..f75d6a0 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -57,14 +57,8 @@
     init();
     initFont();
     initConstraints();
-    initProperties();
     initStaticProperties();
     initExtensions();
-    initTempProperties();
-
-    mDebugLevel = readDebugLevel();
-    ALOGD_IF(mDebugLevel != kDebugDisabled,
-            "Enabling debug mode %d", mDebugLevel);
 }
 
 bool Caches::init() {
@@ -77,10 +71,6 @@
 
     mFunctorsCount = 0;
 
-    debugLayersUpdates = false;
-    debugOverdraw = false;
-    debugStencilClip = kStencilHide;
-
     patchCache.init();
 
     mInitialized = true;
@@ -124,66 +114,6 @@
     }
 }
 
-bool Caches::initProperties() {
-    bool prevDebugLayersUpdates = debugLayersUpdates;
-    bool prevDebugOverdraw = debugOverdraw;
-    StencilClipDebug prevDebugStencilClip = debugStencilClip;
-
-    char property[PROPERTY_VALUE_MAX];
-    if (property_get(PROPERTY_DEBUG_LAYERS_UPDATES, property, nullptr) > 0) {
-        INIT_LOGD("  Layers updates debug enabled: %s", property);
-        debugLayersUpdates = !strcmp(property, "true");
-    } else {
-        debugLayersUpdates = false;
-    }
-
-    debugOverdraw = false;
-    if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) {
-        INIT_LOGD("  Overdraw debug enabled: %s", property);
-        if (!strcmp(property, "show")) {
-            debugOverdraw = true;
-            mOverdrawDebugColorSet = kColorSet_Default;
-        } else if (!strcmp(property, "show_deuteranomaly")) {
-            debugOverdraw = true;
-            mOverdrawDebugColorSet = kColorSet_Deuteranomaly;
-        }
-    }
-
-    // See Properties.h for valid values
-    if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, nullptr) > 0) {
-        INIT_LOGD("  Stencil clip debug enabled: %s", property);
-        if (!strcmp(property, "hide")) {
-            debugStencilClip = kStencilHide;
-        } else if (!strcmp(property, "highlight")) {
-            debugStencilClip = kStencilShowHighlight;
-        } else if (!strcmp(property, "region")) {
-            debugStencilClip = kStencilShowRegion;
-        }
-    } else {
-        debugStencilClip = kStencilHide;
-    }
-
-    if (property_get(PROPERTY_DISABLE_DRAW_DEFER, property, "false")) {
-        drawDeferDisabled = !strcasecmp(property, "true");
-        INIT_LOGD("  Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
-    } else {
-        drawDeferDisabled = false;
-        INIT_LOGD("  Draw defer enabled");
-    }
-
-    if (property_get(PROPERTY_DISABLE_DRAW_REORDER, property, "false")) {
-        drawReorderDisabled = !strcasecmp(property, "true");
-        INIT_LOGD("  Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
-    } else {
-        drawReorderDisabled = false;
-        INIT_LOGD("  Draw reorder enabled");
-    }
-
-    return (prevDebugLayersUpdates != debugLayersUpdates)
-            || (prevDebugOverdraw != debugOverdraw)
-            || (prevDebugStencilClip != debugStencilClip);
-}
-
 void Caches::terminate() {
     if (!mInitialized) return;
     mRegionMesh.release();
@@ -231,7 +161,9 @@
     };
     if (amount < 1) amount = 1;
     if (amount > 4) amount = 4;
-    return sOverdrawColors[mOverdrawDebugColorSet][amount - 1];
+
+    int overdrawColorIndex = static_cast<int>(Properties::overdrawColorSet);
+    return sOverdrawColors[overdrawColorIndex][amount - 1];
 }
 
 void Caches::dumpMemoryUsage() {
@@ -351,13 +283,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
-    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
+    if (mExtensions.hasTiledRendering() && !Properties::debugOverdraw) {
         glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
     }
 }
 
 void Caches::endTiling() {
-    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
+    if (mExtensions.hasTiledRendering() && !Properties::debugOverdraw) {
         glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
     }
 }
@@ -395,44 +327,5 @@
 // Temporary Properties
 ///////////////////////////////////////////////////////////////////////////////
 
-void Caches::initTempProperties() {
-    propertyLightRadius = -1.0f;
-    propertyLightPosY = -1.0f;
-    propertyLightPosZ = -1.0f;
-    propertyAmbientRatio = -1.0f;
-    propertyAmbientShadowStrength = -1;
-    propertySpotShadowStrength = -1;
-}
-
-void Caches::setTempProperty(const char* name, const char* value) {
-    ALOGD("setting property %s to %s", name, value);
-    if (!strcmp(name, "ambientRatio")) {
-        propertyAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0);
-        ALOGD("ambientRatio = %.2f", propertyAmbientRatio);
-        return;
-    } else if (!strcmp(name, "lightRadius")) {
-        propertyLightRadius = fmin(fmax(atof(value), 0.0), 3000.0);
-        ALOGD("lightRadius = %.2f", propertyLightRadius);
-        return;
-    } else if (!strcmp(name, "lightPosY")) {
-        propertyLightPosY = fmin(fmax(atof(value), 0.0), 3000.0);
-        ALOGD("lightPos Y = %.2f", propertyLightPosY);
-        return;
-    } else if (!strcmp(name, "lightPosZ")) {
-        propertyLightPosZ = fmin(fmax(atof(value), 0.0), 3000.0);
-        ALOGD("lightPos Z = %.2f", propertyLightPosZ);
-        return;
-    } else if (!strcmp(name, "ambientShadowStrength")) {
-        propertyAmbientShadowStrength = atoi(value);
-        ALOGD("ambient shadow strength = 0x%x out of 0xff", propertyAmbientShadowStrength);
-        return;
-    } else if (!strcmp(name, "spotShadowStrength")) {
-        propertySpotShadowStrength = atoi(value);
-        ALOGD("spot shadow strength = 0x%x out of 0xff", propertySpotShadowStrength);
-        return;
-    }
-    ALOGD("    failed");
-}
-
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 8aea8ff..804f609 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -80,7 +80,7 @@
     }
 
     static bool hasInstance() {
-        return sInstance != 0;
+        return sInstance != nullptr;
     }
 private:
     Caches(RenderState& renderState);
@@ -99,11 +99,6 @@
     bool init();
 
     /**
-     * Initialize global system properties.
-     */
-    bool initProperties();
-
-    /**
      * Flush the cache.
      *
      * @param mode Indicates how much of the cache should be flushed
@@ -117,14 +112,6 @@
     void terminate();
 
     /**
-     * Indicates whether the renderer is in debug mode.
-     * This debug mode provides limited information to app developers.
-     */
-    DebugLevel getDebugLevel() const {
-        return mDebugLevel;
-    }
-
-    /**
      * Returns a non-premultiplied ARGB color for the specified
      * amount of overdraw (1 for 1x, 2 for 2x, etc.)
      */
@@ -162,22 +149,9 @@
     void registerFunctors(uint32_t functorCount);
     void unregisterFunctors(uint32_t functorCount);
 
-    bool drawDeferDisabled;
-    bool drawReorderDisabled;
-
     // Misc
     GLint maxTextureSize;
 
-    // Debugging
-    bool debugLayersUpdates;
-    bool debugOverdraw;
-
-    enum StencilClipDebug {
-        kStencilHide,
-        kStencilShowHighlight,
-        kStencilShowRegion
-    };
-    StencilClipDebug debugStencilClip;
 private:
     // Declared before gradientCache and programCache which need this to initialize.
     // TODO: cleanup / move elsewhere
@@ -207,17 +181,6 @@
     PFNGLPUSHGROUPMARKEREXTPROC startMark;
     PFNGLPOPGROUPMARKEREXTPROC endMark;
 
-    // TEMPORARY properties
-    void initTempProperties();
-    void setTempProperty(const char* name, const char* value);
-
-    float propertyLightRadius;
-    float propertyLightPosY;
-    float propertyLightPosZ;
-    float propertyAmbientRatio;
-    int propertyAmbientShadowStrength;
-    int propertySpotShadowStrength;
-
     void setProgram(const ProgramDescription& description);
     void setProgram(Program* program);
 
@@ -227,10 +190,6 @@
     TextureState& textureState() { return *mTextureState; }
 
 private:
-    enum OverdrawColorSet {
-        kColorSet_Default = 0,
-        kColorSet_Deuteranomaly
-    };
 
     void initFont();
     void initExtensions();
@@ -249,13 +208,10 @@
     mutable Mutex mGarbageLock;
     Vector<Layer*> mLayerGarbage;
 
-    DebugLevel mDebugLevel;
     bool mInitialized;
 
     uint32_t mFunctorsCount;
 
-    OverdrawColorSet mOverdrawDebugColorSet;
-
     // TODO: move below to RenderState
     PixelBufferState* mPixelBufferState = nullptr;
     TextureState* mTextureState = nullptr;
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index dd6af03..6fcf958 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -28,6 +28,7 @@
 #include "DeferredDisplayList.h"
 #include "DisplayListOp.h"
 #include "OpenGLRenderer.h"
+#include "Properties.h"
 #include "utils/MathUtils.h"
 
 #if DEBUG_DEFER
@@ -502,7 +503,7 @@
         resetBatchingState();
     }
 
-    if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
+    if (CC_UNLIKELY(Properties::drawReorderDisabled)) {
         // TODO: elegant way to reuse batches?
         DrawBatch* b = new DrawBatch(deferInfo);
         b->add(op, state, deferInfo.opaqueOverBounds);
diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp
index ecde5ff..7addef9 100644
--- a/libs/hwui/DrawProfiler.cpp
+++ b/libs/hwui/DrawProfiler.cpp
@@ -18,12 +18,11 @@
 #include <cutils/compiler.h>
 
 #include "OpenGLRenderer.h"
-#include "Properties.h"
 
 #define DEFAULT_MAX_FRAMES 128
 
-#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == kNone)) return
-#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone && !mShowDirtyRegions)) return
+#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == ProfileType::None)) return
+#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == ProfileType::None && !mShowDirtyRegions)) return
 
 #define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f)
 
@@ -56,18 +55,7 @@
     return (int) (dp * density + 0.5f);
 }
 
-DrawProfiler::DrawProfiler()
-        : mType(kNone)
-        , mDensity(0)
-        , mData(nullptr)
-        , mDataSize(0)
-        , mCurrentFrame(-1)
-        , mPreviousTime(0)
-        , mVerticalUnit(0)
-        , mHorizontalUnit(0)
-        , mThresholdStroke(0)
-        , mShowDirtyRegions(false)
-        , mFlashToggle(false) {
+DrawProfiler::DrawProfiler() {
     setDensity(1);
 }
 
@@ -135,7 +123,7 @@
         }
     }
 
-    if (mType == kBars) {
+    if (mType == ProfileType::Bars) {
         prepareShapes(canvas->getViewportHeight());
         drawGraph(canvas);
         drawCurrentFrame(canvas);
@@ -217,32 +205,20 @@
     canvas->drawLines(pts, 4, &paint);
 }
 
-DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() {
-    ProfileType type = kNone;
-    char buf[PROPERTY_VALUE_MAX] = {'\0',};
-    if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
-        if (!strcmp(buf, PROPERTY_PROFILE_VISUALIZE_BARS)) {
-            type = kBars;
-        } else if (!strcmp(buf, "true")) {
-            type = kConsole;
-        }
-    }
-    return type;
-}
-
-bool DrawProfiler::loadSystemProperties() {
+bool DrawProfiler::consumeProperties() {
     bool changed = false;
-    ProfileType newType = loadRequestedProfileType();
+    ProfileType newType = Properties::getProfileType();
     if (newType != mType) {
         mType = newType;
-        if (mType == kNone) {
+        if (mType == ProfileType::None) {
             destroyData();
         } else {
             createData();
         }
         changed = true;
     }
-    bool showDirty = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false);
+
+    bool showDirty = Properties::showDirtyRegions;
     if (showDirty != mShowDirtyRegions) {
         mShowDirtyRegions = showDirty;
         changed = true;
diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h
index de64088..ef6101c 100644
--- a/libs/hwui/DrawProfiler.h
+++ b/libs/hwui/DrawProfiler.h
@@ -16,9 +16,11 @@
 #ifndef DRAWPROFILER_H
 #define DRAWPROFILER_H
 
-#include <utils/Timers.h>
+#include "Properties.h"
 #include "Rect.h"
 
+#include <utils/Timers.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -29,7 +31,7 @@
     DrawProfiler();
     ~DrawProfiler();
 
-    bool loadSystemProperties();
+    bool consumeProperties();
     void setDensity(float density);
 
     void startFrame(nsecs_t recordDurationNanos = 0);
@@ -43,12 +45,6 @@
     void dumpData(int fd);
 
 private:
-    enum ProfileType {
-        kNone,
-        kConsole,
-        kBars,
-    };
-
     typedef struct {
         float record;
         float prepare;
@@ -65,20 +61,18 @@
     void drawCurrentFrame(OpenGLRenderer* canvas);
     void drawThreshold(OpenGLRenderer* canvas);
 
-    ProfileType loadRequestedProfileType();
+    ProfileType mType = ProfileType::None;
+    float mDensity = 0;
 
-    ProfileType mType;
-    float mDensity;
+    FrameTimingData* mData = nullptr;
+    int mDataSize = 0;
 
-    FrameTimingData* mData;
-    int mDataSize;
+    int mCurrentFrame = -1;
+    nsecs_t mPreviousTime = 0;
 
-    int mCurrentFrame;
-    nsecs_t mPreviousTime;
-
-    int mVerticalUnit;
-    int mHorizontalUnit;
-    int mThresholdStroke;
+    int mVerticalUnit = 0;
+    int mHorizontalUnit = 0;
+    int mThresholdStroke = 0;
 
     /*
      * mRects represents an array of rect shapes, divided into NUM_ELEMENTS
@@ -87,11 +81,11 @@
      * OpenGLRenderer:drawRects() that makes up all the FrameTimingData:record
      * information.
      */
-    float** mRects;
+    float** mRects = nullptr;
 
-    bool mShowDirtyRegions;
+    bool mShowDirtyRegions = false;
     SkRect mDirtyRegion;
-    bool mFlashToggle;
+    bool mFlashToggle = false;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 9ca6bc6..e25f81e 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -588,8 +588,8 @@
 
     // Enable debug highlight when what we're about to draw is tested against
     // the stencil buffer and if stencil highlight debugging is on
-    mDescription.hasDebugHighlight = !mCaches.debugOverdraw
-            && mCaches.debugStencilClip == Caches::kStencilShowHighlight
+    mDescription.hasDebugHighlight = !Properties::debugOverdraw
+            && Properties::debugStencilClip == StencilClipDebug::ShowHighlight
             && mRenderState.stencil().isTestEnabled();
 
     // serialize shader info into ShaderData
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7fc31b8..09674f7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -245,7 +245,7 @@
 #if DEBUG_MEMORY_USAGE
         mCaches.dumpMemoryUsage();
 #else
-        if (mCaches.getDebugLevel() & kDebugMemory) {
+        if (Properties::debugLevel & kDebugMemory) {
             mCaches.dumpMemoryUsage();
         }
 #endif
@@ -339,7 +339,7 @@
 }
 
 void OpenGLRenderer::renderOverdraw() {
-    if (mCaches.debugOverdraw && getTargetFbo() == 0) {
+    if (Properties::debugOverdraw && getTargetFbo() == 0) {
         const Rect* clip = &mTilingClip;
 
         mRenderState.scissor().setEnabled(true);
@@ -381,7 +381,7 @@
             debugOverdraw(false, false);
         }
 
-        if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
+        if (CC_UNLIKELY(inFrame || Properties::drawDeferDisabled)) {
             layer->render(*this);
         } else {
             layer->defer(*this);
@@ -392,7 +392,7 @@
             startTilingCurrentClip();
         }
 
-        layer->debugDrawUpdate = mCaches.debugLayersUpdates;
+        layer->debugDrawUpdate = Properties::debugLayersUpdates;
         layer->hasDrawnSinceUpdate = false;
 
         return true;
@@ -407,7 +407,7 @@
     // in the layer updates list which will be cleared by flushLayers().
     int count = mLayerUpdates.size();
     if (count > 0) {
-        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+        if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
             startMark("Layer Updates");
         } else {
             startMark("Defer Layer Updates");
@@ -419,7 +419,7 @@
             updateLayer(layer, false);
         }
 
-        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+        if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
             mLayerUpdates.clear();
             mRenderState.bindFramebuffer(getTargetFbo());
         }
@@ -883,13 +883,13 @@
  * operations are correctly counted twice for overdraw. NOTE: assumes composeLayerRegion only used
  * by saveLayer's restore
  */
-#define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) {                             \
-        DRAW_COMMAND;                                                            \
-        if (CC_UNLIKELY(mCaches.debugOverdraw && getTargetFbo() == 0 && COND)) { \
-            glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);                 \
-            DRAW_COMMAND;                                                        \
-            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);                     \
-        }                                                                        \
+#define DRAW_DOUBLE_STENCIL_IF(COND, DRAW_COMMAND) { \
+        DRAW_COMMAND; \
+        if (CC_UNLIKELY(Properties::debugOverdraw && getTargetFbo() == 0 && COND)) { \
+            glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); \
+            DRAW_COMMAND; \
+            glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); \
+        } \
     }
 
 #define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
@@ -1324,7 +1324,7 @@
 }
 
 void OpenGLRenderer::setStencilFromClip() {
-    if (!mCaches.debugOverdraw) {
+    if (!Properties::debugOverdraw) {
         if (!currentSnapshot()->clipIsSimple()) {
             int incrementThreshold;
             EVENT_LOGD("setStencilFromClip - enabling");
@@ -1383,8 +1383,8 @@
             // Draw the region used to generate the stencil if the appropriate debug
             // mode is enabled
             // TODO: Implement for rectangle list clip areas
-            if (mCaches.debugStencilClip == Caches::kStencilShowRegion &&
-                    !clipArea.isRectangleList()) {
+            if (Properties::debugStencilClip == StencilClipDebug::ShowRegion
+                    && !clipArea.isRectangleList()) {
                 paint.setColor(0x7f0000ff);
                 paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
                 drawRegionRects(currentSnapshot()->getClipRegion(), paint);
@@ -1469,7 +1469,7 @@
     if (renderNode && renderNode->isRenderable()) {
         // compute 3d ordering
         renderNode->computeOrdering();
-        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+        if (CC_UNLIKELY(Properties::drawDeferDisabled)) {
             startFrame();
             ReplayStateStruct replayStruct(*this, dirty, replayFlags);
             renderNode->replay(replayStruct, 0);
@@ -1478,7 +1478,7 @@
 
         // Don't avoid overdraw when visualizing, since that makes it harder to
         // debug where it's coming from, and when the problem occurs.
-        bool avoidOverdraw = !mCaches.debugOverdraw;
+        bool avoidOverdraw = !Properties::debugOverdraw;
         DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
         DeferStateStruct deferStruct(deferredList, *this, replayFlags);
         renderNode->defer(deferStruct, 0);
@@ -2452,8 +2452,8 @@
 
     // The caller has made sure casterAlpha > 0.
     float ambientShadowAlpha = mAmbientShadowAlpha;
-    if (CC_UNLIKELY(mCaches.propertyAmbientShadowStrength >= 0)) {
-        ambientShadowAlpha = mCaches.propertyAmbientShadowStrength;
+    if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
+        ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
     }
     if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
         paint.setARGB(casterAlpha * ambientShadowAlpha, 0, 0, 0);
@@ -2461,8 +2461,8 @@
     }
 
     float spotShadowAlpha = mSpotShadowAlpha;
-    if (CC_UNLIKELY(mCaches.propertySpotShadowStrength >= 0)) {
-        spotShadowAlpha = mCaches.propertySpotShadowStrength;
+    if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
+        spotShadowAlpha = Properties::overrideSpotShadowStrength;
     }
     if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
         paint.setARGB(casterAlpha * spotShadowAlpha, 0, 0, 0);
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index bdb44a6..74964f6 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -153,7 +153,7 @@
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
     mMaxTextureSize = maxTextureSize;
 
-    mDebugEnabled = readDebugLevel() & kDebugCaches;
+    mDebugEnabled = Properties::debugLevel & kDebugCaches;
 }
 
 PathCache::~PathCache() {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
new file mode 100644
index 0000000..fd32b6f
--- /dev/null
+++ b/libs/hwui/Properties.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+#include "Properties.h"
+
+#include "Debug.h"
+
+#include <cmath>
+#include <cutils/log.h>
+
+namespace android {
+namespace uirenderer {
+
+bool Properties::drawDeferDisabled = false;
+bool Properties::drawReorderDisabled = false;
+bool Properties::debugLayersUpdates = false;
+bool Properties::debugOverdraw = false;
+bool Properties::showDirtyRegions = false;
+
+DebugLevel Properties::debugLevel = kDebugDisabled;
+OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
+StencilClipDebug Properties::debugStencilClip = StencilClipDebug::Hide;
+
+float Properties::overrideLightRadius = -1.0f;
+float Properties::overrideLightPosY = -1.0f;
+float Properties::overrideLightPosZ = -1.0f;
+float Properties::overrideAmbientRatio = -1.0f;
+int Properties::overrideAmbientShadowStrength = -1;
+int Properties::overrideSpotShadowStrength = -1;
+
+ProfileType Properties::sProfileType = ProfileType::None;
+bool Properties::sDisableProfileBars = false;
+
+bool Properties::load() {
+    char property[PROPERTY_VALUE_MAX];
+    bool prevDebugLayersUpdates = debugLayersUpdates;
+    bool prevDebugOverdraw = debugOverdraw;
+    StencilClipDebug prevDebugStencilClip = debugStencilClip;
+
+
+    debugOverdraw = false;
+    if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) {
+        INIT_LOGD("  Overdraw debug enabled: %s", property);
+        if (!strcmp(property, "show")) {
+            debugOverdraw = true;
+            overdrawColorSet = OverdrawColorSet::Default;
+        } else if (!strcmp(property, "show_deuteranomaly")) {
+            debugOverdraw = true;
+            overdrawColorSet = OverdrawColorSet::Deuteranomaly;
+        }
+    }
+
+    // See Properties.h for valid values
+    if (property_get(PROPERTY_DEBUG_STENCIL_CLIP, property, nullptr) > 0) {
+        INIT_LOGD("  Stencil clip debug enabled: %s", property);
+        if (!strcmp(property, "hide")) {
+            debugStencilClip = StencilClipDebug::Hide;
+        } else if (!strcmp(property, "highlight")) {
+            debugStencilClip = StencilClipDebug::ShowHighlight;
+        } else if (!strcmp(property, "region")) {
+            debugStencilClip = StencilClipDebug::ShowRegion;
+        }
+    } else {
+        debugStencilClip = StencilClipDebug::Hide;
+    }
+
+    sProfileType = ProfileType::None;
+    if (property_get(PROPERTY_PROFILE, property, "") > 0) {
+        if (!strcmp(property, PROPERTY_PROFILE_VISUALIZE_BARS)) {
+            sProfileType = ProfileType::Bars;
+        } else if (!strcmp(property, "true")) {
+            sProfileType = ProfileType::Console;
+        }
+    }
+
+    debugLayersUpdates = property_get_bool(PROPERTY_DEBUG_LAYERS_UPDATES, false);
+    INIT_LOGD("  Layers updates debug enabled: %d", debugLayersUpdates);
+
+    drawDeferDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_DEFER, false);
+    INIT_LOGD("  Draw defer %s", drawDeferDisabled ? "disabled" : "enabled");
+
+    drawReorderDisabled = property_get_bool(PROPERTY_DISABLE_DRAW_REORDER, false);
+    INIT_LOGD("  Draw reorder %s", drawReorderDisabled ? "disabled" : "enabled");
+
+    showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false);
+
+    debugLevel = kDebugDisabled;
+    if (property_get(PROPERTY_DEBUG, property, nullptr) > 0) {
+        debugLevel = (DebugLevel) atoi(property);
+    }
+
+    return (prevDebugLayersUpdates != debugLayersUpdates)
+            || (prevDebugOverdraw != debugOverdraw)
+            || (prevDebugStencilClip != debugStencilClip);
+}
+
+void Properties::overrideProperty(const char* name, const char* value) {
+    if (!strcmp(name, "disableProfileBars")) {
+        sDisableProfileBars = !strcmp(value, "true");
+        ALOGD("profile bars %s", sDisableProfileBars ? "disabled" : "enabled");
+        return;
+    } else if (!strcmp(name, "ambientRatio")) {
+        overrideAmbientRatio = fmin(fmax(atof(value), 0.0), 10.0);
+        ALOGD("ambientRatio = %.2f", overrideAmbientRatio);
+        return;
+    } else if (!strcmp(name, "lightRadius")) {
+        overrideLightRadius = fmin(fmax(atof(value), 0.0), 3000.0);
+        ALOGD("lightRadius = %.2f", overrideLightRadius);
+        return;
+    } else if (!strcmp(name, "lightPosY")) {
+        overrideLightPosY = fmin(fmax(atof(value), 0.0), 3000.0);
+        ALOGD("lightPos Y = %.2f", overrideLightPosY);
+        return;
+    } else if (!strcmp(name, "lightPosZ")) {
+        overrideLightPosZ = fmin(fmax(atof(value), 0.0), 3000.0);
+        ALOGD("lightPos Z = %.2f", overrideLightPosZ);
+        return;
+    } else if (!strcmp(name, "ambientShadowStrength")) {
+        overrideAmbientShadowStrength = atoi(value);
+        ALOGD("ambient shadow strength = 0x%x out of 0xff", overrideAmbientShadowStrength);
+        return;
+    } else if (!strcmp(name, "spotShadowStrength")) {
+        overrideSpotShadowStrength = atoi(value);
+        ALOGD("spot shadow strength = 0x%x out of 0xff", overrideSpotShadowStrength);
+        return;
+    }
+    ALOGD("failed overriding property %s to %s", name, value);
+}
+
+ProfileType Properties::getProfileType() {
+    if (CC_UNLIKELY(sDisableProfileBars && sProfileType == ProfileType::Bars))
+        return ProfileType::None;
+    return sProfileType;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index a0312e1..46fa940 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -19,12 +19,16 @@
 
 #include <cutils/properties.h>
 #include <stdlib.h>
+#include <utils/Singleton.h>
 
 /**
  * This file contains the list of system properties used to configure
  * the OpenGLRenderer.
  */
 
+namespace android {
+namespace uirenderer {
+
 ///////////////////////////////////////////////////////////////////////////////
 // Compile-time properties
 ///////////////////////////////////////////////////////////////////////////////
@@ -253,12 +257,61 @@
 // Converts a number of kilo-bytes into bytes
 #define KB(s) s * 1024
 
-static inline DebugLevel readDebugLevel() {
-    char property[PROPERTY_VALUE_MAX];
-    if (property_get(PROPERTY_DEBUG, property, nullptr) > 0) {
-        return (DebugLevel) atoi(property);
-    }
-    return kDebugDisabled;
-}
+enum class ProfileType {
+    None,
+    Console,
+    Bars
+};
+
+enum class OverdrawColorSet {
+    Default = 0,
+    Deuteranomaly
+};
+
+enum class StencilClipDebug {
+    Hide,
+    ShowHighlight,
+    ShowRegion
+};
+
+/**
+ * Renderthread-only singleton which manages several static rendering properties. Most of these
+ * are driven by system properties which are queried once at initialization, and again if init()
+ * is called.
+ */
+class Properties {
+public:
+    static bool load();
+
+    static bool drawDeferDisabled;
+    static bool drawReorderDisabled;
+    static bool debugLayersUpdates;
+    static bool debugOverdraw;
+    static bool showDirtyRegions;
+
+    static DebugLevel debugLevel;
+    static OverdrawColorSet overdrawColorSet;
+    static StencilClipDebug debugStencilClip;
+
+    // Override the value for a subset of properties in this class
+    static void overrideProperty(const char* name, const char* value);
+
+    static float overrideLightRadius;
+    static float overrideLightPosY;
+    static float overrideLightPosZ;
+    static float overrideAmbientRatio;
+    static int overrideAmbientShadowStrength;
+    static int overrideSpotShadowStrength;
+
+    static ProfileType getProfileType();
+
+private:
+    static ProfileType sProfileType;
+    static bool sDisableProfileBars;
+
+}; // class Caches
+
+}; // namespace uirenderer
+}; // namespace android
 
 #endif // ANDROID_HWUI_PROPERTIES_H
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 30d3f41..fb28531 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -20,11 +20,13 @@
 #include <math.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
+#include <utils/Vector.h>
 
 #include "AmbientShadow.h"
-#include "Caches.h"
+#include "Properties.h"
 #include "ShadowTessellator.h"
 #include "SpotShadow.h"
+#include "Vector.h"
 
 namespace android {
 namespace uirenderer {
@@ -40,9 +42,8 @@
     float heightFactor = 1.0f / 128;
     const float geomFactor = 64;
 
-    Caches& caches = Caches::getInstance();
-    if (CC_UNLIKELY(caches.propertyAmbientRatio > 0.0f)) {
-        heightFactor *= caches.propertyAmbientRatio;
+    if (CC_UNLIKELY(Properties::overrideAmbientRatio > 0.0f)) {
+        heightFactor *= Properties::overrideAmbientRatio;
     }
 
     Rect ambientShadowBounds(casterBounds);
@@ -66,14 +67,12 @@
         const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) {
     ATRACE_CALL();
 
-    Caches& caches = Caches::getInstance();
-
     Vector3 adjustedLightCenter(lightCenter);
-    if (CC_UNLIKELY(caches.propertyLightPosY > 0)) {
-        adjustedLightCenter.y = - caches.propertyLightPosY; // negated since this shifts up
+    if (CC_UNLIKELY(Properties::overrideLightPosY > 0)) {
+        adjustedLightCenter.y = - Properties::overrideLightPosY; // negated since this shifts up
     }
-    if (CC_UNLIKELY(caches.propertyLightPosZ > 0)) {
-        adjustedLightCenter.z = caches.propertyLightPosZ;
+    if (CC_UNLIKELY(Properties::overrideLightPosZ > 0)) {
+        adjustedLightCenter.z = Properties::overrideLightPosZ;
     }
 
 #if DEBUG_SHADOW
@@ -87,8 +86,8 @@
     reverseReceiverTransform.loadInverse(receiverTransform);
     reverseReceiverTransform.mapPoint3d(adjustedLightCenter);
 
-    if (CC_UNLIKELY(caches.propertyLightRadius > 0)) {
-        lightRadius = caches.propertyLightRadius;
+    if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) {
+        lightRadius = Properties::overrideLightRadius;
     }
 
     // Now light and caster are both in local space, we will check whether
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 7edb9fb..fc173f7 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -311,7 +311,7 @@
 
     mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
     mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener);
-    mDebugEnabled = readDebugLevel() & kDebugCaches;
+    mDebugEnabled = Properties::debugLevel & kDebugCaches;
 }
 
 TessellationCache::~TessellationCache() {
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index c2e88f3..8b1d4cb 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -123,7 +123,7 @@
 
 void TextDropShadowCache::init() {
     mCache.setOnEntryRemovedListener(this);
-    mDebugEnabled = readDebugLevel() & kDebugMoreCaches;
+    mDebugEnabled = Properties::debugLevel & kDebugMoreCaches;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index b911a0f..e59688c 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -66,7 +66,7 @@
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
     INIT_LOGD("    Maximum texture dimension is %d pixels", mMaxTextureSize);
 
-    mDebugEnabled = readDebugLevel() & kDebugCaches;
+    mDebugEnabled = Properties::debugLevel & kDebugCaches;
 }
 
 TextureCache::~TextureCache() {
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 7b44d6d..e54fa5a 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -154,7 +154,7 @@
 }
 
 void RenderState::debugOverdraw(bool enable, bool clear) {
-    if (mCaches->debugOverdraw && mFramebuffer == 0) {
+    if (Properties::debugOverdraw && mFramebuffer == 0) {
         if (clear) {
             scissor().setEnabled(false);
             stencil().clear();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 7c04f40..c643e1d 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -112,9 +112,9 @@
 CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) {
     bool needsRedraw = false;
     if (Caches::hasInstance()) {
-        needsRedraw = Caches::getInstance().initProperties();
+        needsRedraw = Properties::load();
     }
-    if (args->context->profiler().loadSystemProperties()) {
+    if (args->context->profiler().consumeProperties()) {
         needsRedraw = true;
     }
     return (void*) needsRedraw;
@@ -135,7 +135,7 @@
     SETUP_TASK(setName);
     args->context = mContext;
     args->name = name;
-    postAndWait(task);
+    postAndWait(task); // block since name/value pointers owned by caller
 }
 
 CREATE_BRIDGE2(initialize, CanvasContext* context, ANativeWindow* window) {
@@ -331,7 +331,7 @@
     post(task);
 }
 
-CREATE_BRIDGE2(timMemory, RenderThread* thread, int level) {
+CREATE_BRIDGE2(trimMemory, RenderThread* thread, int level) {
     CanvasContext::trimMemory(*args->thread, args->level);
     return nullptr;
 }
@@ -340,13 +340,26 @@
     // Avoid creating a RenderThread to do a trimMemory.
     if (RenderThread::hasInstance()) {
         RenderThread& thread = RenderThread::getInstance();
-        SETUP_TASK(timMemory);
+        SETUP_TASK(trimMemory);
         args->thread = &thread;
         args->level = level;
         thread.queue(task);
     }
 }
 
+CREATE_BRIDGE2(overrideProperty, const char* name, const char* value) {
+    Properties::overrideProperty(args->name, args->value);
+    return nullptr;
+}
+
+void RenderProxy::overrideProperty(const char* name, const char* value) {
+    RenderThread& thread = RenderThread::getInstance();
+    SETUP_TASK(overrideProperty);
+    args->name = name;
+    args->value = value;
+    staticPostAndWait(task); // expensive, but block here since name/value pointers owned by caller
+}
+
 CREATE_BRIDGE0(fence) {
     // Intentionally empty
     return nullptr;
@@ -383,8 +396,10 @@
     mRenderThread.queueAtFront(task);
 }
 
-CREATE_BRIDGE3(dumpProfileInfo, CanvasContext* context, int fd, int dumpFlags) {
+CREATE_BRIDGE4(dumpProfileInfo, CanvasContext* context, RenderThread* thread,
+        int fd, int dumpFlags) {
     args->context->profiler().dumpData(args->fd);
+    args->thread->jankTracker().dump(args->fd);
     if (args->dumpFlags & DumpFlags::kFrameStats) {
         args->context->dumpFrames(args->fd);
     }
@@ -397,11 +412,23 @@
 void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
     SETUP_TASK(dumpProfileInfo);
     args->context = mContext;
+    args->thread = &mRenderThread;
     args->fd = fd;
     args->dumpFlags = dumpFlags;
     postAndWait(task);
 }
 
+CREATE_BRIDGE1(resetProfileInfo, CanvasContext* context) {
+    args->context->resetFrameStats();
+    return nullptr;
+}
+
+void RenderProxy::resetProfileInfo() {
+    SETUP_TASK(resetProfileInfo);
+    args->context = mContext;
+    postAndWait(task);
+}
+
 CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) {
     args->thread->jankTracker().dump(args->fd);
 
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index cc475fa..31456cd 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -90,12 +90,15 @@
 
     ANDROID_API void destroyHardwareResources();
     ANDROID_API static void trimMemory(int level);
+    ANDROID_API static void overrideProperty(const char* name, const char* value);
 
     ANDROID_API void fence();
     ANDROID_API void stopDrawing();
     ANDROID_API void notifyFramePending();
 
     ANDROID_API void dumpProfileInfo(int fd, int dumpFlags);
+    // Not exported, only used for testing
+    void resetProfileInfo();
     ANDROID_API static void dumpGraphicsMemory(int fd);
 
     ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3ac2976..64075f1 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -144,6 +144,7 @@
         , mFrameCallbackTask(nullptr)
         , mRenderState(nullptr)
         , mEglManager(nullptr) {
+    Properties::load();
     mFrameCallbackTask = new DispatchFrameCallbacks(this);
     mLooper = new Looper(false);
     run("RenderThread");
diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp
index 542bbae..3687a50 100644
--- a/libs/hwui/tests/TestContext.cpp
+++ b/libs/hwui/tests/TestContext.cpp
@@ -57,6 +57,10 @@
 }
 
 void TestContext::waitForVsync() {
+#if HWUI_NULL_GPU
+    return;
+#endif
+
     // Request vsync
     mDisplayEventReceiver.requestNextVsync();
 
@@ -71,4 +75,3 @@
 } // namespace test
 } // namespace uirenderer
 } // namespace android
-
diff --git a/libs/hwui/tests/how_to_run.txt b/libs/hwui/tests/how_to_run.txt
new file mode 100644
index 0000000..686cd78
--- /dev/null
+++ b/libs/hwui/tests/how_to_run.txt
@@ -0,0 +1,17 @@
+mmm -j8 frameworks/base/libs/hwui/tests/ &&
+    adb push $OUT/data/local/tmp/hwuitest /data/local/tmp/hwuitest &&
+    adb shell /data/local/tmp/hwuitest
+
+
+Command arguments:
+hwuitest [testname]
+
+Default test is 'shadowgrid'
+
+List of tests:
+
+shadowgrid: creates a grid of rounded rects that cast shadows, high CPU & GPU load
+
+rectgrid: creates a grid of 1x1 rects
+
+oval: draws 1 oval
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index 62782af..aca7c52 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include <stdio.h>
-
 #include <cutils/log.h>
 #include <gui/Surface.h>
 #include <ui/PixelFormat.h>
@@ -28,6 +26,9 @@
 
 #include "TestContext.h"
 
+#include <stdio.h>
+#include <unistd.h>
+
 using namespace android;
 using namespace android::uirenderer;
 using namespace android::uirenderer::renderthread;
@@ -93,16 +94,27 @@
         animation.createContent(width, height, renderer);
         endRecording(renderer, rootNode);
 
-        for (int i = 0; i < animation.getFrameCount(); i++) {
-#if !HWUI_NULL_GPU
+        // Do a few cold runs then reset the stats so that the caches are all hot
+        for (int i = 0; i < 3; i++) {
             testContext.waitForVsync();
-#endif
+            proxy->syncAndDrawFrame();
+        }
+        proxy->resetProfileInfo();
 
+        for (int i = 0; i < animation.getFrameCount(); i++) {
+            testContext.waitForVsync();
+
+            // workaround b/20853441
+            proxy->fence();
             ATRACE_NAME("UI-Draw Frame");
+            nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+            UiFrameInfoBuilder(proxy->frameInfo())
+                    .setVsync(vsync, vsync);
             animation.doFrame(i);
             proxy->syncAndDrawFrame();
         }
 
+        proxy->dumpProfileInfo(STDOUT_FILENO, 0);
         rootNode->decStrong(nullptr);
     }
 };
diff --git a/libs/hwui/unit_tests/Android.mk b/libs/hwui/unit_tests/Android.mk
index 51601b0..917e646 100644
--- a/libs/hwui/unit_tests/Android.mk
+++ b/libs/hwui/unit_tests/Android.mk
@@ -27,6 +27,7 @@
 
 LOCAL_SRC_FILES += \
     unit_tests/ClipAreaTests.cpp \
+    unit_tests/DamageAccumulatorTests.cpp \
     unit_tests/LinearAllocatorTests.cpp \
     unit_tests/main.cpp
 
diff --git a/libs/hwui/unit_tests/DamageAccumulatorTests.cpp b/libs/hwui/unit_tests/DamageAccumulatorTests.cpp
new file mode 100644
index 0000000..c8d6004
--- /dev/null
+++ b/libs/hwui/unit_tests/DamageAccumulatorTests.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <DamageAccumulator.h>
+#include <Matrix.h>
+#include <utils/LinearAllocator.h>
+
+#include <SkRect.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+// Test that push & pop are propegating the dirty rect
+// There is no transformation of the dirty rect, the input is the same
+// as the output.
+TEST(DamageAccumulator, identity) {
+    DamageAccumulator da;
+    Matrix4 identity;
+    SkRect curDirty;
+    identity.loadIdentity();
+    da.pushTransform(&identity);
+    da.dirty(50, 50, 100, 100);
+    da.pushTransform(&identity);
+    da.peekAtDirty(&curDirty);
+    ASSERT_EQ(SkRect(), curDirty);
+    da.popTransform();
+    da.peekAtDirty(&curDirty);
+    ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty);
+    da.popTransform();
+    da.finish(&curDirty);
+    ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty);
+}
+
+// Test that transformation is happening at the correct levels via
+// peekAtDirty & popTransform. Just uses a simple translate to test this
+TEST(DamageAccumulator, translate) {
+    DamageAccumulator da;
+    Matrix4 translate;
+    SkRect curDirty;
+    translate.loadTranslate(25, 25, 0);
+    da.pushTransform(&translate);
+    da.dirty(50, 50, 100, 100);
+    da.peekAtDirty(&curDirty);
+    ASSERT_EQ(SkRect::MakeLTRB(50, 50, 100, 100), curDirty);
+    da.popTransform();
+    da.finish(&curDirty);
+    ASSERT_EQ(SkRect::MakeLTRB(75, 75, 125, 125), curDirty);
+}
+
+// Test that dirty rectangles are being unioned across "siblings
+TEST(DamageAccumulator, union) {
+    DamageAccumulator da;
+    Matrix4 identity;
+    SkRect curDirty;
+    identity.loadIdentity();
+    da.pushTransform(&identity);
+    da.pushTransform(&identity);
+    da.dirty(50, 50, 100, 100);
+    da.popTransform();
+    da.pushTransform(&identity);
+    da.dirty(150, 50, 200, 125);
+    da.popTransform();
+    da.popTransform();
+    da.finish(&curDirty);
+    ASSERT_EQ(SkRect::MakeLTRB(50, 50, 200, 125), curDirty);
+}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 974c9af..89d419a 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1733,6 +1733,72 @@
                         maxBlocks, maxBlocksPerSecond,
                         16 /* blockWidth */, 16 /* blockHeight */,
                         1 /* widthAlignment */, 1 /* heightAlignment */);
+            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG2)) {
+                int maxWidth = 11, maxHeight = 9, maxRate = 15;
+                maxBlocks = 99;
+                maxBlocksPerSecond = 1485;
+                maxBps = 64000;
+                for (CodecProfileLevel profileLevel: profileLevels) {
+                    int MBPS = 0, FS = 0, BR = 0, FR = 0, W = 0, H = 0;
+                    boolean supported = true;
+                    switch (profileLevel.profile) {
+                        case CodecProfileLevel.MPEG2ProfileSimple:
+                            switch (profileLevel.level) {
+                                case CodecProfileLevel.MPEG2LevelML:
+                                    FR = 30; W = 45; H =  36; MBPS =  48600; FS =  1620; BR =  15000; break;
+                                default:
+                                    Log.w(TAG, "Unrecognized profile/level "
+                                            + profileLevel.profile + "/"
+                                            + profileLevel.level + " for " + mime);
+                                    errors |= ERROR_UNRECOGNIZED;
+                            }
+                            break;
+                        case CodecProfileLevel.MPEG2ProfileMain:
+                            switch (profileLevel.level) {
+                                case CodecProfileLevel.MPEG2LevelLL:
+                                    FR = 30; W = 22; H =  18; MBPS =  11880; FS =  396; BR =  4000; break;
+                                case CodecProfileLevel.MPEG2LevelML:
+                                    FR = 30; W = 45; H =  36; MBPS =  48600; FS =  1620; BR =  15000; break;
+                                case CodecProfileLevel.MPEG2LevelH14:
+                                    FR = 60; W = 90; H =  68; MBPS =  367200; FS =  6120; BR = 60000; break;
+                                case CodecProfileLevel.MPEG2LevelHL:
+                                    FR = 60; W = 120; H = 68; MBPS =  489600; FS =  8160; BR = 80000; break;
+                                default:
+                                    Log.w(TAG, "Unrecognized profile/level "
+                                            + profileLevel.profile + "/"
+                                            + profileLevel.level + " for " + mime);
+                                    errors |= ERROR_UNRECOGNIZED;
+                            }
+                            break;
+                        case CodecProfileLevel.MPEG2Profile422:
+                        case CodecProfileLevel.MPEG2ProfileSNR:
+                        case CodecProfileLevel.MPEG2ProfileSpatial:
+                        case CodecProfileLevel.MPEG2ProfileHigh:
+                            Log.i(TAG, "Unsupported profile "
+                                    + profileLevel.profile + " for " + mime);
+                            errors |= ERROR_UNSUPPORTED;
+                            supported = false;
+                            break;
+                        default:
+                            Log.w(TAG, "Unrecognized profile "
+                                    + profileLevel.profile + " for " + mime);
+                            errors |= ERROR_UNRECOGNIZED;
+                    }
+                    if (supported) {
+                        errors &= ~ERROR_NONE_SUPPORTED;
+                    }
+                    maxBlocksPerSecond = Math.max(MBPS, maxBlocksPerSecond);
+                    maxBlocks = Math.max(FS, maxBlocks);
+                    maxBps = Math.max(BR * 1000, maxBps);
+                    maxWidth = Math.max(W, maxWidth);
+                    maxHeight = Math.max(H, maxHeight);
+                    maxRate = Math.max(FR, maxRate);
+                }
+                applyMacroBlockLimits(maxWidth, maxHeight,
+                        maxBlocks, maxBlocksPerSecond,
+                        16 /* blockWidth */, 16 /* blockHeight */,
+                        1 /* widthAlignment */, 1 /* heightAlignment */);
+                mFrameRateRange = mFrameRateRange.intersect(12, maxRate);
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
                 int maxWidth = 11, maxHeight = 9, maxRate = 15;
                 maxBlocks = 99;
@@ -2343,6 +2409,20 @@
         public static final int MPEG4Level4a     = 0x40;
         public static final int MPEG4Level5      = 0x80;
 
+        // from OMX_VIDEO_MPEG2PROFILETYPE
+        public static final int MPEG2ProfileSimple              = 0x00;
+        public static final int MPEG2ProfileMain                = 0x01;
+        public static final int MPEG2Profile422                 = 0x02;
+        public static final int MPEG2ProfileSNR                 = 0x03;
+        public static final int MPEG2ProfileSpatial             = 0x04;
+        public static final int MPEG2ProfileHigh                = 0x05;
+
+        // from OMX_VIDEO_MPEG2LEVELTYPE
+        public static final int MPEG2LevelLL     = 0x00;
+        public static final int MPEG2LevelML     = 0x01;
+        public static final int MPEG2LevelH14    = 0x02;
+        public static final int MPEG2LevelHL     = 0x03;
+
         // from OMX_AUDIO_AACPROFILETYPE
         public static final int AACObjectMain       = 1;
         public static final int AACObjectLC         = 2;
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index daeb1cc..043b80e 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -783,10 +783,15 @@
     private final int mHashCode;
 
     /**
-     * Rating constant denoting unrated content.
+     * Rating constant denoting unrated content. Used to handle the case where the content rating
+     * information is missing.
+     *
+     * <p>TV input services can call {@link TvInputManager#isRatingBlocked} with this constant to
+     * determine whether they should block unrated content. The subsequent call to
+     * {@link TvInputService.Session#notifyContentBlocked} with the same constant notifies
+     * applications that the current program content is blocked by parental controls.
      */
-    public static final TvContentRating UNRATED = new TvContentRating("com.android.tv", "",
-            "UNRATED", null);
+    public static final TvContentRating UNRATED = new TvContentRating("null", "null", "null", null);
 
     /**
      * Creates a {@code TvContentRating} object with predefined content rating strings.
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 3272a23..dca0631 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1035,7 +1035,7 @@
     /**
      * Checks whether a given TV content rating is blocked by the user.
      *
-     * @param rating The TV content rating to check.
+     * @param rating The TV content rating to check. Can be {@link TvContentRating#UNRATED}.
      * @return {@code true} if the given TV content rating is blocked, {@code false} otherwise.
      */
     public boolean isRatingBlocked(@NonNull TvContentRating rating) {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 34c36c3..c1035b0 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -528,11 +528,12 @@
          * TvInputManager.isParentalControlsEnabled()} returns {@code true}). Whether the TV input
          * service should block the content or not is determined by invoking
          * {@link TvInputManager#isRatingBlocked TvInputManager.isRatingBlocked(TvContentRating)}
-         * with the content rating for the current program. Then the {@link TvInputManager} makes a
-         * judgment based on the user blocked ratings stored in the secure settings and returns the
-         * result. If the rating in question turns out to be blocked, the TV input service must
-         * immediately block the content and call this method with the content rating of the current
-         * program to prompt the PIN verification screen.
+         * with the content rating for the current program or {@link TvContentRating#UNRATED} in
+         * case the rating information is missing. Then the {@link TvInputManager} makes a judgment
+         * based on the user blocked ratings stored in the secure settings and returns the result.
+         * If the rating in question turns out to be blocked, the TV input service must immediately
+         * block the content and call this method with the content rating of the current program to
+         * prompt the PIN verification screen.
          *
          * <p>Each TV input service also needs to continuously listen to any changes made to the
          * parental controls settings by registering a broadcast receiver to receive
@@ -540,7 +541,8 @@
          * {@link TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} and immediately
          * reevaluate the current program with the new parental controls settings.
          *
-         * @param rating The content rating for the current TV program.
+         * @param rating The content rating for the current TV program. Can be
+         *            {@link TvContentRating#UNRATED}.
          * @see #notifyContentAllowed
          * @see TvInputManager
          */
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index 86c23c7..16b4c43 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -60,6 +60,7 @@
     public static final String VIDEO_H264_AAC = "/sdcard/media_api/video/H264_320_AAC_64.3gp";
     public static final String VIDEO_H264_AMR = "/sdcard/media_api/video/H264_320_AMRNB_6.3gp";
     public static final String VIDEO_HEVC_AAC = "/sdcard/media_api/video/HEVC_320_AAC_128.mp4";
+    public static final String VIDEO_MPEG2_AAC = "/sdcard/media_api/video/MPEG2_1500_AAC_128.mp4";
     public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp";
     public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp";
     public static final String VIDEO_WEBM = "/sdcard/media_api/video/big-buck-bunny_trailer.webm";
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 244b07f..c5281657 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -432,7 +432,22 @@
         assertTrue("HEVC playback memory test", memoryResult);
     }
 
-    // Test case 4: Capture the memory usage after every 20 video only recorded
+    // Test case 4: Capture the memory usage after every 20 mpeg2 playback
+    @LargeTest
+    public void testMPEG2VideoPlaybackMemoryUsage() throws Exception {
+        boolean memoryResult = false;
+
+        mStartPid = getMediaserverPid();
+        for (int i = 0; i < NUM_STRESS_LOOP; i++) {
+            mediaStressPlayback(MediaNames.VIDEO_MPEG2_AAC);
+            getMemoryWriteToLog(i);
+            writeProcmemInfo();
+        }
+        memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
+        assertTrue("MPEG2 playback memory test", memoryResult);
+    }
+
+    // Test case 5: Capture the memory usage after every 20 video only recorded
     @LargeTest
     public void testH263RecordVideoOnlyMemoryUsage() throws Exception {
         if (mCamcorderProfile != null) {
@@ -453,7 +468,7 @@
         }
     }
 
-    // Test case 5: Capture the memory usage after every 20 video only recorded
+    // Test case 6: Capture the memory usage after every 20 video only recorded
     @LargeTest
     public void testMpeg4RecordVideoOnlyMemoryUsage() throws Exception {
         if (mCamcorderProfile != null) {
@@ -474,7 +489,7 @@
         }
     }
 
-    // Test case 6: Capture the memory usage after every 20 video and audio
+    // Test case 7: Capture the memory usage after every 20 video and audio
     // recorded
     @LargeTest
     public void testRecordVideoAudioMemoryUsage() throws Exception {
@@ -496,7 +511,7 @@
         }
     }
 
-    // Test case 7: Capture the memory usage after every 20 audio only recorded
+    // Test case 8: Capture the memory usage after every 20 audio only recorded
     @LargeTest
     public void testRecordAudioOnlyMemoryUsage() throws Exception {
         boolean memoryResult = false;
@@ -511,7 +526,7 @@
         assertTrue("audio record only memory test", memoryResult);
     }
 
-    // Test case 8: Capture the memory usage after every 20 camera preview
+    // Test case 9: Capture the memory usage after every 20 camera preview
     @LargeTest
     public void testCameraPreviewMemoryUsage() throws Exception {
         boolean memoryResult = false;
diff --git a/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4 b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4
new file mode 100644
index 0000000..33f66a0
--- /dev/null
+++ b/media/tests/contents/media_api/video/MPEG2_1500_AAC_128.mp4
Binary files differ
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 2e0bece..202402f0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -449,7 +449,7 @@
         InputStream src = null;
         OutputStream dst = null;
 
-        boolean errorOccurred = false;
+        IOException copyError = null;
         try {
             srcFile = mSrcClient.openFile(srcUri, "r", canceller);
             dstFile = mDstClient.openFile(dstUri, "w", canceller);
@@ -462,16 +462,14 @@
                 dst.write(buffer, 0, len);
                 makeProgress(len);
             }
+
             srcFile.checkError();
-            dstFile.checkError();
         } catch (IOException e) {
-            errorOccurred = true;
-            Log.e(TAG, "Error while copying " + srcUri.toString(), e);
+            copyError = e;
             try {
-                mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri));
-            } catch (FileNotFoundException ignore) {
-                Log.w(TAG, "Source file gone: " + srcUri, e);
-              // The source file is gone.
+                dstFile.closeWithError(copyError.getMessage());
+            } catch (IOException closeError) {
+                Log.e(TAG, "Error closing destination", closeError);
             }
         } finally {
             // This also ensures the file descriptors are closed.
@@ -479,7 +477,18 @@
             IoUtils.closeQuietly(dst);
         }
 
-        if (errorOccurred || mIsCancelled) {
+        if (copyError != null) {
+            // Log errors.
+            Log.e(TAG, "Error while copying " + srcUri.toString(), copyError);
+            try {
+                mFailedFiles.add(DocumentInfo.fromUri(getContentResolver(), srcUri));
+            } catch (FileNotFoundException ignore) {
+                Log.w(TAG, "Source file gone: " + srcUri, copyError);
+              // The source file is gone.
+            }
+        }
+
+        if (copyError != null || mIsCancelled) {
             // Clean up half-copied files.
             canceller.cancel();
             try {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
index 13f7daa..b1c84dd 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
@@ -16,23 +16,21 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.model.DocumentInfo.getCursorString;
-
-import android.app.NotificationManager;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
-import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Document;
 import android.test.MoreAsserts;
 import android.test.ServiceTestCase;
+import android.test.mock.MockContentResolver;
 import android.util.Log;
 
 import com.android.documentsui.model.DocumentInfo;
@@ -43,40 +41,93 @@
 import libcore.io.IoUtils;
 import libcore.io.Streams;
 
-import org.mockito.Mockito;
-
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 public class CopyTest extends ServiceTestCase<CopyService> {
 
+    /**
+     * A test resolver that enables this test suite to listen for notifications that mark when copy
+     * operations are done.
+     */
+    class TestContentResolver extends MockContentResolver {
+        private CountDownLatch mReadySignal;
+        private CountDownLatch mNotificationSignal;
+
+        public TestContentResolver() {
+            mReadySignal = new CountDownLatch(1);
+        }
+
+        /**
+         * Wait for the given number of files to be copied to destination. Times out after 1 sec.
+         */
+        public void waitForChanges(int count) throws Exception {
+            // Wait for no more than 1 second by default.
+            waitForChanges(count, 1000);
+        }
+
+        /**
+         * Wait for files to be copied to destination.
+         *
+         * @param count Number of files to wait for.
+         * @param timeOut Timeout in ms. TimeoutException will be thrown if this function times out.
+         */
+        public void waitForChanges(int count, int timeOut) throws Exception {
+            mNotificationSignal = new CountDownLatch(count);
+            // Signal that the test is now waiting for files.
+            mReadySignal.countDown();
+            if (!mNotificationSignal.await(timeOut, TimeUnit.MILLISECONDS)) {
+                throw new TimeoutException("Timed out waiting for files to be copied.");
+            }
+        }
+
+        @Override
+        public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+            // Wait until the test is ready to receive file notifications.
+            try {
+                mReadySignal.await();
+            } catch (InterruptedException e) {
+                Log.d(TAG, "Interrupted while waiting for file copy readiness");
+                Thread.currentThread().interrupt();
+            }
+            if (DocumentsContract.isDocumentUri(mContext, uri)) {
+                Log.d(TAG, "Notification: " + uri);
+                // Watch for document URI change notifications - this signifies the end of a copy.
+                mNotificationSignal.countDown();
+            }
+        }
+    };
+
     public CopyTest() {
         super(CopyService.class);
     }
 
-    private static String TAG = "CopyTest";
-    // This must match the authority for the StubProvider.
     private static String AUTHORITY = "com.android.documentsui.stubprovider";
+    private static String DST = "sd1";
+    private static String SRC = "sd0";
+    private static String TAG = "CopyTest";
     private List<RootInfo> mRoots;
     private Context mContext;
-    private ContentResolver mResolver;
+    private TestContentResolver mResolver;
     private ContentProviderClient mClient;
-    private NotificationManager mNotificationManager;
+    private StubProvider mStorage;
+    private Context mSystemContext;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        setupTestContext();
 
-        mResolver = mContext.getContentResolver();
+        setupTestContext();
         mClient = mResolver.acquireContentProviderClient(AUTHORITY);
 
         // Reset the stub provider's storage.
-        mClient.call("clear", "", null);
+        mStorage.clearCacheAndBuildRoots();
 
         mRoots = Lists.newArrayList();
         Uri queryUri = DocumentsContract.buildRootsUri(AUTHORITY);
@@ -84,9 +135,7 @@
         try {
             cursor = mClient.query(queryUri, null, null, null, null);
             while (cursor.moveToNext()) {
-                final RootInfo root = RootInfo.fromRootsCursor(AUTHORITY, cursor);
-                final String id = root.rootId;
-                mRoots.add(root);
+                mRoots.add(RootInfo.fromRootsCursor(AUTHORITY, cursor));
             }
         } finally {
             IoUtils.closeQuietly(cursor);
@@ -100,68 +149,94 @@
         super.tearDown();
     }
 
-    public List<Uri> setupTestFiles() throws Exception {
-        Uri rootUri = DocumentsContract.buildDocumentUri(AUTHORITY, mRoots.get(0).documentId);
-        List<Uri> testFiles = Lists.newArrayList(
-                DocumentsContract.createDocument(mClient, rootUri, "text/plain", "test0.txt"),
-                DocumentsContract.createDocument(mClient, rootUri, "text/plain", "test1.txt"),
-                DocumentsContract.createDocument(mClient, rootUri, "text/plain", "test2.txt")
-        );
-        String testContent[] = {
-                "The five boxing wizards jump quickly",
-                "The quick brown fox jumps over the lazy dog",
-                "Jackdaws love my big sphinx of quartz"
-        };
-        for (int i = 0; i < testFiles.size(); ++i) {
-            ParcelFileDescriptor pfd = null;
-            OutputStream out = null;
-            try {
-                pfd = mClient.openFile(testFiles.get(i), "w");
-                out = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);
-                out.write(testContent[i].getBytes());
-            } finally {
-                IoUtils.closeQuietly(out);
-            }
-        }
-        return testFiles;
-    }
-
     /**
      * Test copying a single file.
      */
     public void testCopyFile() throws Exception {
-        Uri testFile = setupTestFiles().get(0);
+        String srcPath = "/test0.txt";
+        Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
+                "The five boxing wizards jump quickly".getBytes());
 
-        // Just copy one file.
+        assertDstFileCountEquals(0);
+
         copyToDestination(Lists.newArrayList(testFile));
 
-        // A call to NotificationManager.cancel marks the end of the copy operation.
-        Mockito.verify(mNotificationManager, Mockito.timeout(1000)).cancel(Mockito.anyString(),
-                Mockito.anyInt());
+        // 2 operations: file creation, then writing data.
+        mResolver.waitForChanges(2);
 
         // Verify that one file was copied; check file contents.
         assertDstFileCountEquals(1);
-        assertCopied(testFile);
+        assertCopied(srcPath);
     }
 
     /**
      * Test copying multiple files.
      */
     public void testCopyMultipleFiles() throws Exception {
-        List<Uri> testFiles = setupTestFiles();
+        String testContent[] = {
+                "The five boxing wizards jump quickly",
+                "The quick brown fox jumps over the lazy dog",
+                "Jackdaws love my big sphinx of quartz"
+        };
+        String srcPaths[] = {
+                "/test0.txt",
+                "/test1.txt",
+                "/test2.txt"
+        };
+        List<Uri> testFiles = Lists.newArrayList(
+                mStorage.createFile(SRC, srcPaths[0], "text/plain", testContent[0].getBytes()),
+                mStorage.createFile(SRC, srcPaths[1], "text/plain", testContent[1].getBytes()),
+                mStorage.createFile(SRC, srcPaths[2], "text/plain", testContent[2].getBytes()));
+
+        assertDstFileCountEquals(0);
+
         // Copy all the test files.
         copyToDestination(testFiles);
 
-        // A call to NotificationManager.cancel marks the end of the copy operation.
-        Mockito.verify(mNotificationManager, Mockito.timeout(1000)).cancel(Mockito.anyString(),
-                Mockito.anyInt());
+        // 3 file creations, 3 file writes.
+        mResolver.waitForChanges(6);
 
         assertDstFileCountEquals(3);
-        for (Uri testFile : testFiles) {
-            assertCopied(testFile);
+        for (String path : srcPaths) {
+            assertCopied(path);
         }
     }
 
+    public void testCopyEmptyDir() throws Exception {
+        String srcPath = "/emptyDir";
+        Uri testDir = mStorage.createFile(SRC, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
+                null);
+
+        assertDstFileCountEquals(0);
+
+        copyToDestination(Lists.newArrayList(testDir));
+
+        // Just 1 operation: Directory creation.
+        mResolver.waitForChanges(1);
+
+        assertDstFileCountEquals(1);
+
+        File dst = mStorage.getFile(DST, srcPath);
+        assertTrue(dst.isDirectory());
+    }
+
+    public void testReadErrors() throws Exception {
+        String srcPath = "/test0.txt";
+        Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
+                "The five boxing wizards jump quickly".getBytes());
+
+        assertDstFileCountEquals(0);
+
+        mStorage.simulateReadErrors(true);
+
+        copyToDestination(Lists.newArrayList(testFile));
+
+        // 3 operations: file creation, writing, then deletion (due to failed copy).
+        mResolver.waitForChanges(3);
+
+        assertDstFileCountEquals(0);
+    }
+
     /**
      * Copies the given files to a pre-determined destination.
      *
@@ -200,82 +275,55 @@
         assertEquals("Incorrect file count after copy", expected, count);
     }
 
-    /**
-     * Verifies that the file pointed to by the given URI was correctly copied to the destination.
-     */
-    private void assertCopied(Uri src) throws Exception {
-        Cursor cursor = null;
-        String srcName = null;
+    private void assertCopied(String path) throws Exception {
+        File srcFile = mStorage.getFile(SRC, path);
+        File dstFile = mStorage.getFile(DST, path);
+        assertNotNull(dstFile);
+
+        FileInputStream src = null;
+        FileInputStream dst = null;
         try {
-            cursor = mClient.query(src, null, null, null, null);
-            if (cursor.moveToFirst()) {
-                srcName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
-            }
+            src = new FileInputStream(srcFile);
+            dst = new FileInputStream(dstFile);
+            byte[] srcbuf = Streams.readFully(src);
+            byte[] dstbuf = Streams.readFully(dst);
+
+            MoreAsserts.assertEquals(srcbuf, dstbuf);
         } finally {
-            IoUtils.closeQuietly(cursor);
+            IoUtils.closeQuietly(src);
+            IoUtils.closeQuietly(dst);
         }
-        Uri dst = getDstFileUri(srcName);
-
-        InputStream in0 = null;
-        InputStream in1 = null;
-        try {
-            in0 = new ParcelFileDescriptor.AutoCloseInputStream(mClient.openFile(src, "r"));
-            in1 = new ParcelFileDescriptor.AutoCloseInputStream(mClient.openFile(dst, "r"));
-
-            byte[] buffer0 = Streams.readFully(in0);
-            byte[] buffer1 = Streams.readFully(in1);
-
-            MoreAsserts.assertEquals(buffer0, buffer1);
-        } finally {
-            IoUtils.closeQuietly(in0);
-            IoUtils.closeQuietly(in1);
-        }
-    }
-
-    /**
-     * Generates a file URI from a given filename. This assumes the file already exists in the
-     * destination root.
-     */
-    private Uri getDstFileUri(String filename) throws RemoteException {
-        final Uri dstFileQuery = DocumentsContract.buildChildDocumentsUri(AUTHORITY,
-                mRoots.get(1).documentId);
-        Cursor cursor = null;
-        try {
-            // StubProvider doesn't seem to support query strings; filter the results manually.
-            cursor = mClient.query(dstFileQuery, null, null, null, null);
-            while (cursor.moveToNext()) {
-                if (filename.equals(getCursorString(cursor, Document.COLUMN_DISPLAY_NAME))) {
-                    return DocumentsContract.buildDocumentUri(AUTHORITY,
-                            getCursorString(cursor, Document.COLUMN_DOCUMENT_ID));
-                }
-            }
-        } finally {
-            IoUtils.closeQuietly(cursor);
-        }
-        return null;
     }
 
     /**
      * Sets up a ContextWrapper that substitutes a stub NotificationManager. This allows the test to
      * listen for notification events, to gauge copy progress.
+     *
+     * @throws FileNotFoundException
      */
-    private void setupTestContext() {
-        mContext = getSystemContext();
-        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().getPath());
+    private void setupTestContext() throws FileNotFoundException {
+        mSystemContext = getSystemContext();
 
-        mNotificationManager = Mockito.spy((NotificationManager) mContext
-                .getSystemService(Context.NOTIFICATION_SERVICE));
-
-        // Insert a stub NotificationManager that enables us to listen for when copying is complete.
-        setContext(new ContextWrapper(mContext) {
+        // Set up the context with the test content resolver.
+        mResolver = new TestContentResolver();
+        mContext = new ContextWrapper(mSystemContext) {
             @Override
-            public Object getSystemService(String name) {
-                if (Context.NOTIFICATION_SERVICE.equals(name)) {
-                    return mNotificationManager;
-                } else {
-                    return super.getSystemService(name);
-                }
+            public ContentResolver getContentResolver() {
+                return mResolver;
             }
-        });
+        };
+        setContext(mContext);
+
+        // Create a local stub provider and add it to the content resolver.
+        ProviderInfo info = new ProviderInfo();
+        info.authority = AUTHORITY;
+        info.exported = true;
+        info.grantUriPermissions = true;
+        info.readPermission = android.Manifest.permission.MANAGE_DOCUMENTS;
+        info.writePermission = android.Manifest.permission.MANAGE_DOCUMENTS;
+
+        mStorage = new StubProvider();
+        mStorage.attachInfo(mContext, info);
+        mResolver.addProvider(AUTHORITY, mStorage);
     }
 }
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
index 438f6cd..8cef433 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
@@ -21,9 +21,10 @@
 import android.content.pm.ProviderInfo;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
-import android.database.MatrixCursor.RowBuilder;
 import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
 import android.graphics.Point;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.FileUtils;
@@ -32,15 +33,16 @@
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
 import android.provider.DocumentsProvider;
+import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 
 import com.google.android.collect.Maps;
 
 import libcore.io.IoUtils;
 
-import java.io.FileOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -70,6 +72,7 @@
     private String mAuthority;
     private SharedPreferences mPrefs;
     private Map<String, RootInfo> mRoots;
+    private boolean mSimulateReadErrors;
 
     @Override
     public void attachInfo(Context context, ProviderInfo info) {
@@ -83,7 +86,8 @@
         return true;
     }
 
-    private void clearCacheAndBuildRoots() {
+    @VisibleForTesting
+    public void clearCacheAndBuildRoots() {
         final File cacheDir = getContext().getCacheDir();
         removeRecursively(cacheDir);
         mStorage.clear();
@@ -164,7 +168,7 @@
         } else {
             try {
                 if (!file.createNewFile()) {
-                    throw new FileNotFoundException();
+                    throw new IllegalStateException("The file " + file.getPath() + " already exists");
                 }
             } catch (IOException e) {
                 throw new FileNotFoundException();
@@ -173,6 +177,10 @@
 
         final StubDocument document = new StubDocument(file, mimeType, parentDocument);
         notifyParentChanged(document.parentId);
+        getContext().getContentResolver().notifyChange(
+                DocumentsContract.buildDocumentUri(mAuthority, document.documentId),
+                null, false);
+
         return document.documentId;
     }
 
@@ -187,6 +195,9 @@
             document.rootInfo.size -= fileSize;
         }
         notifyParentChanged(document.parentId);
+        getContext().getContentResolver().notifyChange(
+                DocumentsContract.buildDocumentUri(mAuthority, document.documentId),
+                null, false);
     }
 
     @Override
@@ -226,7 +237,17 @@
             throw new FileNotFoundException();
 
         if ("r".equals(mode)) {
-            return ParcelFileDescriptor.open(document.file, ParcelFileDescriptor.MODE_READ_ONLY);
+            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(document.file,
+                    ParcelFileDescriptor.MODE_READ_ONLY);
+            if (mSimulateReadErrors) {
+                pfd = new ParcelFileDescriptor(pfd) {
+                    @Override
+                    public void checkError() throws IOException {
+                        throw new IOException("Test error");
+                    }
+                };
+            }
+            return pfd;
         }
         if ("w".equals(mode)) {
             return startWrite(document);
@@ -235,6 +256,11 @@
         throw new FileNotFoundException();
     }
 
+    @VisibleForTesting
+    public void simulateReadErrors(boolean b) {
+        mSimulateReadErrors = b;
+    }
+
     @Override
     public AssetFileDescriptor openDocumentThumbnail(
             String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
@@ -281,11 +307,15 @@
                         }
                     }
                 } catch (IOException e) {
+                    Log.e(TAG, "Error on close", e);
                     closePipeWithErrorSilently(readPipe, e.getMessage());
                 } finally {
                     IoUtils.closeQuietly(inputStream);
                     IoUtils.closeQuietly(outputStream);
                     notifyParentChanged(document.parentId);
+                    getContext().getContentResolver().notifyChange(
+                            DocumentsContract.buildDocumentUri(mAuthority, document.documentId),
+                            null, false);
                 }
             }
         }.start();
@@ -302,7 +332,6 @@
 
     @Override
     public Bundle call(String method, String arg, Bundle extras) {
-        Log.d(TAG, "call: " + method + arg);
         switch (method) {
             case "clear":
                 clearCacheAndBuildRoots();
@@ -376,30 +405,51 @@
         }
     }
 
-    public File createFile(String rootId, File parent, String mimeType, String name)
-            throws IOException {
-        StubDocument parentDoc = null;
+    @VisibleForTesting
+    public Uri createFile(String rootId, String path, String mimeType, byte[] content)
+            throws FileNotFoundException, IOException {
+        StubDocument root = mRoots.get(rootId).rootDocument;
+        if (root == null) {
+            throw new FileNotFoundException("No roots with the ID " + rootId + " were found");
+        }
+        File file = new File(root.file, path.substring(1));
+        StubDocument parent = mStorage.get(getDocumentIdForFile(file.getParentFile()));
         if (parent == null) {
-            // Use the root dir as the parent, if one wasn't specified.
-            parentDoc = mRoots.get(rootId).rootDocument;
-        } else {
-            // Verify that the parent exists and is a directory.
-            parentDoc = mStorage.get(getDocumentIdForFile(parent));
-            if (parentDoc == null) {
-                throw new IllegalArgumentException("Parent file not found.");
-            }
-            if (!Document.MIME_TYPE_DIR.equals(parentDoc.mimeType)) {
-                throw new IllegalArgumentException("Parent file must be a directory.");
-            }
+            parent = mStorage.get(createFile(rootId, file.getParentFile().getPath(),
+                    DocumentsContract.Document.MIME_TYPE_DIR, null));
         }
-        File file = new File(parentDoc.file, name);
-        if (Document.MIME_TYPE_DIR.equals(mimeType)) {
-            file.mkdir();
+
+        if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
+            if (!file.mkdirs()) {
+                throw new FileNotFoundException("Couldn't create directory " + file.getPath());
+            }
         } else {
-            file.createNewFile();
+            if (!file.createNewFile()) {
+                throw new FileNotFoundException("Couldn't create file " + file.getPath());
+            }
+            // Add content to the file.
+            FileOutputStream fout = new FileOutputStream(file);
+            fout.write(content);
+            fout.close();
         }
-        new StubDocument(file, mimeType, parentDoc);
-        return file;
+        final StubDocument document = new StubDocument(file, mimeType, parent);
+        return DocumentsContract.buildDocumentUri(mAuthority,  document.documentId);
+    }
+
+    @VisibleForTesting
+    public File getFile(String rootId, String path) throws FileNotFoundException {
+        StubDocument root = mRoots.get(rootId).rootDocument;
+        if (root == null) {
+            throw new FileNotFoundException("No roots with the ID " + rootId + " were found");
+        }
+        // Convert the path string into a path that's relative to the root.
+        File needle = new File(root.file, path.substring(1));
+
+        StubDocument found = mStorage.get(getDocumentIdForFile(needle));
+        if (found == null) {
+            return null;
+        }
+        return found.file;
     }
 
     final class RootInfo {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c7b7628..d13d71c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -58,7 +58,6 @@
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
 import android.hardware.fingerprint.FingerprintUtils;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
-import android.service.trust.TrustAgentService;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -154,6 +153,7 @@
     private SubscriptionManager mSubscriptionManager;
     private List<SubscriptionInfo> mSubscriptionInfo;
     private boolean mFingerprintDetectionRunning;
+    private TrustManager mTrustManager;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -550,7 +550,7 @@
 
         @Override
         public void onAuthenticationError(int errMsgId, CharSequence errString) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, errMsgId, 0, errString);
+            mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, errMsgId, 0, errString).sendToTarget();
         }
     };
     private CancellationSignal mFingerprintCancelSignal;
@@ -784,8 +784,8 @@
             e.printStackTrace();
         }
 
-        TrustManager trustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
-        trustManager.registerTrustListener(this);
+        mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
+        mTrustManager.registerTrustListener(this);
 
         mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
         updateFingerprintListeningState();
@@ -801,7 +801,8 @@
     }
 
     private boolean shouldListenForFingerprint() {
-        return mScreenOn && mKeyguardIsVisible && !mSwitchingUser;
+        return mScreenOn && mKeyguardIsVisible && !mSwitchingUser
+                && mTrustManager.hasUserAuthenticatedSinceBoot(ActivityManager.getCurrentUser());
     }
 
     private void startListeningForFingerprint() {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b2c9cd0..4082bf5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -111,8 +111,10 @@
     <string name="bluetooth_profile_pbap_summary">Use for contact sharing</string>
     <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (sharing this device's Internet connection). [CHAR LIMIT=40] -->
     <string name="bluetooth_profile_pan_nap">Internet connection sharing</string>
-    <!-- Bluetooth settings.  The user-visible string that is used whenever referring to the map profile. -->
+    <!-- Bluetooth settings. The user-visible string that is used whenever referring to the map profile. -->
     <string name="bluetooth_profile_map">Message Access</string>
+    <!-- Bluetooth settings. The user-visible string that is used whenever referring to the SAP profile (sharing SIM card). -->
+    <string name="bluetooth_profile_sap">SIM Access</string>
 
     <!-- Bluetooth settings.  Connection options screen.  The summary for the A2DP checkbox preference when A2DP is connected. -->
     <string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
@@ -122,6 +124,8 @@
     <string name="bluetooth_opp_profile_summary_connected">Connected to file transfer server</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the map checkbox preference when map is connected. -->
     <string name="bluetooth_map_profile_summary_connected">Connected to map</string>
+    <!-- Bluetooth settings.  Connection options screen.  The summary for the sap checkbox preference when SAP is connected. -->
+    <string name="bluetooth_sap_profile_summary_connected">Connected to SAP</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the OPP checkbox preference when OPP is not connected. -->
     <string name="bluetooth_opp_profile_summary_not_connected">Not connected to file transfer server</string>
     <!-- Bluetooth settings. Connection options screen. The summary for the HID checkbox preference when HID is connected. -->
@@ -137,6 +141,8 @@
     <string name="bluetooth_pan_profile_summary_use_for">Use for Internet access</string>
     <!-- Bluetooth settings. Connection options screen.  The summary for the map checkbox preference that describes how checking it will set the map profile as preferred. -->
     <string name="bluetooth_map_profile_summary_use_for">Use for map</string>
+    <!-- Bluetooth settings.  Connection options screen.  The summary for the sap checkbox preference that describes how checking it will set the sap profile as preferred. -->
+    <string name="bluetooth_sap_profile_summary_use_for">Use for SIM access</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the A2DP checkbox preference that describes how checking it will set the A2DP profile as preferred. -->
     <string name="bluetooth_a2dp_profile_summary_use_for">Use for media audio</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the headset checkbox preference that describes how checking it will set the headset profile as preferred. -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index dd2368f..7534b8e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -67,10 +67,6 @@
 
     private boolean mVisible;
 
-    private int mPhonebookPermissionChoice;
-
-    private int mMessagePermissionChoice;
-
     private int mMessageRejectionCount;
 
     private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
@@ -556,6 +552,7 @@
             mConnectAfterPairing = false;  // cancel auto-connect
             setPhonebookPermissionChoice(ACCESS_UNKNOWN);
             setMessagePermissionChoice(ACCESS_UNKNOWN);
+            setSimPermissionChoice(ACCESS_UNKNOWN);
             mMessageRejectionCount = 0;
             saveMessageRejectionCount();
         }
@@ -732,6 +729,26 @@
         mDevice.setMessageAccessPermission(permission);
     }
 
+    public int getSimPermissionChoice() {
+        int permission = mDevice.getSimAccessPermission();
+        if (permission == BluetoothDevice.ACCESS_ALLOWED) {
+            return ACCESS_ALLOWED;
+        } else if (permission == BluetoothDevice.ACCESS_REJECTED) {
+            return ACCESS_REJECTED;
+        }
+        return ACCESS_UNKNOWN;
+    }
+
+    void setSimPermissionChoice(int permissionChoice) {
+        int permission = BluetoothDevice.ACCESS_UNKNOWN;
+        if (permissionChoice == ACCESS_ALLOWED) {
+            permission = BluetoothDevice.ACCESS_ALLOWED;
+        } else if (permissionChoice == ACCESS_REJECTED) {
+            permission = BluetoothDevice.ACCESS_REJECTED;
+        }
+        mDevice.setSimAccessPermission(permission);
+    }
+
     // Migrates data from old data store (in Settings app's shared preferences) to new (in Bluetooth
     // app's shared preferences).
     private void migrateMessagePermissionChoice() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
new file mode 100644
index 0000000..25c53e6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2015 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.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSap;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * SapProfile handles Bluetooth SAP profile.
+ */
+final class SapProfile implements LocalBluetoothProfile {
+    private static final String TAG = "SapProfile";
+    private static boolean V = true;
+
+    private BluetoothSap mService;
+    private boolean mIsProfileReady;
+
+    private final LocalBluetoothAdapter mLocalAdapter;
+    private final CachedBluetoothDeviceManager mDeviceManager;
+    private final LocalBluetoothProfileManager mProfileManager;
+
+    static final ParcelUuid[] UUIDS = {
+        BluetoothUuid.SAP,
+    };
+
+    static final String NAME = "SAP";
+
+    // Order of this profile in device profiles list
+    private static final int ORDINAL = 10;
+
+    // These callbacks run on the main thread.
+    private final class SapServiceListener
+            implements BluetoothProfile.ServiceListener {
+
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (V) Log.d(TAG,"Bluetooth service connected");
+            mService = (BluetoothSap) proxy;
+            // We just bound to the service, so refresh the UI for any connected SAP devices.
+            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+            while (!deviceList.isEmpty()) {
+                BluetoothDevice nextDevice = deviceList.remove(0);
+                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+                // we may add a new device here, but generally this should not happen
+                if (device == null) {
+                    Log.w(TAG, "SapProfile found new device: " + nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                }
+                device.onProfileStateChanged(SapProfile.this,
+                        BluetoothProfile.STATE_CONNECTED);
+                device.refresh();
+            }
+
+            mProfileManager.callServiceConnectedListeners();
+            mIsProfileReady=true;
+        }
+
+        public void onServiceDisconnected(int profile) {
+            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            mProfileManager.callServiceDisconnectedListeners();
+            mIsProfileReady=false;
+        }
+    }
+
+    public boolean isProfileReady() {
+        return mIsProfileReady;
+    }
+
+    SapProfile(Context context, LocalBluetoothAdapter adapter,
+            CachedBluetoothDeviceManager deviceManager,
+            LocalBluetoothProfileManager profileManager) {
+        mLocalAdapter = adapter;
+        mDeviceManager = deviceManager;
+        mProfileManager = profileManager;
+        mLocalAdapter.getProfileProxy(context, new SapServiceListener(),
+                BluetoothProfile.SAP);
+    }
+
+    public boolean isConnectable() {
+        return true;
+    }
+
+    public boolean isAutoConnectable() {
+        return true;
+    }
+
+    public boolean connect(BluetoothDevice device) {
+        if (mService == null) return false;
+        List<BluetoothDevice> sinks = mService.getConnectedDevices();
+        if (sinks != null) {
+            for (BluetoothDevice sink : sinks) {
+                mService.disconnect(sink);
+            }
+        }
+        return mService.connect(device);
+    }
+
+    public boolean disconnect(BluetoothDevice device) {
+        if (mService == null) return false;
+        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+        if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
+            if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            }
+            return mService.disconnect(device);
+        } else {
+            return false;
+        }
+    }
+
+    public int getConnectionStatus(BluetoothDevice device) {
+        if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+
+        return !deviceList.isEmpty() && deviceList.get(0).equals(device)
+                ? mService.getConnectionState(device)
+                : BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    public boolean isPreferred(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+    }
+
+    public int getPreferred(BluetoothDevice device) {
+        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        return mService.getPriority(device);
+    }
+
+    public void setPreferred(BluetoothDevice device, boolean preferred) {
+        if (mService == null) return;
+        if (preferred) {
+            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            }
+        } else {
+            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+        }
+    }
+
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        return mService.getDevicesMatchingConnectionStates(
+              new int[] {BluetoothProfile.STATE_CONNECTED,
+                         BluetoothProfile.STATE_CONNECTING,
+                         BluetoothProfile.STATE_DISCONNECTING});
+    }
+
+    public String toString() {
+        return NAME;
+    }
+
+    public int getOrdinal() {
+        return ORDINAL;
+    }
+
+    public int getNameResource(BluetoothDevice device) {
+        return R.string.bluetooth_profile_sap;
+    }
+
+    public int getSummaryResourceForDevice(BluetoothDevice device) {
+        int state = getConnectionStatus(device);
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return R.string.bluetooth_sap_profile_summary_use_for;
+
+            case BluetoothProfile.STATE_CONNECTED:
+                return R.string.bluetooth_sap_profile_summary_connected;
+
+            default:
+                return Utils.getConnectionStateSummary(state);
+        }
+    }
+
+    public int getDrawableResource(BluetoothClass btClass) {
+        return R.drawable.ic_bt_cellphone;
+    }
+
+    protected void finalize() {
+        if (V) Log.d(TAG, "finalize()");
+        if (mService != null) {
+            try {
+                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.SAP,
+                                                                       mService);
+                mService = null;
+            }catch (Throwable t) {
+                Log.w(TAG, "Error cleaning up SAP proxy", t);
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index 1cf7248..c81f22a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -32,7 +32,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageVolume;
@@ -349,6 +348,11 @@
         final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
                 details);
 
+        if (mVolume == null || !mVolume.isMountedReadable()) {
+            finished.sendToTarget();
+            return;
+        }
+
         if (mSharedVolume != null && mSharedVolume.isMountedReadable()) {
             final File basePath = mSharedVolume.getPathForUser(currentUser);
 
@@ -375,8 +379,10 @@
         }
 
         final File file = mVolume.getPath();
-        details.totalSize = file.getTotalSpace();
-        details.availSize = file.getFreeSpace();
+        if (file != null) {
+            details.totalSize = file.getTotalSpace();
+            details.availSize = file.getFreeSpace();
+        }
 
         // Measure all apps hosted on this volume for all users
         if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) {
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e5a39c8..8466a5a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -958,6 +958,12 @@
     <!-- Button label for ending zen mode in the volume dialog -->
     <string name="volume_zen_end_now">End now</string>
 
+    <!-- Content description for accessibility (not shown on the screen): volume dialog expand button. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_volume_expand">Expand</string>
+
+    <!-- Content description for accessibility (not shown on the screen): volume dialog collapse button. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_volume_collapse">Collapse</string>
+
     <!-- Screen pinning dialog title. -->
     <string name="screen_pinning_title">Screen is pinned</string>
     <!-- Screen pinning dialog description. -->
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 1001feb..ad97f91 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -39,7 +39,6 @@
 import com.android.systemui.recents.misc.DebugTrigger;
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
@@ -392,15 +391,6 @@
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
         registerReceiver(mSystemBroadcastReceiver, filter);
-
-        // Private API calls to make the shadows look better
-        try {
-            Utilities.setShadowProperty("ambientRatio", String.valueOf(1.5f));
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        } catch (InvocationTargetException e) {
-            e.printStackTrace();
-        }
     }
 
     /** Inflates the debug overlay if debug mode is enabled. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 84544ff..e810410 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -22,27 +22,11 @@
 import android.graphics.Rect;
 import android.view.View;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 
 /* Common code */
 public class Utilities {
 
-    // Reflection methods for altering shadows
-    private static Method sPropertyMethod;
-    static {
-        try {
-            Class<?> c = Class.forName("android.view.DisplayListCanvas");
-            sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
-            if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
-        } catch (ClassNotFoundException e) {
-            e.printStackTrace();
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-        }
-    }
-
     /** Scales a rect about its centroid */
     public static void scaleRectAboutCenter(Rect r, float scale) {
         if (scale != 1.0f) {
@@ -163,12 +147,6 @@
                     (1f - overlayAlpha) * Color.blue(overlayColor)));
     }
 
-    /** Sets some private shadow properties. */
-    public static void setShadowProperty(String property, String value)
-            throws IllegalAccessException, InvocationTargetException {
-        sPropertyMethod.invoke(null, property, value);
-    }
-
     /**
      * Cancels an animation ensuring that if it has listeners, onCancel and onEnd
      * are not called.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index f3d4c7f..d5209ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -80,6 +80,14 @@
         }
     }
 
+    public void setBouncerShowing(boolean showing) {
+        if (mPanelHolder != null) {
+            mPanelHolder.setImportantForAccessibility(
+                    showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                            : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+        }
+    }
+
     public float getBarHeight() {
         return getMeasuredHeight();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f3335af..351977b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -76,6 +76,7 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.ThreadedRenderer;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
@@ -859,6 +860,12 @@
         // listen for USER_SETUP_COMPLETE setting (per-user)
         resetUserSetupObserver();
 
+        // disable profiling bars, since they overlap and clutter the output on app windows
+        ThreadedRenderer.overrideProperty("disableProfileBars", "true");
+
+        // Private API call to make the shadows look better for Recents
+        ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
+
         return mStatusBarView;
     }
 
@@ -3608,6 +3615,7 @@
     @Override
     public void setBouncerShowing(boolean bouncerShowing) {
         super.setBouncerShowing(bouncerShowing);
+        mStatusBarView.setBouncerShowing(bouncerShowing);
         disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java
index f2b2f66..a7fdadc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataControllerImpl.java
@@ -162,7 +162,7 @@
                 usage.warningLevel = DEFAULT_WARNING_LEVEL;
             }
             if (usage != null) {
-                usage.carrier = mNetworkController.getMobileNetworkName();
+                usage.carrier = mNetworkController.getMobileDataNetworkName();
             }
             return usage;
         } catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index c3c6b12..22bf47c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -84,6 +84,7 @@
         mapIconSets();
 
         mLastState.networkName = mCurrentState.networkName = mNetworkNameDefault;
+        mLastState.networkNameData = mCurrentState.networkNameData = mNetworkNameDefault;
         mLastState.enabled = mCurrentState.enabled = hasMobileData;
         mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
         // Get initial data sim state.
@@ -294,6 +295,7 @@
         if (action.equals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION)) {
             updateNetworkName(intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false),
                     intent.getStringExtra(TelephonyIntents.EXTRA_SPN),
+                    intent.getStringExtra(TelephonyIntents.EXTRA_DATA_SPN),
                     intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
                     intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
             notifyListenersIfNecessary();
@@ -322,14 +324,18 @@
     /**
      * Updates the network's name based on incoming spn and plmn.
      */
-    void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
+    void updateNetworkName(boolean showSpn, String spn, String dataSpn,
+            boolean showPlmn, String plmn) {
         if (CHATTY) {
-            Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+            Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn
+                    + " spn=" + spn + " dataSpn=" + dataSpn
                     + " showPlmn=" + showPlmn + " plmn=" + plmn);
         }
         StringBuilder str = new StringBuilder();
+        StringBuilder strData = new StringBuilder();
         if (showPlmn && plmn != null) {
             str.append(plmn);
+            strData.append(plmn);
         }
         if (showSpn && spn != null) {
             if (str.length() != 0) {
@@ -342,6 +348,17 @@
         } else {
             mCurrentState.networkName = mNetworkNameDefault;
         }
+        if (showSpn && dataSpn != null) {
+            if (strData.length() != 0) {
+                strData.append(mNetworkNameSeparator);
+            }
+            strData.append(dataSpn);
+        }
+        if (strData.length() != 0) {
+            mCurrentState.networkNameData = strData.toString();
+        } else {
+            mCurrentState.networkNameData = mNetworkNameDefault;
+        }
     }
 
     /**
@@ -492,6 +509,7 @@
 
     static class MobileState extends SignalController.State {
         String networkName;
+        String networkNameData;
         boolean dataSim;
         boolean dataConnected;
         boolean isEmergency;
@@ -505,6 +523,7 @@
             MobileState state = (MobileState) s;
             dataSim = state.dataSim;
             networkName = state.networkName;
+            networkNameData = state.networkNameData;
             dataConnected = state.dataConnected;
             inetForNetwork = state.inetForNetwork;
             isEmergency = state.isEmergency;
@@ -518,6 +537,7 @@
             builder.append(',');
             builder.append("dataSim=").append(dataSim).append(',');
             builder.append("networkName=").append(networkName).append(',');
+            builder.append("networkNameData=").append(networkNameData).append(',');
             builder.append("dataConnected=").append(dataConnected).append(',');
             builder.append("inetForNetwork=").append(inetForNetwork).append(',');
             builder.append("isEmergency=").append(isEmergency).append(',');
@@ -529,6 +549,7 @@
         public boolean equals(Object o) {
             return super.equals(o)
                     && Objects.equals(((MobileState) o).networkName, networkName)
+                    && Objects.equals(((MobileState) o).networkNameData, networkNameData)
                     && ((MobileState) o).dataSim == dataSim
                     && ((MobileState) o).dataConnected == dataConnected
                     && ((MobileState) o).isEmergency == isEmergency
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 6d88535..df133e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -246,9 +246,9 @@
         return mDefaultSignalController;
     }
 
-    public String getMobileNetworkName() {
+    public String getMobileDataNetworkName() {
         MobileSignalController controller = getDataController();
-        return controller != null ? controller.getState().networkName : "";
+        return controller != null ? controller.getState().networkNameData : "";
     }
 
     public boolean isEmergencyOnly() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 9434036..7115897 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.volume;
 
+import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
@@ -32,6 +35,7 @@
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.media.AudioSystem;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -43,6 +47,8 @@
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnClickListener;
 import android.view.View.OnLayoutChangeListener;
 import android.view.View.OnTouchListener;
@@ -50,6 +56,9 @@
 import android.view.ViewGroup.MarginLayoutParams;
 import android.view.Window;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
@@ -97,6 +106,7 @@
     private final ZenFooter mZenFooter;
     private final LayoutTransition mLayoutTransition;
     private final Object mSafetyWarningLock = new Object();
+    private final Accessibility mAccessibility = new Accessibility();
 
     private boolean mShowing;
     private boolean mExpanded;
@@ -174,6 +184,8 @@
         mZenFooter = (ZenFooter) mDialog.findViewById(R.id.volume_zen_footer);
         mZenFooter.init(zenModeController);
 
+        mAccessibility.init();
+
         controller.addCallback(mControllerCallbackH, mHandler);
         controller.getState();
     }
@@ -409,10 +421,13 @@
 
     protected void rescheduleTimeoutH() {
         mHandler.removeMessages(H.DISMISS);
-        final int timeout = computeTimeoutH();
-        if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout);
-        mHandler.sendMessageDelayed(mHandler
-                .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+        int timeout = -1;
+        if (!mAccessibility.mFeedbackEnabled) {
+            timeout = computeTimeoutH();
+            mHandler.sendMessageDelayed(mHandler
+                    .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+        }
+        if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
         mController.userActivity();
     }
 
@@ -475,6 +490,8 @@
         if (res == mExpandButtonRes) return;
         mExpandButtonRes = res;
         mExpandButton.setImageResource(res);
+        mExpandButton.setContentDescription(mContext.getString(mExpanded ?
+                R.string.accessibility_volume_collapse : R.string.accessibility_volume_expand));
     }
 
     private boolean isVisibleH(VolumeRow row, boolean isActive) {
@@ -632,6 +649,7 @@
                 : (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes)
                         ? Events.ICON_STATE_UNMUTE
                 : Events.ICON_STATE_UNKNOWN;
+        row.icon.setContentDescription(ss.name);
 
         // update slider
         updateVolumeRowSliderH(row, zenMuted);
@@ -931,6 +949,56 @@
         }
     }
 
+    private final class Accessibility {
+        private AccessibilityManager mMgr;
+        private boolean mFeedbackEnabled;
+
+        public void init() {
+            mMgr = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+            mDialogView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    // noop
+                }
+
+                @Override
+                public void onViewAttachedToWindow(View v) {
+                    updateFeedbackEnabled();
+                }
+            });
+            mDialogView.setAccessibilityDelegate(new AccessibilityDelegate() {
+                @Override
+                public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
+                        AccessibilityEvent event) {
+                    rescheduleTimeoutH();
+                    return super.onRequestSendAccessibilityEvent(host, child, event);
+                }
+            });
+            mMgr.addAccessibilityStateChangeListener(new AccessibilityStateChangeListener() {
+                @Override
+                public void onAccessibilityStateChanged(boolean enabled) {
+                    updateFeedbackEnabled();
+                }
+            });
+            updateFeedbackEnabled();
+        }
+
+        private void updateFeedbackEnabled() {
+            mFeedbackEnabled = computeFeedbackEnabled();
+        }
+
+        private boolean computeFeedbackEnabled() {
+            final List<AccessibilityServiceInfo> services =
+                    mMgr.getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
+            for (AccessibilityServiceInfo asi : services) {
+                if ((asi.feedbackType & FEEDBACK_ALL_MASK) != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
     private static class VolumeRow {
         private View view;
         private View space;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index a176c98..29f461e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -306,6 +306,6 @@
     }
 
    protected void assertNetworkNameEquals(String expected) {
-       assertEquals("Network name", expected, mNetworkController.getMobileNetworkName());
+       assertEquals("Network name", expected, mNetworkController.getMobileDataNetworkName());
    }
 }
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index e7f210b..4f10699 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -302,8 +302,12 @@
         long[] fieldIDs, long[] values, int[] sizes, long[] depClosures,
         long[] depFieldIDs) {
       validate();
-      return rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
+      long c = rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
           sizes, depClosures, depFieldIDs);
+      if (c == 0) {
+          throw new RSRuntimeException("Failed creating closure.");
+      }
+      return c;
     }
 
     native long rsnInvokeClosureCreate(long con, long invokeID, byte[] params,
@@ -311,8 +315,12 @@
     synchronized long nInvokeClosureCreate(long invokeID, byte[] params,
         long[] fieldIDs, long[] values, int[] sizes) {
       validate();
-      return rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
+      long c = rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
           values, sizes);
+      if (c == 0) {
+          throw new RSRuntimeException("Failed creating closure.");
+      }
+      return c;
     }
 
     native void rsnClosureSetArg(long con, long closureID, int index,
@@ -337,7 +345,11 @@
     synchronized long nScriptGroup2Create(String name, String cachePath,
                                           long[] closures) {
       validate();
-      return rsnScriptGroup2Create(mContext, name, cachePath, closures);
+      long g = rsnScriptGroup2Create(mContext, name, cachePath, closures);
+      if (g == 0) {
+          throw new RSRuntimeException("Failed creating script group.");
+      }
+      return g;
     }
 
     native void rsnScriptGroup2Execute(long con, long groupID);
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 5ef807d..80d6515 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -43,6 +43,9 @@
 
 //#define LOG_API ALOGE
 static constexpr bool kLogApi = false;
+static constexpr size_t kMaxNumberArgsAndBindings = 1000;
+static constexpr size_t kMaxNumberClosuresInScriptGroup = 1000000;
+static constexpr size_t kMaxNumberKernelArguments = 256;
 
 using namespace android;
 
@@ -328,79 +331,167 @@
                jlong returnValue, jlongArray fieldIDArray,
                jlongArray valueArray, jintArray sizeArray,
                jlongArray depClosureArray, jlongArray depFieldIDArray) {
+  jlong ret = 0;
+
   jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
   jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
-  RsScriptFieldID* fieldIDs =
-      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
-  for (int i = 0; i< fieldIDs_length; i++) {
-    fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
-  }
-
   jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
   jsize values_length = _env->GetArrayLength(valueArray);
-  uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
-  for (int i = 0; i < values_length; i++) {
-    values[i] = (uintptr_t)jValues[i];
-  }
-
-  jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
+  jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
   jsize sizes_length = _env->GetArrayLength(sizeArray);
-
   jlong* jDepClosures =
       _env->GetLongArrayElements(depClosureArray, nullptr);
   jsize depClosures_length = _env->GetArrayLength(depClosureArray);
-  RsClosure* depClosures =
-      (RsClosure*)alloca(sizeof(RsClosure) * depClosures_length);
-  for (int i = 0; i < depClosures_length; i++) {
-    depClosures[i] = (RsClosure)jDepClosures[i];
-  }
-
   jlong* jDepFieldIDs =
       _env->GetLongArrayElements(depFieldIDArray, nullptr);
   jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray);
-  RsScriptFieldID* depFieldIDs =
-      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * depFieldIDs_length);
-  for (int i = 0; i < depClosures_length; i++) {
+
+  size_t numValues, numDependencies;
+  RsScriptFieldID* fieldIDs;
+  uintptr_t* values;
+  RsClosure* depClosures;
+  RsScriptFieldID* depFieldIDs;
+
+  if (fieldIDs_length != values_length || values_length != sizes_length) {
+      ALOGE("Unmatched field IDs, values, and sizes in closure creation.");
+      goto exit;
+  }
+
+  numValues = (size_t)fieldIDs_length;
+
+  if (depClosures_length != depFieldIDs_length) {
+      ALOGE("Unmatched closures and field IDs for dependencies in closure creation.");
+      goto exit;
+  }
+
+  numDependencies = (size_t)depClosures_length;
+
+  if (numDependencies > numValues) {
+      ALOGE("Unexpected number of dependencies in closure creation");
+      goto exit;
+  }
+
+  if (numValues > kMaxNumberArgsAndBindings) {
+      ALOGE("Too many arguments or globals in closure creation");
+      goto exit;
+  }
+
+  fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
+  if (fieldIDs == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numValues; i++) {
+    fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
+  }
+
+  values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
+  if (values == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numValues; i++) {
+    values[i] = (uintptr_t)jValues[i];
+  }
+
+  depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies);
+  if (depClosures == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numDependencies; i++) {
+    depClosures[i] = (RsClosure)jDepClosures[i];
+  }
+
+  depFieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numDependencies);
+  if (depFieldIDs == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numDependencies; i++) {
     depFieldIDs[i] = (RsClosure)jDepFieldIDs[i];
   }
 
-  return (jlong)(uintptr_t)rsClosureCreate(
+  ret = (jlong)(uintptr_t)rsClosureCreate(
       (RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
-      fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
-      (int*)sizes, (size_t)sizes_length,
-      depClosures, (size_t)depClosures_length,
-      depFieldIDs, (size_t)depFieldIDs_length);
+      fieldIDs, numValues, values, numValues,
+      (int*)jSizes, numValues,
+      depClosures, numDependencies,
+      depFieldIDs, numDependencies);
+
+exit:
+
+  _env->ReleaseLongArrayElements(depFieldIDArray, jDepFieldIDs, JNI_ABORT);
+  _env->ReleaseLongArrayElements(depClosureArray, jDepClosures, JNI_ABORT);
+  _env->ReleaseIntArrayElements (sizeArray,       jSizes,       JNI_ABORT);
+  _env->ReleaseLongArrayElements(valueArray,      jValues,      JNI_ABORT);
+  _env->ReleaseLongArrayElements(fieldIDArray,    jFieldIDs,    JNI_ABORT);
+
+  return ret;
 }
 
 static jlong
 nInvokeClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong invokeID,
                      jbyteArray paramArray, jlongArray fieldIDArray, jlongArray valueArray,
                      jintArray sizeArray) {
+  jlong ret = 0;
+
   jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr);
   jsize jParamLength = _env->GetArrayLength(paramArray);
-
   jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
   jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
-  RsScriptFieldID* fieldIDs =
-      (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
-  for (int i = 0; i< fieldIDs_length; i++) {
+  jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
+  jsize values_length = _env->GetArrayLength(valueArray);
+  jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
+  jsize sizes_length = _env->GetArrayLength(sizeArray);
+
+  size_t numValues;
+  RsScriptFieldID* fieldIDs;
+  uintptr_t* values;
+
+  if (fieldIDs_length != values_length || values_length != sizes_length) {
+      ALOGE("Unmatched field IDs, values, and sizes in closure creation.");
+      goto exit;
+  }
+
+  numValues = (size_t) fieldIDs_length;
+
+  if (numValues > kMaxNumberArgsAndBindings) {
+      ALOGE("Too many arguments or globals in closure creation");
+      goto exit;
+  }
+
+  fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
+  if (fieldIDs == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i< numValues; i++) {
     fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
   }
 
-  jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
-  jsize values_length = _env->GetArrayLength(valueArray);
-  uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
-  for (int i = 0; i < values_length; i++) {
+  values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
+  if (values == nullptr) {
+      goto exit;
+  }
+
+  for (size_t i = 0; i < numValues; i++) {
     values[i] = (uintptr_t)jValues[i];
   }
 
-  jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
-  jsize sizes_length = _env->GetArrayLength(sizeArray);
-
-  return (jlong)(uintptr_t)rsInvokeClosureCreate(
+  ret = (jlong)(uintptr_t)rsInvokeClosureCreate(
       (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
-      fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
-      (int*)sizes, (size_t)sizes_length);
+      fieldIDs, numValues, values, numValues,
+      (int*)jSizes, numValues);
+
+exit:
+
+  _env->ReleaseIntArrayElements (sizeArray,       jSizes,       JNI_ABORT);
+  _env->ReleaseLongArrayElements(valueArray,      jValues,      JNI_ABORT);
+  _env->ReleaseLongArrayElements(fieldIDArray,    jFieldIDs,    JNI_ABORT);
+  _env->ReleaseByteArrayElements(paramArray,      jParams,      JNI_ABORT);
+
+  return ret;
 }
 
 static void
@@ -420,20 +511,40 @@
 static long
 nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name,
                     jstring cacheDir, jlongArray closureArray) {
+  jlong ret = 0;
+
   AutoJavaStringToUTF8 nameUTF(_env, name);
   AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
 
   jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
   jsize numClosures = _env->GetArrayLength(closureArray);
-  RsClosure* closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
+
+  RsClosure* closures;
+
+  if (numClosures > (jsize) kMaxNumberClosuresInScriptGroup) {
+    ALOGE("Too many closures in script group");
+    goto exit;
+  }
+
+  closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
+  if (closures == nullptr) {
+      goto exit;
+  }
+
   for (int i = 0; i < numClosures; i++) {
     closures[i] = (RsClosure)jClosures[i];
   }
 
-  return (jlong)(uintptr_t)rsScriptGroup2Create(
+  ret = (jlong)(uintptr_t)rsScriptGroup2Create(
       (RsContext)con, nameUTF.c_str(), nameUTF.length(),
       cacheDirUTF.c_str(), cacheDirUTF.length(),
       closures, numClosures);
+
+exit:
+
+  _env->ReleaseLongArrayElements(closureArray, jClosures, JNI_ABORT);
+
+  return ret;
 }
 
 static void
@@ -1756,8 +1867,14 @@
 
     if (ains != nullptr) {
         in_len = _env->GetArrayLength(ains);
-        in_ptr = _env->GetLongArrayElements(ains, nullptr);
+        if (in_len > (jint)kMaxNumberKernelArguments) {
+            ALOGE("Too many arguments in kernel launch.");
+            // TODO (b/20758983): Report back to Java and throw an exception
+            return;
+        }
 
+        // TODO (b/20760800): Check in_ptr is not null
+        in_ptr = _env->GetLongArrayElements(ains, nullptr);
         if (sizeof(RsAllocation) == sizeof(jlong)) {
             in_allocs = (RsAllocation*)in_ptr;
 
@@ -1765,6 +1882,11 @@
             // Convert from 64-bit jlong types to the native pointer type.
 
             in_allocs = (RsAllocation*)alloca(in_len * sizeof(RsAllocation));
+            if (in_allocs == nullptr) {
+                ALOGE("Failed launching kernel for lack of memory.");
+                _env->ReleaseLongArrayElements(ains, in_ptr, JNI_ABORT);
+                return;
+            }
 
             for (int index = in_len; --index >= 0;) {
                 in_allocs[index] = (RsAllocation)in_ptr[index];
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 4fda370..45909db 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -145,10 +145,9 @@
     private static final char INPUT_METHOD_SEPARATOR = ':';
     private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = ';';
 
-    static final int MSG_SHOW_IM_PICKER = 1;
-    static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
-    static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3;
-    static final int MSG_SHOW_IM_CONFIG = 4;
+    static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
+    static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
+    static final int MSG_SHOW_IM_CONFIG = 3;
 
     static final int MSG_UNBIND_INPUT = 1000;
     static final int MSG_BIND_INPUT = 1010;
@@ -2597,12 +2596,8 @@
     public boolean handleMessage(Message msg) {
         SomeArgs args;
         switch (msg.what) {
-            case MSG_SHOW_IM_PICKER:
-                showInputMethodMenu();
-                return true;
-
             case MSG_SHOW_IM_SUBTYPE_PICKER:
-                showInputMethodSubtypeMenu();
+                showInputMethodMenu();
                 return true;
 
             case MSG_SHOW_IM_SUBTYPE_ENABLER:
@@ -2861,14 +2856,6 @@
 
     // ----------------------------------------------------------------------
 
-    private void showInputMethodMenu() {
-        showInputMethodMenuInternal(false);
-    }
-
-    private void showInputMethodSubtypeMenu() {
-        showInputMethodMenuInternal(true);
-    }
-
     private void showInputMethodAndSubtypeEnabler(String inputMethodId) {
         Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -2893,7 +2880,7 @@
                 && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure();
     }
 
-    private void showInputMethodMenuInternal(boolean showSubtypes) {
+    private void showInputMethodMenu() {
         if (DEBUG) Slog.v(TAG, "Show switching menu");
 
         final Context context = mContext;
@@ -2915,7 +2902,7 @@
 
             final List<ImeSubtypeListItem> imList =
                     mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
-                            showSubtypes, mInputShown, isScreenLocked);
+                            true /* showSubtypes */, mInputShown, isScreenLocked);
 
             if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
                 final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
@@ -3016,7 +3003,7 @@
             };
             mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener);
 
-            if (showSubtypes && !isScreenLocked) {
+            if (!isScreenLocked) {
                 final OnClickListener positiveListener = new OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int whichButton) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index eea6234..82dbfeec 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -414,6 +414,11 @@
     ActivityRecord mFocusedActivity = null;
 
     /**
+     * User id of the last activity mFocusedActivity was set to.
+     */
+    private int mLastFocusedUserId;
+
+    /**
      * List of intents that were used to start the most recent tasks.
      */
     private final RecentTasks mRecentTasks;
@@ -2561,19 +2566,32 @@
                 mWindowManager.setFocusedApp(r.appToken, true);
             }
             applyUpdateLockStateLocked(r);
-            if (last != null && last.userId != mFocusedActivity.userId) {
+            if (mFocusedActivity.userId != mLastFocusedUserId) {
                 mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
                 mHandler.sendMessage(mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG,
                                 mFocusedActivity.userId, 0));
+                mLastFocusedUserId = mFocusedActivity.userId;
             }
         }
-        EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY, mCurrentUserId,
+        EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY,
+                mFocusedActivity == null ? -1 : mFocusedActivity.userId,
                 mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName);
     }
 
     final void clearFocusedActivity(ActivityRecord r) {
         if (mFocusedActivity == r) {
+            ActivityStack stack = mStackSupervisor.getFocusedStack();
+            if (stack != null) {
+                ActivityRecord top = stack.topActivity();
+                if (top != null && top.userId != mLastFocusedUserId) {
+                    mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
+                    mHandler.sendMessage(mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG,
+                                    top.userId, 0));
+                    mLastFocusedUserId = top.userId;
+                }
+            }
             mFocusedActivity = null;
+            EventLog.writeEvent(EventLogTags.AM_FOCUSED_ACTIVITY, -1, "NULL");
         }
     }
 
@@ -10754,9 +10772,7 @@
 
     @Override
     public void killUid(int uid, String reason) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("killUid only available to the system");
-        }
+        enforceCallingPermission(Manifest.permission.KILL_UID, "killUid");
         synchronized (this) {
             killPackageProcessesLocked(null, UserHandle.getAppId(uid), UserHandle.getUserId(uid),
                     ProcessList.FOREGROUND_APP_ADJ-1, false, true, true, false,
diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java
index 8734d97..c82df48 100644
--- a/services/core/java/com/android/server/notification/CalendarTracker.java
+++ b/services/core/java/com/android/server/notification/CalendarTracker.java
@@ -55,7 +55,6 @@
         Attendees.EVENT_ID,
         Attendees.ATTENDEE_EMAIL,
         Attendees.ATTENDEE_STATUS,
-        Attendees.ATTENDEE_TYPE,
     };
 
     private static final String ATTENDEE_SELECTION = Attendees.EVENT_ID + " = ? AND "
@@ -191,16 +190,13 @@
                 final long rowEventId = cursor.getLong(0);
                 final String rowEmail = cursor.getString(1);
                 final int status = cursor.getInt(2);
-                final int type = cursor.getInt(3);
                 final boolean meetsReply = meetsReply(filter.reply, status);
-                final boolean meetsAttendance = meetsAttendance(filter.attendance, type);
                 if (DEBUG) Log.d(TAG, (DEBUG_ATTENDEES ? String.format(
                         "rowEventId=%s, rowEmail=%s, ", rowEventId, rowEmail) : "") +
-                        String.format("status=%s, type=%s, meetsReply=%s, meetsAttendance=%s",
-                        attendeeStatusToString(status), attendeeTypeToString(type), meetsReply,
-                        meetsAttendance));
+                        String.format("status=%s, meetsReply=%s",
+                        attendeeStatusToString(status), meetsReply));
                 final boolean eventMeets = rowEventId == eventId && Objects.equals(rowEmail, email)
-                        && meetsReply && meetsAttendance;
+                        && meetsReply;
                 rt |= eventMeets;
             }
             return rt;
@@ -232,35 +228,17 @@
         }
     }
 
-    private static String attendeeTypeToString(int type) {
-        switch (type) {
-            case Attendees.TYPE_NONE: return "TYPE_NONE";
-            case Attendees.TYPE_REQUIRED: return "TYPE_REQUIRED";
-            case Attendees.TYPE_OPTIONAL: return "TYPE_OPTIONAL";
-            case Attendees.TYPE_RESOURCE: return "TYPE_RESOURCE";
-            default: return "TYPE_" + type;
-        }
-    }
-
-    private static boolean meetsAttendance(int attendance, int attendeeType) {
-        switch (attendance) {
-            case EventInfo.ATTENDANCE_OPTIONAL:
-                return attendeeType == Attendees.TYPE_OPTIONAL;
-            case EventInfo.ATTENDANCE_REQUIRED:
-                return attendeeType == Attendees.TYPE_REQUIRED;
-            default: // EventInfo.ATTENDANCE_REQUIRED_OR_OPTIONAL
-                return true;
-        }
-    }
-
     private static boolean meetsReply(int reply, int attendeeStatus) {
         switch (reply) {
             case EventInfo.REPLY_YES:
                 return attendeeStatus == Attendees.ATTENDEE_STATUS_ACCEPTED;
+            case EventInfo.REPLY_YES_OR_MAYBE:
+                return attendeeStatus == Attendees.ATTENDEE_STATUS_ACCEPTED
+                        || attendeeStatus == Attendees.ATTENDEE_STATUS_TENTATIVE;
             case EventInfo.REPLY_ANY_EXCEPT_NO:
                 return attendeeStatus != Attendees.ATTENDEE_STATUS_DECLINED;
-            default: // EventInfo.REPLY_ANY
-                return true;
+            default:
+                return false;
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 423c732..f30a567 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -277,8 +277,6 @@
     private static final boolean DEBUG_DEXOPT = false;
     private static final boolean DEBUG_ABI_SELECTION = false;
 
-    static final boolean RUNTIME_PERMISSIONS_ENABLED = true;
-
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -2131,13 +2129,6 @@
                     + "; regranting permissions for internal storage");
             mSettings.mInternalSdkPlatform = mSdkVersion;
 
-            // For now runtime permissions are toggled via a system property.
-            if (!RUNTIME_PERMISSIONS_ENABLED) {
-                // Remove the runtime permissions state if the feature
-                // was disabled by flipping the system property.
-                mSettings.deleteRuntimePermissionsFiles();
-            }
-
             updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
                     | (regrantPermissions
                             ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
@@ -3149,13 +3140,9 @@
     }
 
     @Override
-    public boolean grantPermission(String packageName, String name, int userId) {
-        if (!RUNTIME_PERMISSIONS_ENABLED) {
-            return false;
-        }
-
+    public void grantPermission(String packageName, String name, int userId) {
         if (!sUserManager.exists(userId)) {
-            return false;
+            return;
         }
 
         mContext.enforceCallingOrSelfPermission(
@@ -3191,12 +3178,13 @@
             final int result = permissionsState.grantRuntimePermission(bp, userId);
             switch (result) {
                 case PermissionsState.PERMISSION_OPERATION_FAILURE: {
-                    return false;
+                    return;
                 }
 
                 case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
                     gidsChanged = true;
-                } break;
+                }
+                break;
             }
 
             // Not critical if that is lost - app has to request again.
@@ -3206,18 +3194,12 @@
         if (gidsChanged) {
             killSettingPackagesForUser(sb, userId, KILL_APP_REASON_GIDS_CHANGED);
         }
-
-        return true;
     }
 
     @Override
-    public boolean revokePermission(String packageName, String name, int userId) {
-        if (!RUNTIME_PERMISSIONS_ENABLED) {
-            return false;
-        }
-
+    public void revokePermission(String packageName, String name, int userId) {
         if (!sUserManager.exists(userId)) {
-            return false;
+            return;
         }
 
         mContext.enforceCallingOrSelfPermission(
@@ -3251,7 +3233,7 @@
 
             if (permissionsState.revokeRuntimePermission(bp, userId) ==
                     PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                return false;
+                return;
             }
 
             // Critical, after this call all should never have the permission.
@@ -3259,8 +3241,6 @@
         }
 
         killSettingPackagesForUser(sb, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
-
-        return true;
     }
 
     @Override
@@ -7604,9 +7584,7 @@
                 } break;
 
                 case PermissionInfo.PROTECTION_DANGEROUS: {
-                    if (!RUNTIME_PERMISSIONS_ENABLED
-                            || pkg.applicationInfo.targetSdkVersion
-                                    <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+                    if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
                         // For legacy apps dangerous permissions are install time ones.
                         grant = GRANT_INSTALL;
                     } else if (ps.isSystem()) {
@@ -7745,10 +7723,8 @@
         ps.setPermissionsUpdatedForUserIds(currentUserIds);
 
         // Persist the runtime permissions state for users with changes.
-        if (RUNTIME_PERMISSIONS_ENABLED) {
-            for (int userId : changedRuntimePermissionUserIds) {
-                mSettings.writeRuntimePermissionsForUserLPr(userId, true);
-            }
+        for (int userId : changedRuntimePermissionUserIds) {
+            mSettings.writeRuntimePermissionsForUserLPr(userId, true);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index d3bfdeb..d476bfde 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4165,19 +4165,11 @@
         }
 
         public void writePermissionsForUserSyncLPr(int userId) {
-            if (!PackageManagerService.RUNTIME_PERMISSIONS_ENABLED) {
-                return;
-            }
-
             mHandler.removeMessages(userId);
             writePermissionsSync(userId);
         }
 
         public void writePermissionsForUserAsyncLPr(int userId) {
-            if (!PackageManagerService.RUNTIME_PERMISSIONS_ENABLED) {
-                return;
-            }
-
             final long currentTimeMillis = SystemClock.uptimeMillis();
 
             if (mWriteScheduled.get(userId)) {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 7d2fb43..726db4e 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -227,7 +227,7 @@
             if (!userInfo.supportsSwitchTo()) continue;
             if (!mActivityManager.isUserRunning(userInfo.id)) continue;
             if (!lockPatternUtils.isSecure(userInfo.id)) continue;
-            if (!mUserHasAuthenticatedSinceBoot.get(userInfo.id)) continue;
+            if (!getUserHasAuthenticated(userInfo.id)) continue;
             DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager();
             int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id);
             final boolean disableTrustAgents =
@@ -506,7 +506,7 @@
     // Agent dispatch and aggregation
 
     private boolean aggregateIsTrusted(int userId) {
-        if (!mUserHasAuthenticatedSinceBoot.get(userId)) {
+        if (!getUserHasAuthenticated(userId)) {
             return false;
         }
         for (int i = 0; i < mActiveAgents.size(); i++) {
@@ -521,7 +521,7 @@
     }
 
     private boolean aggregateIsTrustManaged(int userId) {
-        if (!mUserHasAuthenticatedSinceBoot.get(userId)) {
+        if (!getUserHasAuthenticated(userId)) {
             return false;
         }
         for (int i = 0; i < mActiveAgents.size(); i++) {
@@ -549,21 +549,44 @@
     }
 
     private void updateUserHasAuthenticated(int userId) {
-        if (!mUserHasAuthenticatedSinceBoot.get(userId)) {
-            mUserHasAuthenticatedSinceBoot.put(userId, true);
+        boolean changed = setUserHasAuthenticated(userId);
+        if (changed) {
             refreshAgentList(userId);
         }
     }
 
+    private boolean getUserHasAuthenticated(int userId) {
+        synchronized (mUserHasAuthenticatedSinceBoot) {
+            return mUserHasAuthenticatedSinceBoot.get(userId);
+        }
+    }
+
+    /**
+     * @return whether the value has changed
+     */
+    private boolean setUserHasAuthenticated(int userId) {
+        synchronized (mUserHasAuthenticatedSinceBoot) {
+            if (!mUserHasAuthenticatedSinceBoot.get(userId)) {
+                mUserHasAuthenticatedSinceBoot.put(userId, true);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private void clearUserHasAuthenticated(int userId) {
+        synchronized (mUserHasAuthenticatedSinceBoot) {
+            if (userId == UserHandle.USER_ALL) {
+                mUserHasAuthenticatedSinceBoot.clear();
+            } else {
+                mUserHasAuthenticatedSinceBoot.put(userId, false);
+            }
+        }
+    }
 
     private void requireCredentialEntry(int userId) {
-        if (userId == UserHandle.USER_ALL) {
-            mUserHasAuthenticatedSinceBoot.clear();
-            refreshAgentList(UserHandle.USER_ALL);
-        } else {
-            mUserHasAuthenticatedSinceBoot.put(userId, false);
-            refreshAgentList(userId);
-        }
+        clearUserHasAuthenticated(userId);
+        refreshAgentList(userId);
     }
 
     // Listeners
@@ -705,6 +728,18 @@
             }
         }
 
+        @Override
+        public boolean hasUserAuthenticatedSinceBoot(int userId) throws RemoteException {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, null);
+            long token = Binder.clearCallingIdentity();
+            try {
+                return getUserHasAuthenticated(userId);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         private void enforceReportPermission() {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events");
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index b5d566a..fd0c06d 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -14,6 +14,7 @@
 
 package android.telecom;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -35,15 +36,27 @@
     private static final String TAG = "DefaultDialerManager";
 
     /**
-     * Sets the specified package name as the default dialer application. The caller of this method
-     * needs to have permission to write to secure settings.
+     * Sets the specified package name as the default dialer application for the current user.
+     * The caller of this method needs to have permission to write to secure settings and
+     * manage users on the device.
      *
      * @hide
      * */
     public static void setDefaultDialerApplication(Context context, String packageName) {
+        setDefaultDialerApplication(context, packageName, ActivityManager.getCurrentUser());
+    }
+
+    /**
+     * Sets the specified package name as the default dialer application for the specified user.
+     * The caller of this method needs to have permission to write to secure settings and
+     * manage users on the device.
+     *
+     * @hide
+     * */
+    public static void setDefaultDialerApplication(Context context, String packageName, int user) {
         // Get old package name
-        String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.DIALER_DEFAULT_APPLICATION);
+        String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
+                Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
 
         if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
             // No change
@@ -55,26 +68,44 @@
 
         if (packageNames.contains(packageName)) {
             // Update the secure setting.
-            Settings.Secure.putString(context.getContentResolver(),
-                    Settings.Secure.DIALER_DEFAULT_APPLICATION, packageName);
+            Settings.Secure.putStringForUser(context.getContentResolver(),
+                    Settings.Secure.DIALER_DEFAULT_APPLICATION, packageName, user);
         }
     }
 
     /**
-     * Returns the installed dialer application that will be used to receive incoming calls, and is
-     * allowed to make emergency calls.
+     * Returns the installed dialer application for the current user that will be used to receive
+     * incoming calls, and is allowed to make emergency calls.
      *
      * The application will be returned in order of preference:
      * 1) User selected phone application (if still installed)
      * 2) Pre-installed system dialer (if not disabled)
      * 3) Null
      *
+     * The caller of this method needs to have permission to manage users on the device.
+     *
      * @hide
      * */
     public static String getDefaultDialerApplication(Context context) {
-        String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.DIALER_DEFAULT_APPLICATION);
+        return getDefaultDialerApplication(context, ActivityManager.getCurrentUser());
+    }
 
+    /**
+     * Returns the installed dialer application for the specified user that will be used to receive
+     * incoming calls, and is allowed to make emergency calls.
+     *
+     * The application will be returned in order of preference:
+     * 1) User selected phone application (if still installed)
+     * 2) Pre-installed system dialer (if not disabled)
+     * 3) Null
+     *
+     * The caller of this method needs to have permission to manage users on the device.
+     *
+     * @hide
+     * */
+    public static String getDefaultDialerApplication(Context context, int user) {
+        String defaultPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
+                Settings.Secure.DIALER_DEFAULT_APPLICATION, user);
 
         final List<String> packageNames = getInstalledDialerApplications(context);
 
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 85a489b..f563839 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -317,6 +317,7 @@
     public static final String EXTRA_PLMN       = "plmn";
     public static final String EXTRA_SHOW_SPN   = "showSpn";
     public static final String EXTRA_SPN        = "spn";
+    public static final String EXTRA_DATA_SPN   = "spnData";
 
     /**
      * <p>Broadcast Action: It indicates one column of a subinfo record has been changed
diff --git a/tests/MemoryUsage/AndroidManifest.xml b/tests/MemoryUsage/AndroidManifest.xml
index 3932e5b..cd559c5 100644
--- a/tests/MemoryUsage/AndroidManifest.xml
+++ b/tests/MemoryUsage/AndroidManifest.xml
@@ -6,7 +6,9 @@
                      android:name="com.android.tests.memoryusage.MemoryUsageInstrumentation"
                      android:targetPackage="com.android.tests.memoryusage" />
 
+    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+
     <application android:label="Memory Usage Test">
         <uses-library android:name="android.test.runner" />
     </application>
-</manifest>
\ No newline at end of file
+</manifest>