Merge "TIF: fix setSurface() logic" into lmp-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index c9d7579..fb26daf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28691,7 +28691,7 @@
     method public java.lang.String getSubscriberId();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
-    method public int hasCarrierPrivileges();
+    method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
     method public boolean iccCloseLogicalChannel(int);
     method public byte[] iccExchangeSimIO(int, int, int, int, int, java.lang.String);
@@ -28711,10 +28711,6 @@
     field public static final int CALL_STATE_IDLE = 0; // 0x0
     field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
     field public static final int CALL_STATE_RINGING = 1; // 0x1
-    field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
-    field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
-    field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
-    field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
     field public static final int DATA_ACTIVITY_DORMANT = 4; // 0x4
     field public static final int DATA_ACTIVITY_IN = 1; // 0x1
     field public static final int DATA_ACTIVITY_INOUT = 3; // 0x3
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 948c9a2..bd34a9c 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -501,7 +501,7 @@
 
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
-            final ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null);
+            final ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null);
             copy(new FileInputStream(fd.getFileDescriptor()), System.out);
         }
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index c3028b7..6ec48e5 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1186,6 +1186,18 @@
             return true;
         }
 
+        case CHECK_PERMISSION_WITH_TOKEN_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String perm = data.readString();
+            int pid = data.readInt();
+            int uid = data.readInt();
+            IBinder token = data.readStrongBinder();
+            int res = checkPermissionWithToken(perm, pid, uid, token);
+            reply.writeNoException();
+            reply.writeInt(res);
+            return true;
+        }
+
         case CHECK_URI_PERMISSION_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             Uri uri = Uri.CREATOR.createFromParcel(data);
@@ -1193,7 +1205,8 @@
             int uid = data.readInt();
             int mode = data.readInt();
             int userId = data.readInt();
-            int res = checkUriPermission(uri, pid, uid, mode, userId);
+            IBinder callerToken = data.readStrongBinder();
+            int res = checkUriPermission(uri, pid, uid, mode, userId, callerToken);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -3742,7 +3755,7 @@
         mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
         reply.readException();
         IIntentSender res = IIntentSender.Stub.asInterface(
-            reply.readStrongBinder());
+                reply.readStrongBinder());
         data.recycle();
         reply.recycle();
         return res;
@@ -3851,6 +3864,22 @@
         reply.recycle();
         return res;
     }
+    public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(permission);
+        data.writeInt(pid);
+        data.writeInt(uid);
+        data.writeStrongBinder(callerToken);
+        mRemote.transact(CHECK_PERMISSION_WITH_TOKEN_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int res = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
     public boolean clearApplicationUserData(final String packageName,
             final IPackageDataObserver observer, final int userId) throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -3866,8 +3895,8 @@
         reply.recycle();
         return res;
     }
-    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
-            throws RemoteException {
+    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId,
+            IBinder callerToken) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
@@ -3876,6 +3905,7 @@
         data.writeInt(uid);
         data.writeInt(mode);
         data.writeInt(userId);
+        data.writeStrongBinder(callerToken);
         mRemote.transact(CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0);
         reply.readException();
         int res = reply.readInt();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7fafc38..1de9b47 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1863,6 +1863,21 @@
         }
     }
 
+    /** @hide */
+    @Override
+    public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+        if (permission == null) {
+            throw new IllegalArgumentException("permission is null");
+        }
+
+        try {
+            return ActivityManagerNative.getDefault().checkPermissionWithToken(
+                    permission, pid, uid, callerToken);
+        } catch (RemoteException e) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
     @Override
     public int checkCallingPermission(String permission) {
         if (permission == null) {
@@ -1951,7 +1966,19 @@
         try {
             return ActivityManagerNative.getDefault().checkUriPermission(
                     ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags,
-                    resolveUserId(uri));
+                    resolveUserId(uri), null);
+        } catch (RemoteException e) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
+    /** @hide */
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+        try {
+            return ActivityManagerNative.getDefault().checkUriPermission(
+                    ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags,
+                    resolveUserId(uri), callerToken);
         } catch (RemoteException e) {
             return PackageManager.PERMISSION_DENIED;
         }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 6433f3f..5362303 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -219,9 +219,11 @@
 
     public int checkPermission(String permission, int pid, int uid)
             throws RemoteException;
-
-    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId)
+    public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken)
             throws RemoteException;
+
+    public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId,
+            IBinder callerToken) throws RemoteException;
     public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri,
             int mode, int userId) throws RemoteException;
     public void revokeUriPermission(IApplicationThread caller, Uri uri, int mode, int userId)
@@ -785,4 +787,5 @@
     int GET_TASK_DESCRIPTION_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+238;
     int LAUNCH_ASSIST_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+239;
     int START_IN_PLACE_ANIMATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+240;
+    int CHECK_PERMISSION_WITH_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+241;
 }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4c82efd..360f308 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -31,6 +31,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.OperationCanceledException;
 import android.os.ParcelFileDescriptor;
@@ -201,7 +202,7 @@
                 ICancellationSignal cancellationSignal) {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
                         CancellationSignal.fromTransport(cancellationSignal));
             }
@@ -227,7 +228,7 @@
             validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return rejectInsert(uri, initialValues);
             }
             final String original = setCallingPackage(callingPkg);
@@ -242,7 +243,7 @@
         public int bulkInsert(String callingPkg, Uri uri, ContentValues[] initialValues) {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
             final String original = setCallingPackage(callingPkg);
@@ -270,13 +271,13 @@
                     operations.set(i, operation);
                 }
                 if (operation.isReadOperation()) {
-                    if (enforceReadPermission(callingPkg, uri)
+                    if (enforceReadPermission(callingPkg, uri, null)
                             != AppOpsManager.MODE_ALLOWED) {
                         throw new OperationApplicationException("App op not allowed", 0);
                     }
                 }
                 if (operation.isWriteOperation()) {
-                    if (enforceWritePermission(callingPkg, uri)
+                    if (enforceWritePermission(callingPkg, uri, null)
                             != AppOpsManager.MODE_ALLOWED) {
                         throw new OperationApplicationException("App op not allowed", 0);
                     }
@@ -301,7 +302,7 @@
         public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
             final String original = setCallingPackage(callingPkg);
@@ -317,7 +318,7 @@
                 String[] selectionArgs) {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceWritePermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return 0;
             }
             final String original = setCallingPackage(callingPkg);
@@ -330,11 +331,11 @@
 
         @Override
         public ParcelFileDescriptor openFile(
-                String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
-                throws FileNotFoundException {
+                String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal,
+                IBinder callerToken) throws FileNotFoundException {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            enforceFilePermission(callingPkg, uri, mode);
+            enforceFilePermission(callingPkg, uri, mode, callerToken);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openFile(
@@ -350,7 +351,7 @@
                 throws FileNotFoundException {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            enforceFilePermission(callingPkg, uri, mode);
+            enforceFilePermission(callingPkg, uri, mode, null);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openAssetFile(
@@ -382,7 +383,7 @@
                 Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
             validateIncomingUri(uri);
             uri = getUriWithoutUserId(uri);
-            enforceFilePermission(callingPkg, uri, "r");
+            enforceFilePermission(callingPkg, uri, "r", null);
             final String original = setCallingPackage(callingPkg);
             try {
                 return ContentProvider.this.openTypedAssetFile(
@@ -402,7 +403,7 @@
             validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
             final String original = setCallingPackage(callingPkg);
@@ -418,7 +419,7 @@
             validateIncomingUri(uri);
             int userId = getUserIdFromUri(uri);
             uri = getUriWithoutUserId(uri);
-            if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+            if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                 return null;
             }
             final String original = setCallingPackage(callingPkg);
@@ -429,29 +430,33 @@
             }
         }
 
-        private void enforceFilePermission(String callingPkg, Uri uri, String mode)
-                throws FileNotFoundException, SecurityException {
+        private void enforceFilePermission(String callingPkg, Uri uri, String mode,
+                IBinder callerToken) throws FileNotFoundException, SecurityException {
             if (mode != null && mode.indexOf('w') != -1) {
-                if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+                if (enforceWritePermission(callingPkg, uri, callerToken)
+                        != AppOpsManager.MODE_ALLOWED) {
                     throw new FileNotFoundException("App op not allowed");
                 }
             } else {
-                if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+                if (enforceReadPermission(callingPkg, uri, callerToken)
+                        != AppOpsManager.MODE_ALLOWED) {
                     throw new FileNotFoundException("App op not allowed");
                 }
             }
         }
 
-        private int enforceReadPermission(String callingPkg, Uri uri) throws SecurityException {
-            enforceReadPermissionInner(uri);
+        private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken)
+                throws SecurityException {
+            enforceReadPermissionInner(uri, callerToken);
             if (mReadOp != AppOpsManager.OP_NONE) {
                 return mAppOpsManager.noteOp(mReadOp, Binder.getCallingUid(), callingPkg);
             }
             return AppOpsManager.MODE_ALLOWED;
         }
 
-        private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException {
-            enforceWritePermissionInner(uri);
+        private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken)
+                throws SecurityException {
+            enforceWritePermissionInner(uri, callerToken);
             if (mWriteOp != AppOpsManager.OP_NONE) {
                 return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg);
             }
@@ -467,7 +472,8 @@
     }
 
     /** {@hide} */
-    protected void enforceReadPermissionInner(Uri uri) throws SecurityException {
+    protected void enforceReadPermissionInner(Uri uri, IBinder callerToken)
+            throws SecurityException {
         final Context context = getContext();
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
@@ -480,7 +486,8 @@
         if (mExported && checkUser(pid, uid, context)) {
             final String componentPerm = getReadPermission();
             if (componentPerm != null) {
-                if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
+                if (context.checkPermission(componentPerm, pid, uid, callerToken)
+                        == PERMISSION_GRANTED) {
                     return;
                 } else {
                     missingPerm = componentPerm;
@@ -497,7 +504,8 @@
                 for (PathPermission pp : pps) {
                     final String pathPerm = pp.getReadPermission();
                     if (pathPerm != null && pp.match(path)) {
-                        if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
+                        if (context.checkPermission(pathPerm, pid, uid, callerToken)
+                                == PERMISSION_GRANTED) {
                             return;
                         } else {
                             // any denied <path-permission> means we lose
@@ -518,8 +526,8 @@
         final int callingUserId = UserHandle.getUserId(uid);
         final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid))
                 ? maybeAddUserId(uri, callingUserId) : uri;
-        if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                == PERMISSION_GRANTED) {
+        if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                callerToken) == PERMISSION_GRANTED) {
             return;
         }
 
@@ -532,7 +540,8 @@
     }
 
     /** {@hide} */
-    protected void enforceWritePermissionInner(Uri uri) throws SecurityException {
+    protected void enforceWritePermissionInner(Uri uri, IBinder callerToken)
+            throws SecurityException {
         final Context context = getContext();
         final int pid = Binder.getCallingPid();
         final int uid = Binder.getCallingUid();
@@ -545,7 +554,8 @@
         if (mExported && checkUser(pid, uid, context)) {
             final String componentPerm = getWritePermission();
             if (componentPerm != null) {
-                if (context.checkPermission(componentPerm, pid, uid) == PERMISSION_GRANTED) {
+                if (context.checkPermission(componentPerm, pid, uid, callerToken)
+                        == PERMISSION_GRANTED) {
                     return;
                 } else {
                     missingPerm = componentPerm;
@@ -562,7 +572,8 @@
                 for (PathPermission pp : pps) {
                     final String pathPerm = pp.getWritePermission();
                     if (pathPerm != null && pp.match(path)) {
-                        if (context.checkPermission(pathPerm, pid, uid) == PERMISSION_GRANTED) {
+                        if (context.checkPermission(pathPerm, pid, uid, callerToken)
+                                == PERMISSION_GRANTED) {
                             return;
                         } else {
                             // any denied <path-permission> means we lose
@@ -580,8 +591,8 @@
         }
 
         // last chance, check against any uri grants
-        if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
-                == PERMISSION_GRANTED) {
+        if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                callerToken) == PERMISSION_GRANTED) {
             return;
         }
 
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index cefc27f..e15ac94 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -288,7 +288,7 @@
                 remoteSignal = mContentProvider.createCancellationSignal();
                 signal.setRemote(remoteSignal);
             }
-            return mContentProvider.openFile(mPackageName, url, mode, remoteSignal);
+            return mContentProvider.openFile(mPackageName, url, mode, remoteSignal, null);
         } catch (DeadObjectException e) {
             if (!mStable) {
                 mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 39286d6..f2e7fc4 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -234,9 +234,10 @@
                     String mode = data.readString();
                     ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
                             data.readStrongBinder());
+                    IBinder callerToken = data.readStrongBinder();
 
                     ParcelFileDescriptor fd;
-                    fd = openFile(callingPkg, url, mode, signal);
+                    fd = openFile(callingPkg, url, mode, signal, callerToken);
                     reply.writeNoException();
                     if (fd != null) {
                         reply.writeInt(1);
@@ -575,7 +576,7 @@
 
     @Override
     public ParcelFileDescriptor openFile(
-            String callingPkg, Uri url, String mode, ICancellationSignal signal)
+            String callingPkg, Uri url, String mode, ICancellationSignal signal, IBinder token)
             throws RemoteException, FileNotFoundException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -586,6 +587,7 @@
             url.writeToParcel(data, 0);
             data.writeString(mode);
             data.writeStrongBinder(signal != null ? signal.asBinder() : null);
+            data.writeStrongBinder(token);
 
             mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c9b7d0a..a73ba74 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -37,6 +37,7 @@
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.StatFs;
 import android.os.UserHandle;
@@ -2864,10 +2865,10 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
-     * android.app.UsageStatsManager} for interacting with the status bar.
+     * android.app.usage.UsageStatsManager} for interacting with the status bar.
      *
      * @see #getSystemService
-     * @see android.app.UsageStatsManager
+     * @see android.app.usage.UsageStatsManager
      * @hide
      */
     public static final String USAGE_STATS_SERVICE = "usagestats";
@@ -2921,6 +2922,11 @@
     @PackageManager.PermissionResult
     public abstract int checkPermission(@NonNull String permission, int pid, int uid);
 
+    /** @hide */
+    @PackageManager.PermissionResult
+    public abstract int checkPermission(@NonNull String permission, int pid, int uid,
+            IBinder callerToken);
+
     /**
      * Determine whether the calling process of an IPC you are handling has been
      * granted a particular permission.  This is basically the same as calling
@@ -3108,6 +3114,10 @@
     public abstract int checkUriPermission(Uri uri, int pid, int uid,
             @Intent.AccessUriMode int modeFlags);
 
+    /** @hide */
+    public abstract int checkUriPermission(Uri uri, int pid, int uid,
+            @Intent.AccessUriMode int modeFlags, IBinder callerToken);
+
     /**
      * Determine whether the calling process and user ID has been
      * granted permission to access a specific URI.  This is basically
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index ad7c350..cfae1cf 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.view.DisplayAdjustments;
@@ -566,6 +567,12 @@
         return mBase.checkPermission(permission, pid, uid);
     }
 
+    /** @hide */
+    @Override
+    public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+        return mBase.checkPermission(permission, pid, uid, callerToken);
+    }
+
     @Override
     public int checkCallingPermission(String permission) {
         return mBase.checkCallingPermission(permission);
@@ -608,6 +615,12 @@
         return mBase.checkUriPermission(uri, pid, uid, modeFlags);
     }
 
+    /** @hide */
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+        return mBase.checkUriPermission(uri, pid, uid, modeFlags, callerToken);
+    }
+
     @Override
     public int checkCallingUriPermission(Uri uri, int modeFlags) {
         return mBase.checkCallingUriPermission(uri, modeFlags);
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index f92a404..f858406 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -47,7 +47,8 @@
     public int update(String callingPkg, Uri url, ContentValues values, String selection,
             String[] selectionArgs) throws RemoteException;
     public ParcelFileDescriptor openFile(
-            String callingPkg, Uri url, String mode, ICancellationSignal signal)
+            String callingPkg, Uri url, String mode, ICancellationSignal signal,
+            IBinder callerToken)
             throws RemoteException, FileNotFoundException;
     public AssetFileDescriptor openAssetFile(
             String callingPkg, Uri url, String mode, ICancellationSignal signal)
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 5b926ad..961a3f4 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -60,4 +60,6 @@
 
     void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
     void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
+
+    void verifyNfcPermission();
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index dd9765d..d009295 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -254,7 +254,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -268,7 +272,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -281,7 +289,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -295,7 +307,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -308,7 +324,11 @@
             isResumed = state.resumed;
         }
         if (isResumed) {
+            // requestNfcServiceCallback() verifies permission also
             requestNfcServiceCallback();
+        } else {
+            // Crash API calls early in case NFC permission is missing
+            verifyNfcPermission();
         }
     }
 
@@ -324,6 +344,14 @@
         }
     }
 
+    void verifyNfcPermission() {
+        try {
+            NfcAdapter.sService.verifyNfcPermission();
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
     /** Callback from NFC service, usually on binder thread */
     @Override
     public BeamShareData createBeamShareData() {
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 270d786..4135e8b 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -637,7 +637,7 @@
         final Bundle out = new Bundle();
         try {
             if (METHOD_CREATE_DOCUMENT.equals(method)) {
-                enforceWritePermissionInner(documentUri);
+                enforceWritePermissionInner(documentUri, null);
 
                 final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
                 final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -651,7 +651,7 @@
                 out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
 
             } else if (METHOD_RENAME_DOCUMENT.equals(method)) {
-                enforceWritePermissionInner(documentUri);
+                enforceWritePermissionInner(documentUri, null);
 
                 final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
                 final String newDocumentId = renameDocument(documentId, displayName);
@@ -675,7 +675,7 @@
                 }
 
             } else if (METHOD_DELETE_DOCUMENT.equals(method)) {
-                enforceWritePermissionInner(documentUri);
+                enforceWritePermissionInner(documentUri, null);
                 deleteDocument(documentId);
 
                 // Document no longer exists, clean up any grants
diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java
index a159b40..9749121 100644
--- a/core/java/android/transition/ChangeTransform.java
+++ b/core/java/android/transition/ChangeTransform.java
@@ -376,7 +376,7 @@
         while (outerTransition.mParent != null) {
             outerTransition = outerTransition.mParent;
         }
-        GhostListener listener = new GhostListener(view, ghostView, endMatrix);
+        GhostListener listener = new GhostListener(view, startValues.view, ghostView);
         outerTransition.addListener(listener);
 
         if (startValues.view != endValues.view) {
@@ -466,13 +466,13 @@
 
     private static class GhostListener extends Transition.TransitionListenerAdapter {
         private View mView;
+        private View mStartView;
         private GhostView mGhostView;
-	private Matrix mEndMatrix;
 
-        public GhostListener(View view, GhostView ghostView, Matrix endMatrix) {
+        public GhostListener(View view, View startView, GhostView ghostView) {
             mView = view;
+            mStartView = startView;
             mGhostView = ghostView;
-            mEndMatrix = endMatrix;
         }
 
         @Override
@@ -481,6 +481,7 @@
             GhostView.removeGhost(mView);
             mView.setTagInternal(R.id.transitionTransform, null);
             mView.setTagInternal(R.id.parentMatrix, null);
+            mStartView.setTransitionAlpha(1);
         }
 
         @Override
diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java
index 623cdd1..ad6c2dd 100644
--- a/core/java/android/transition/SidePropagation.java
+++ b/core/java/android/transition/SidePropagation.java
@@ -44,8 +44,8 @@
      * farther from the edge. The default is {@link Gravity#BOTTOM}.
      *
      * @param side The side that is used to calculate the transition propagation. Must be one of
-     *             {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT}, or
-     *             {@link Gravity#BOTTOM}.
+     *             {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT},
+     *             {@link Gravity#BOTTOM}, {@link Gravity#START}, or {@link Gravity#END}.
      */
     public void setSide(int side) {
         mSide = side;
@@ -106,7 +106,7 @@
             epicenterY = (top + bottom) / 2;
         }
 
-        float distance = distance(viewCenterX, viewCenterY, epicenterX, epicenterY,
+        float distance = distance(sceneRoot, viewCenterX, viewCenterY, epicenterX, epicenterY,
                 left, top, right, bottom);
         float maxDistance = getMaxDistance(sceneRoot);
         float distanceFraction = distance/maxDistance;
@@ -119,10 +119,20 @@
         return Math.round(duration * directionMultiplier / mPropagationSpeed * distanceFraction);
     }
 
-    private int distance(int viewX, int viewY, int epicenterX, int epicenterY,
+    private int distance(View sceneRoot, int viewX, int viewY, int epicenterX, int epicenterY,
             int left, int top, int right, int bottom) {
+        final int side;
+        if (mSide == Gravity.START) {
+            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            side = isRtl ? Gravity.RIGHT : Gravity.LEFT;
+        } else if (mSide == Gravity.END) {
+            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            side = isRtl ? Gravity.LEFT : Gravity.RIGHT;
+        } else {
+            side = mSide;
+        }
         int distance = 0;
-        switch (mSide) {
+        switch (side) {
             case Gravity.LEFT:
                 distance = right - viewX + Math.abs(epicenterY - viewY);
                 break;
@@ -143,6 +153,8 @@
         switch (mSide) {
             case Gravity.LEFT:
             case Gravity.RIGHT:
+            case Gravity.START:
+            case Gravity.END:
                 return sceneRoot.getWidth();
             default:
                 return sceneRoot.getHeight();
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index ae2e4aa..be1d907 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -76,6 +76,20 @@
         }
     };
 
+    private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
+        @Override
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            final float x;
+            if (isRtl) {
+                x = view.getTranslationX() + sceneRoot.getWidth();
+            } else {
+                x = view.getTranslationX() - sceneRoot.getWidth();
+            }
+            return x;
+        }
+    };
+
     private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
         @Override
         public float getGoneY(ViewGroup sceneRoot, View view) {
@@ -90,6 +104,20 @@
         }
     };
 
+    private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
+        @Override
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+            final float x;
+            if (isRtl) {
+                x = view.getTranslationX() - sceneRoot.getWidth();
+            } else {
+                x = view.getTranslationX() + sceneRoot.getWidth();
+            }
+            return x;
+        }
+    };
+
     private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
         @Override
         public float getGoneY(ViewGroup sceneRoot, View view) {
@@ -144,7 +172,8 @@
      *
      * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
      *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
-     *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
+     *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
+     *                  {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
      * @attr ref android.R.styleable#Slide_slideEdge
      */
     public void setSlideEdge(int slideEdge) {
@@ -161,6 +190,12 @@
             case Gravity.BOTTOM:
                 mSlideCalculator = sCalculateBottom;
                 break;
+            case Gravity.START:
+                mSlideCalculator = sCalculateStart;
+                break;
+            case Gravity.END:
+                mSlideCalculator = sCalculateEnd;
+                break;
             default:
                 throw new IllegalArgumentException("Invalid slide direction");
         }
@@ -175,7 +210,8 @@
      *
      * @return the edge of the scene to use for Views appearing and disappearing. One of
      *         {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
-     *         {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
+     *         {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
+     *         {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
      * @attr ref android.R.styleable#Slide_slideEdge
      */
     public int getSlideEdge() {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 5579c13..2c8a499 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -274,7 +274,7 @@
     }
 
     private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");
         updateViewTreeDisplayList(view);
 
         if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1d19c9b..b54d462 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -56,6 +56,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -14405,143 +14406,158 @@
     public void buildDrawingCache(boolean autoScale) {
         if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
                 mDrawingCache == null : mUnscaledDrawingCache == null)) {
-            mCachingFailed = false;
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+                        "buildDrawingCache/SW Layer for " + getClass().getSimpleName());
+            }
+            try {
+                buildDrawingCacheImpl(autoScale);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+            }
+        }
+    }
 
-            int width = mRight - mLeft;
-            int height = mBottom - mTop;
+    /**
+     * private, internal implementation of buildDrawingCache, used to enable tracing
+     */
+    private void buildDrawingCacheImpl(boolean autoScale) {
+        mCachingFailed = false;
 
-            final AttachInfo attachInfo = mAttachInfo;
-            final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
+        int width = mRight - mLeft;
+        int height = mBottom - mTop;
 
-            if (autoScale && scalingRequired) {
-                width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
-                height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
+        final AttachInfo attachInfo = mAttachInfo;
+        final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
+
+        if (autoScale && scalingRequired) {
+            width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
+            height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
+        }
+
+        final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
+        final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
+        final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
+
+        final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
+        final long drawingCacheSize =
+                ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
+        if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
+            if (width > 0 && height > 0) {
+                Log.w(VIEW_LOG_TAG, "View too large to fit into drawing cache, needs "
+                        + projectedBitmapSize + " bytes, only "
+                        + drawingCacheSize + " available");
+            }
+            destroyDrawingCache();
+            mCachingFailed = true;
+            return;
+        }
+
+        boolean clear = true;
+        Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
+
+        if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
+            Bitmap.Config quality;
+            if (!opaque) {
+                // Never pick ARGB_4444 because it looks awful
+                // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
+                switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
+                    case DRAWING_CACHE_QUALITY_AUTO:
+                    case DRAWING_CACHE_QUALITY_LOW:
+                    case DRAWING_CACHE_QUALITY_HIGH:
+                    default:
+                        quality = Bitmap.Config.ARGB_8888;
+                        break;
+                }
+            } else {
+                // Optimization for translucent windows
+                // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()
+                quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
             }
 
-            final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
-            final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
-            final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
+            // Try to cleanup memory
+            if (bitmap != null) bitmap.recycle();
 
-            final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
-            final long drawingCacheSize =
-                    ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
-            if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
-                if (width > 0 && height > 0) {
-                    Log.w(VIEW_LOG_TAG, "View too large to fit into drawing cache, needs "
-                            + projectedBitmapSize + " bytes, only "
-                            + drawingCacheSize + " available");
+            try {
+                bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
+                        width, height, quality);
+                bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
+                if (autoScale) {
+                    mDrawingCache = bitmap;
+                } else {
+                    mUnscaledDrawingCache = bitmap;
                 }
-                destroyDrawingCache();
+                if (opaque && use32BitCache) bitmap.setHasAlpha(false);
+            } catch (OutOfMemoryError e) {
+                // If there is not enough memory to create the bitmap cache, just
+                // ignore the issue as bitmap caches are not required to draw the
+                // view hierarchy
+                if (autoScale) {
+                    mDrawingCache = null;
+                } else {
+                    mUnscaledDrawingCache = null;
+                }
                 mCachingFailed = true;
                 return;
             }
 
-            boolean clear = true;
-            Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
+            clear = drawingCacheBackgroundColor != 0;
+        }
 
-            if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
-                Bitmap.Config quality;
-                if (!opaque) {
-                    // Never pick ARGB_4444 because it looks awful
-                    // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
-                    switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
-                        case DRAWING_CACHE_QUALITY_AUTO:
-                        case DRAWING_CACHE_QUALITY_LOW:
-                        case DRAWING_CACHE_QUALITY_HIGH:
-                        default:
-                            quality = Bitmap.Config.ARGB_8888;
-                            break;
-                    }
-                } else {
-                    // Optimization for translucent windows
-                    // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()
-                    quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
-                }
-
-                // Try to cleanup memory
-                if (bitmap != null) bitmap.recycle();
-
-                try {
-                    bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
-                            width, height, quality);
-                    bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
-                    if (autoScale) {
-                        mDrawingCache = bitmap;
-                    } else {
-                        mUnscaledDrawingCache = bitmap;
-                    }
-                    if (opaque && use32BitCache) bitmap.setHasAlpha(false);
-                } catch (OutOfMemoryError e) {
-                    // If there is not enough memory to create the bitmap cache, just
-                    // ignore the issue as bitmap caches are not required to draw the
-                    // view hierarchy
-                    if (autoScale) {
-                        mDrawingCache = null;
-                    } else {
-                        mUnscaledDrawingCache = null;
-                    }
-                    mCachingFailed = true;
-                    return;
-                }
-
-                clear = drawingCacheBackgroundColor != 0;
+        Canvas canvas;
+        if (attachInfo != null) {
+            canvas = attachInfo.mCanvas;
+            if (canvas == null) {
+                canvas = new Canvas();
             }
+            canvas.setBitmap(bitmap);
+            // Temporarily clobber the cached Canvas in case one of our children
+            // is also using a drawing cache. Without this, the children would
+            // steal the canvas by attaching their own bitmap to it and bad, bad
+            // thing would happen (invisible views, corrupted drawings, etc.)
+            attachInfo.mCanvas = null;
+        } else {
+            // This case should hopefully never or seldom happen
+            canvas = new Canvas(bitmap);
+        }
 
-            Canvas canvas;
-            if (attachInfo != null) {
-                canvas = attachInfo.mCanvas;
-                if (canvas == null) {
-                    canvas = new Canvas();
-                }
-                canvas.setBitmap(bitmap);
-                // Temporarily clobber the cached Canvas in case one of our children
-                // is also using a drawing cache. Without this, the children would
-                // steal the canvas by attaching their own bitmap to it and bad, bad
-                // thing would happen (invisible views, corrupted drawings, etc.)
-                attachInfo.mCanvas = null;
-            } else {
-                // This case should hopefully never or seldom happen
-                canvas = new Canvas(bitmap);
+        if (clear) {
+            bitmap.eraseColor(drawingCacheBackgroundColor);
+        }
+
+        computeScroll();
+        final int restoreCount = canvas.save();
+
+        if (autoScale && scalingRequired) {
+            final float scale = attachInfo.mApplicationScale;
+            canvas.scale(scale, scale);
+        }
+
+        canvas.translate(-mScrollX, -mScrollY);
+
+        mPrivateFlags |= PFLAG_DRAWN;
+        if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
+                mLayerType != LAYER_TYPE_NONE) {
+            mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
+        }
+
+        // Fast path for layouts with no backgrounds
+        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
+            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
+            dispatchDraw(canvas);
+            if (mOverlay != null && !mOverlay.isEmpty()) {
+                mOverlay.getOverlayView().draw(canvas);
             }
+        } else {
+            draw(canvas);
+        }
 
-            if (clear) {
-                bitmap.eraseColor(drawingCacheBackgroundColor);
-            }
+        canvas.restoreToCount(restoreCount);
+        canvas.setBitmap(null);
 
-            computeScroll();
-            final int restoreCount = canvas.save();
-
-            if (autoScale && scalingRequired) {
-                final float scale = attachInfo.mApplicationScale;
-                canvas.scale(scale, scale);
-            }
-
-            canvas.translate(-mScrollX, -mScrollY);
-
-            mPrivateFlags |= PFLAG_DRAWN;
-            if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
-                    mLayerType != LAYER_TYPE_NONE) {
-                mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
-            }
-
-            // Fast path for layouts with no backgrounds
-            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
-                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
-                dispatchDraw(canvas);
-                if (mOverlay != null && !mOverlay.isEmpty()) {
-                    mOverlay.getOverlayView().draw(canvas);
-                }
-            } else {
-                draw(canvas);
-            }
-
-            canvas.restoreToCount(restoreCount);
-            canvas.setBitmap(null);
-
-            if (attachInfo != null) {
-                // Restore the cached Canvas for our siblings
-                attachInfo.mCanvas = canvas;
-            }
+        if (attachInfo != null) {
+            // Restore the cached Canvas for our siblings
+            attachInfo.mCanvas = canvas;
         }
     }
 
@@ -15470,10 +15486,10 @@
                 && mAttachInfo.mHardwareRenderer != null) {
             mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
 
-            final RenderNode displayList = mBackgroundRenderNode;
-            if (displayList != null && displayList.isValid()) {
-                setBackgroundDisplayListProperties(displayList);
-                ((HardwareCanvas) canvas).drawRenderNode(displayList);
+            final RenderNode renderNode = mBackgroundRenderNode;
+            if (renderNode != null && renderNode.isValid()) {
+                setBackgroundRenderNodeProperties(renderNode);
+                ((HardwareCanvas) canvas).drawRenderNode(renderNode);
                 return;
             }
         }
@@ -15489,14 +15505,9 @@
         }
     }
 
-    /**
-     * Set up background drawable display list properties.
-     *
-     * @param displayList Valid display list for the background drawable
-     */
-    private void setBackgroundDisplayListProperties(RenderNode displayList) {
-        displayList.setTranslationX(mScrollX);
-        displayList.setTranslationY(mScrollY);
+    private void setBackgroundRenderNodeProperties(RenderNode renderNode) {
+        renderNode.setTranslationX(mScrollX);
+        renderNode.setTranslationY(mScrollY);
     }
 
     /**
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 674d7b8..3fdcaf7 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -28,6 +28,7 @@
         android:layout_height="@dimen/notification_large_icon_height"
         />
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="top"
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index ef916ed1..935424a 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -28,6 +28,7 @@
         android:layout_height="@dimen/notification_large_icon_height"
         />
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 3415814..d0c10b2 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -28,6 +28,7 @@
         android:layout_height="@dimen/notification_large_icon_height"
         />
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 8a66c3f..ac448ee 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -28,6 +28,7 @@
         android:layout_height="@dimen/notification_large_icon_height"
         />
     <LinearLayout
+        android:id="@+id/notification_main_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="top"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 403fd99..2fd7d30 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5665,6 +5665,10 @@
             <enum name="right" value="0x05" />
             <!-- Slide to and from the bottom edge of the Scene. -->
             <enum name="bottom" value="0x50" />
+            <!-- Slide to and from the x-axis position at the start of the Scene root. -->
+            <enum name="start" value="0x00800003"/>
+            <!-- Slide to and from the x-axis position at the end of the Scene root. -->
+            <enum name="end" value="0x00800005"/>
         </attr>
     </declare-styleable>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cbdb256..6e635f31 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1942,4 +1942,8 @@
     <string-array translatable="false" name="config_sms_convert_destination_number_support">
         <item>false</item>
     </string-array>
+
+    <!-- The maximum bitmap size that can be written to a MediaMetadata object. This value
+         is the max width/height allowed in dips.-->
+    <dimen name="config_mediaMetadataBitmapMaxSize">320dp</dimen>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6135466..cbc379b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -202,6 +202,7 @@
   <java-symbol type="id" name="status_bar_latest_event_content" />
   <java-symbol type="id" name="action_divider" />
   <java-symbol type="id" name="overflow_divider" />
+  <java-symbol type="id" name="notification_main_column" />
   <java-symbol type="id" name="sms_short_code_confirm_message" />
   <java-symbol type="id" name="sms_short_code_detail_layout" />
   <java-symbol type="id" name="sms_short_code_detail_message" />
@@ -1809,6 +1810,8 @@
   <java-symbol type="color" name="notification_progress_background_color" />
   <java-symbol type="id" name="media_actions" />
 
+  <java-symbol type="dimen" name="config_mediaMetadataBitmapMaxSize" />
+
     <!-- From SystemUI -->
   <java-symbol type="anim" name="push_down_in" />
   <java-symbol type="anim" name="push_down_out" />
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index b95636b..9aa29ca 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,11 +20,18 @@
 
 #include "Caches.h"
 #include "DeferredDisplayList.h"
-#include "RenderState.h"
 #include "Layer.h"
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
 #include "RenderNode.h"
+#include "RenderState.h"
+#include "utils/TraceUtils.h"
+
+#define ATRACE_LAYER_WORK(label) \
+    ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \
+            label, \
+            (renderNode.get() != NULL) ? renderNode->getName() : "", \
+            getWidth(), getHeight())
 
 namespace android {
 namespace uirenderer {
@@ -223,6 +230,8 @@
 }
 
 void Layer::defer(const OpenGLRenderer& rootRenderer) {
+    ATRACE_LAYER_WORK("Optimize");
+
     updateLightPosFromRenderer(rootRenderer);
     const float width = layer.getWidth();
     const float height = layer.getHeight();
@@ -260,6 +269,9 @@
 void Layer::flush() {
     // renderer is checked as layer may be destroyed/put in layer cache with flush scheduled
     if (deferredList && renderer) {
+        ATRACE_LAYER_WORK("Issue");
+        renderer->startMark((renderNode.get() != NULL) ? renderNode->getName() : "Layer");
+
         renderer->setViewport(layer.getWidth(), layer.getHeight());
         renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
                 !isBlend());
@@ -270,10 +282,14 @@
 
         dirtyRect.setEmpty();
         renderNode = NULL;
+
+        renderer->endMark();
     }
 }
 
 void Layer::render(const OpenGLRenderer& rootRenderer) {
+    ATRACE_LAYER_WORK("Direct-Issue");
+
     updateLightPosFromRenderer(rootRenderer);
     renderer->setViewport(layer.getWidth(), layer.getHeight());
     renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 394c647..83f9c6a 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -27,6 +27,7 @@
 #include "Matrix.h"
 #include "Properties.h"
 #include "Rect.h"
+#include "utils/TraceUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -185,7 +186,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) {
-    ATRACE_CALL();
+    ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height);
     LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
 
     Caches& caches = Caches::getInstance();
@@ -310,7 +311,7 @@
 
 void LayerRenderer::destroyLayer(Layer* layer) {
     if (layer) {
-        ATRACE_CALL();
+        ATRACE_FORMAT("Destroy %ux%u HW Layer", layer->getWidth(), layer->getHeight());
         LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d",
                 layer->getWidth(), layer->getHeight(), layer->getFbo());
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index d570b0d..80b4c2a 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -42,6 +42,7 @@
 #include "ShadowTessellator.h"
 #include "SkiaShader.h"
 #include "utils/GLUtils.h"
+#include "utils/TraceUtils.h"
 #include "Vector.h"
 #include "VertexBuffer.h"
 
@@ -51,21 +52,6 @@
     #define EVENT_LOGD(...)
 #endif
 
-static void atraceFormatBegin(const char* fmt, ...) {
-    const int BUFFER_SIZE = 256;
-    va_list ap;
-    char buf[BUFFER_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(buf, BUFFER_SIZE, fmt, ap);
-    va_end(ap);
-
-    ATRACE_BEGIN(buf);
-}
-
-#define ATRACE_FORMAT_BEGIN(fmt, ...) \
-    if (CC_UNLIKELY(ATRACE_ENABLED())) atraceFormatBegin(fmt, ##__VA_ARGS__)
-
 namespace android {
 namespace uirenderer {
 
@@ -466,8 +452,6 @@
 bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
     if (layer->deferredUpdateScheduled && layer->renderer
             && layer->renderNode.get() && layer->renderNode->isRenderable()) {
-        ATRACE_CALL();
-
         Rect& dirty = layer->dirtyRect;
 
         if (inFrame) {
@@ -525,20 +509,10 @@
     int count = mLayerUpdates.size();
     if (count > 0) {
         startMark("Apply Layer Updates");
-        char layerName[12];
 
         // Note: it is very important to update the layers in order
         for (int i = 0; i < count; i++) {
-            Layer* layer = mLayerUpdates.itemAt(i).get();
-
-            sprintf(layerName, "Layer #%d", i);
-            startMark(layerName);
-            ATRACE_FORMAT_BEGIN("flushLayer %ux%u", layer->getWidth(), layer->getHeight());
-
-            layer->flush();
-
-            ATRACE_END();
-            endMark();
+            mLayerUpdates.itemAt(i)->flush();
         }
 
         mLayerUpdates.clear();
@@ -575,7 +549,7 @@
 }
 
 void OpenGLRenderer::flushLayerUpdates() {
-    ATRACE_CALL();
+    ATRACE_NAME("Update HW Layers");
     syncState();
     updateLayers();
     flushLayers();
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 9ba8854..6f48e4d 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -255,7 +255,7 @@
 
 PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path,
         const SkPaint* paint) {
-    ATRACE_CALL();
+    ATRACE_NAME("Generate Path Texture");
 
     float left, top, offset;
     uint32_t width, height;
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 0dad0dc..e6fd2dc 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -132,7 +132,7 @@
 }
 
 GLuint Program::buildShader(const char* source, GLenum type) {
-    ATRACE_CALL();
+    ATRACE_NAME("Build GL Shader");
 
     GLuint shader = glCreateShader(type);
     glShaderSource(shader, 1, &source, 0);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 13c5499..eb0948f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -25,7 +25,6 @@
 #include <SkCanvas.h>
 #include <algorithm>
 
-#include <utils/Trace.h>
 
 #include "DamageAccumulator.h"
 #include "Debug.h"
@@ -34,6 +33,7 @@
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
 #include "utils/MathUtils.h"
+#include "utils/TraceUtils.h"
 #include "renderthread/CanvasContext.h"
 
 namespace android {
@@ -426,6 +426,10 @@
                 clipFlags = 0; // all clipping done by saveLayer
             }
 
+            ATRACE_FORMAT("%s alpha caused %ssaveLayer %ux%u",
+                    getName(), clipFlags ? "" : "unclipped ",
+                    layerBounds.getWidth(), layerBounds.getHeight());
+
             SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
                     layerBounds.left, layerBounds.top, layerBounds.right, layerBounds.bottom,
                     properties().getAlpha() * 255, saveFlags);
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 3b8a9a4..5fcb194 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -26,6 +26,7 @@
 #include "Caches.h"
 #include "TextureCache.h"
 #include "Properties.h"
+#include "utils/TraceUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -266,7 +267,7 @@
         return;
     }
 
-    ATRACE_CALL();
+    ATRACE_FORMAT("Upload %ux%u Texture", bitmap->width(), bitmap->height());
 
     // We could also enable mipmapping if both bitmap dimensions are powers
     // of 2 but we'd have to deal with size changes. Let's keep this simple
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index ba878bac..af39f16 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -363,7 +363,7 @@
 }
 
 void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) {
-    ATRACE_NAME("precacheText");
+    ATRACE_NAME("Precache Glyphs");
 
     if (numGlyphs == 0 || text == NULL) {
         return;
diff --git a/libs/hwui/utils/TraceUtils.h b/libs/hwui/utils/TraceUtils.h
new file mode 100644
index 0000000..ff8ccb8
--- /dev/null
+++ b/libs/hwui/utils/TraceUtils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef TRACE_UTILS_H
+#define TRACE_UTILS_H
+
+#include <utils/Trace.h>
+
+#define ATRACE_FORMAT(fmt, ...) \
+    TraceUtils::TraceEnder __traceEnder = (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), TraceUtils::TraceEnder())
+
+#define ATRACE_FORMAT_BEGIN(fmt, ...) \
+    TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__)
+
+namespace android {
+namespace uirenderer {
+
+class TraceUtils {
+public:
+    class TraceEnder {
+    public:
+        ~TraceEnder() { ATRACE_END(); }
+    };
+
+    static void atraceFormatBegin(const char* fmt, ...) {
+        if (CC_UNLIKELY(!ATRACE_ENABLED())) return;
+
+        const int BUFFER_SIZE = 256;
+        va_list ap;
+        char buf[BUFFER_SIZE];
+
+        va_start(ap, fmt);
+        vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+        va_end(ap);
+
+        ATRACE_BEGIN(buf);
+    }
+
+}; // class TraceUtils
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TRACE_UTILS_H */
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index 7c0d758..a6cc493 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -35,4 +35,7 @@
 
     public abstract void setStreamVolumeForUid(int streamType, int direction, int flags,
             String callingPackage, int uid);
+
+    public abstract void adjustMasterVolumeForUid(int steps, int flags, String callingPackage,
+            int uid);
 }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 6e14aba..c70ac55 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -50,7 +50,6 @@
 import android.media.MediaPlayer.OnErrorListener;
 import android.media.audiopolicy.AudioMix;
 import android.media.audiopolicy.AudioPolicyConfig;
-import android.media.session.MediaSessionLegacyHelper;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Environment;
@@ -61,7 +60,6 @@
 import android.os.PowerManager;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -75,12 +73,11 @@
 import android.util.MathUtils;
 import android.util.Slog;
 import android.view.KeyEvent;
+import android.view.OrientationEventListener;
 import android.view.Surface;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
-import android.view.OrientationEventListener;
 
-import com.android.internal.telephony.ITelephony;
 import com.android.internal.util.XmlUtils;
 import com.android.server.LocalServices;
 
@@ -91,8 +88,6 @@
 import java.io.PrintWriter;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -100,6 +95,7 @@
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * The implementation of the volume manager service.
@@ -1152,6 +1148,10 @@
 
     /** @see AudioManager#adjustMasterVolume(int, int) */
     public void adjustMasterVolume(int steps, int flags, String callingPackage) {
+        adjustMasterVolume(steps, flags, callingPackage, Binder.getCallingUid());
+    }
+
+    public void adjustMasterVolume(int steps, int flags, String callingPackage, int uid) {
         if (mUseFixedVolume) {
             return;
         }
@@ -1166,7 +1166,7 @@
         }
 
         //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
-        setMasterVolume(volume, flags, callingPackage);
+        setMasterVolume(volume, flags, callingPackage, uid);
     }
 
     // StreamVolumeCommand contains the information needed to defer the process of
@@ -1679,18 +1679,24 @@
         }
     }
 
+    @Override
     public int getMasterVolume() {
         if (isMasterMute()) return 0;
         return getLastAudibleMasterVolume();
     }
 
+    @Override
     public void setMasterVolume(int volume, int flags, String callingPackage) {
+        setMasterVolume(volume, flags, callingPackage, Binder.getCallingUid());
+    }
+
+    public void setMasterVolume(int volume, int flags, String callingPackage, int uid) {
         if (mUseFixedVolume) {
             return;
         }
 
-        if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(),
-                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+        if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
             return;
         }
 
@@ -5667,6 +5673,12 @@
                 String callingPackage, int uid) {
             setStreamVolume(streamType, direction, flags, callingPackage, uid);
         }
+
+        @Override
+        public void adjustMasterVolumeForUid(int steps, int flags, String callingPackage,
+                int uid) {
+            adjustMasterVolume(steps, flags, callingPackage, uid);
+        }
     }
 
     //==========================================================================================
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 924078c..754da0e 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -16,8 +16,6 @@
 package android.media;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -25,16 +23,14 @@
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.OperationCanceledException;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Size;
 import android.util.SparseArray;
 
+import java.util.ArrayList;
 import java.util.Set;
 
 /**
@@ -569,6 +565,29 @@
         }
 
         /**
+         * Create a Builder using a {@link MediaMetadata} instance to set
+         * initial values, but replace bitmaps with a scaled down copy if they
+         * are larger than maxBitmapSize.
+         *
+         * @param source The original metadata to copy.
+         * @param maxBitmapSize The maximum height/width for bitmaps contained
+         *            in the metadata.
+         * @hide
+         */
+        public Builder(MediaMetadata source, int maxBitmapSize) {
+            this(source);
+            for (String key : mBundle.keySet()) {
+                Object value = mBundle.get(key);
+                if (value != null && value instanceof Bitmap) {
+                    Bitmap bmp = (Bitmap) value;
+                    if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
+                        putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
+                    }
+                }
+            }
+        }
+
+        /**
          * Put a CharSequence value into the metadata. Custom keys may be used,
          * but if the METADATA_KEYs defined in this class are used they may only
          * be one of the following:
@@ -707,6 +726,10 @@
          * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
          * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
          * </ul>
+         * <p>
+         * Large bitmaps may be scaled down by the system. To pass full
+         * resolution images {@link Uri Uris} should be used with
+         * {@link #putString}.
          *
          * @param key The key for referencing this value
          * @param value The Bitmap to store
@@ -731,5 +754,15 @@
         public MediaMetadata build() {
             return new MediaMetadata(mBundle);
         }
+
+        private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
+            float maxSizeF = maxSize;
+            float widthScale = maxSizeF / bmp.getWidth();
+            float heightScale = maxSizeF / bmp.getHeight();
+            float scale = Math.min(widthScale, heightScale);
+            int height = (int) (bmp.getHeight() * scale);
+            int width = (int) (bmp.getWidth() * scale);
+            return Bitmap.createScaledBitmap(bmp, width, height, true);
+        }
     }
 }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 973527f..e13f008 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -104,6 +104,7 @@
     public @interface SessionFlags { }
 
     private final Object mLock = new Object();
+    private final int mMaxBitmapSize;
 
     private final MediaSession.Token mSessionToken;
     private final MediaController mController;
@@ -147,6 +148,8 @@
         if (TextUtils.isEmpty(tag)) {
             throw new IllegalArgumentException("tag cannot be null or empty");
         }
+        mMaxBitmapSize = context.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
         mCbStub = new CallbackStub(this);
         MediaSessionManager manager = (MediaSessionManager) context
                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
@@ -409,6 +412,7 @@
      * @param metadata The new metadata
      */
     public void setMetadata(@Nullable MediaMetadata metadata) {
+        metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
         try {
             mBinder.setMetadata(metadata);
         } catch (RemoteException e) {
diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
index 5c776e6..456d2f9 100644
--- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml
@@ -57,6 +57,7 @@
                 android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_back"
+                android:scaleType="centerInside"
                 systemui:keyCode="4"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_back"
@@ -65,6 +66,7 @@
                 android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_home"
+                android:scaleType="centerInside"
                 systemui:keyCode="3"
                 systemui:keyRepeat="true"
                 android:layout_weight="0"
@@ -74,6 +76,7 @@
                 android:layout_width="128dp" android:paddingStart="25dp" android:paddingEnd="25dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
+                android:scaleType="centerInside"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_recent"
                 />
@@ -91,6 +94,7 @@
                     android:layout_width="@dimen/navigation_extra_key_width"
                     android:layout_height="match_parent"
                     android:src="@drawable/ic_sysbar_menu"
+                    android:scaleType="centerInside"
                     android:layout_marginEnd="2dp"
                     systemui:keyCode="82"
                     android:visibility="invisible"
@@ -198,6 +202,7 @@
                 android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_back"
+                android:scaleType="centerInside"
                 systemui:keyCode="4"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_back"
@@ -206,6 +211,7 @@
                 android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_home"
+                android:scaleType="centerInside"
                 systemui:keyCode="3"
                 systemui:keyRepeat="true"
                 android:layout_weight="0"
@@ -215,6 +221,7 @@
                 android:layout_width="162dp" android:paddingStart="42dp" android:paddingEnd="42dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent"
+                android:scaleType="centerInside"
                 android:layout_weight="0"
                 android:contentDescription="@string/accessibility_recent"
                 />
@@ -233,6 +240,7 @@
                     android:layout_height="match_parent"
                     android:layout_marginEnd="2dp"
                     android:src="@drawable/ic_sysbar_menu"
+                    android:scaleType="centerInside"
                     systemui:keyCode="82"
                     android:visibility="invisible"
                     android:contentDescription="@string/accessibility_menu" />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index ef85847..4526af5 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -32,6 +32,7 @@
         />
 
     <LinearLayout
+        android:id="@+id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
         <TextView
diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
new file mode 100644
index 0000000..2ff8f8a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+/**
+ * Helper to invert the colors of views and fade between the states.
+ */
+public class ViewInvertHelper {
+
+    private final Paint mDarkPaint = new Paint();
+    private final Interpolator mLinearOutSlowInInterpolator;
+    private final View mTarget;
+    private final ColorMatrix mMatrix = new ColorMatrix();
+    private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
+    private final long mFadeDuration;
+
+    public ViewInvertHelper(View target, long fadeDuration) {
+        mTarget = target;
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTarget.getContext(),
+                android.R.interpolator.linear_out_slow_in);
+        mFadeDuration = fadeDuration;
+    }
+
+    public void fade(final boolean invert, long delay) {
+        float startIntensity = invert ? 0f : 1f;
+        float endIntensity = invert ? 1f : 0f;
+        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateInvertPaint((Float) animation.getAnimatedValue());
+                mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+            }
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!invert) {
+                    mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
+                }
+            }
+        });
+        animator.setDuration(mFadeDuration);
+        animator.setInterpolator(mLinearOutSlowInInterpolator);
+        animator.setStartDelay(delay);
+        animator.start();
+    }
+
+    public void update(boolean invert) {
+        if (invert) {
+            updateInvertPaint(1f);
+            mTarget.setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+        } else {
+            mTarget.setLayerType(View.LAYER_TYPE_NONE, null);
+        }
+    }
+
+    public View getTarget() {
+        return mTarget;
+    }
+
+    private void updateInvertPaint(float intensity) {
+        float components = 1 - 2 * intensity;
+        final float[] invert = {
+                components, 0f,         0f,         0f, 255f * intensity,
+                0f,         components, 0f,         0f, 255f * intensity,
+                0f,         0f,         components, 0f, 255f * intensity,
+                0f,         0f,         0f,         1f, 0f
+        };
+        mMatrix.set(invert);
+        mGrayscaleMatrix.setSaturation(1 - intensity);
+        mMatrix.preConcat(mGrayscaleMatrix);
+        mDarkPaint.setColorFilter(new ColorMatrixColorFilter(mMatrix));
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
index 0ab6626..4f812bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SecureSetting.java
@@ -26,24 +26,26 @@
 
 /** Helper for managing a secure setting. **/
 public abstract class SecureSetting extends ContentObserver implements Listenable {
+    private static final int DEFAULT = 0;
+
     private final Context mContext;
     private final String mSettingName;
 
     private boolean mListening;
     private int mUserId;
+    private int mObservedValue = DEFAULT;
 
-    protected abstract void handleValueChanged(int value);
+    protected abstract void handleValueChanged(int value, boolean observedChange);
 
     public SecureSetting(Context context, Handler handler, String settingName) {
         super(handler);
         mContext = context;
         mSettingName = settingName;
         mUserId = ActivityManager.getCurrentUser();
-        setListening(true);
     }
 
     public int getValue() {
-        return Secure.getIntForUser(mContext.getContentResolver(), mSettingName, 0, mUserId);
+        return Secure.getIntForUser(mContext.getContentResolver(), mSettingName, DEFAULT, mUserId);
     }
 
     public void setValue(int value) {
@@ -52,18 +54,23 @@
 
     @Override
     public void setListening(boolean listening) {
+        if (listening == mListening) return;
         mListening = listening;
         if (listening) {
+            mObservedValue = getValue();
             mContext.getContentResolver().registerContentObserver(
                     Secure.getUriFor(mSettingName), false, this, mUserId);
         } else {
             mContext.getContentResolver().unregisterContentObserver(this);
+            mObservedValue = DEFAULT;
         }
     }
 
     @Override
     public void onChange(boolean selfChange) {
-        handleValueChanged(getValue());
+        final int value = getValue();
+        handleValueChanged(value, value != mObservedValue);
+        mObservedValue = value;
     }
 
     public void setUserId(int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index b565afa..5963a45 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -41,8 +41,10 @@
         mSetting = new SecureSetting(mContext, mHandler,
                 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED) {
             @Override
-            protected void handleValueChanged(int value) {
-                mUsageTracker.trackUsage();
+            protected void handleValueChanged(int value, boolean observedChange) {
+                if (value != 0 || observedChange) {
+                    mUsageTracker.trackUsage();
+                }
                 if (mListening) {
                     handleRefreshState(value);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index bb9a105..38ce467 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -32,6 +32,7 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Pair;
 import android.view.LayoutInflater;
@@ -361,8 +362,7 @@
         // If the user has toggled it too quickly, then just eat up the event here (it's better than
         // showing a janky screenshot).
         // NOTE: Ideally, the screenshot mechanism would take the window transform into account
-        long currentTime = System.currentTimeMillis();
-        if ((currentTime > mLastToggleTime) && (currentTime - mLastToggleTime) < sMinToggleDelay) {
+        if ((SystemClock.elapsedRealtime() - mLastToggleTime) < sMinToggleDelay) {
             return;
         }
 
@@ -377,7 +377,7 @@
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
                     Intent.FLAG_RECEIVER_FOREGROUND);
             mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
-            mLastToggleTime = System.currentTimeMillis();
+            mLastToggleTime = SystemClock.elapsedRealtime();
             return;
         } else {
             // Otherwise, start the recents activity
@@ -559,7 +559,7 @@
                 startAlternateRecentsActivity(topTask, opts, EXTRA_FROM_HOME, stackVr);
             }
         }
-        mLastToggleTime = System.currentTimeMillis();
+        mLastToggleTime = SystemClock.elapsedRealtime();
     }
 
     /** Starts the recents activity */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 676f1ab..6dc2edb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -28,6 +28,7 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.Pair;
 import android.view.KeyEvent;
@@ -564,13 +565,13 @@
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_TAB: {
-                boolean hasRepKeyTimeElapsed = (System.currentTimeMillis() -
+                boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
                         mLastTabKeyEventTime) > mConfig.altTabKeyDelay;
                 if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
                     // Focus the next task in the stack
                     final boolean backward = event.isShiftPressed();
                     mRecentsView.focusNextTask(!backward);
-                    mLastTabKeyEventTime = System.currentTimeMillis();
+                    mLastTabKeyEventTime = SystemClock.elapsedRealtime();
                 }
                 return true;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 02b9378..7b60307 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -43,6 +43,7 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 /**
  * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -53,6 +54,7 @@
     private static final long DOUBLETAP_TIMEOUT_MS = 1200;
     private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
     private static final int ACTIVATE_ANIMATION_LENGTH = 220;
+    private static final int DARK_ANIMATION_LENGTH = 170;
 
     /**
      * The amount of width, which is kept in the end when performing a disappear animation (also
@@ -84,6 +86,11 @@
      */
     private static final float VERTICAL_ANIMATION_START = 1.0f;
 
+    /**
+     * Scale for the background to animate from when exiting dark mode.
+     */
+    private static final float DARK_EXIT_SCALE_START = 0.93f;
+
     private static final Interpolator ACTIVATE_INVERSE_INTERPOLATOR
             = new PathInterpolator(0.6f, 0, 0.5f, 1);
     private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
@@ -94,7 +101,6 @@
 
     private boolean mDimmed;
     private boolean mDark;
-    private final Paint mDarkPaint = createDarkPaint();
 
     private int mBgTint = 0;
     private final int mRoundedRectCornerRadius;
@@ -332,40 +338,32 @@
         if (mDimmed != dimmed) {
             mDimmed = dimmed;
             if (fade) {
-                fadeBackground();
+                fadeDimmedBackground();
             } else {
                 updateBackground();
             }
         }
     }
 
-    public void setDark(boolean dark, boolean fade) {
-        // TODO implement fade
-        if (mDark != dark) {
-            mDark = dark;
-            if (mDark) {
-                setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
-            } else {
-                setLayerType(View.LAYER_TYPE_NONE, null);
-            }
+    public void setDark(boolean dark, boolean fade, long delay) {
+        super.setDark(dark, fade, delay);
+        if (mDark == dark) {
+            return;
         }
-    }
-
-    private static Paint createDarkPaint() {
-        final Paint p = new Paint();
-        final float[] invert = {
-            -1f,  0f,  0f, 1f, 1f,
-             0f, -1f,  0f, 1f, 1f,
-             0f,  0f, -1f, 1f, 1f,
-             0f,  0f,  0f, 1f, 0f
-        };
-        final ColorMatrix m = new ColorMatrix(invert);
-        final ColorMatrix grayscale = new ColorMatrix();
-        grayscale.setSaturation(0);
-        m.preConcat(grayscale);
-        p.setColorFilter(new ColorMatrixColorFilter(m));
-        return p;
-    }
+        mDark = dark;
+        if (!dark && fade) {
+            if (mActivated) {
+                mBackgroundDimmed.setVisibility(View.VISIBLE);
+                mBackgroundNormal.setVisibility(View.VISIBLE);
+            } else {
+                mBackgroundDimmed.setVisibility(View.VISIBLE);
+                mBackgroundNormal.setVisibility(View.INVISIBLE);
+            }
+            fadeDarkToDimmed(delay);
+        } else {
+            updateBackground();
+        }
+     }
 
     public void setShowingLegacyBackground(boolean showing) {
         mShowingLegacyBackground = showing;
@@ -402,7 +400,39 @@
         mBackgroundNormal.setRippleColor(rippleColor);
     }
 
-    private void fadeBackground() {
+    /**
+     * Fades the dimmed background when exiting dark mode.
+     */
+    private void fadeDarkToDimmed(long delay) {
+        mBackgroundDimmed.setAlpha(0f);
+        mBackgroundDimmed.setPivotX(mBackgroundDimmed.getWidth() / 2f);
+        mBackgroundDimmed.setPivotY(getActualHeight() / 2f);
+        mBackgroundDimmed.setScaleX(DARK_EXIT_SCALE_START);
+        mBackgroundDimmed.setScaleY(DARK_EXIT_SCALE_START);
+        mBackgroundDimmed.animate()
+                .alpha(1f)
+                .scaleX(1f)
+                .scaleY(1f)
+                .setDuration(DARK_ANIMATION_LENGTH)
+                .setStartDelay(delay)
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        // Jump state if we are cancelled
+                        mBackgroundDimmed.setScaleX(1f);
+                        mBackgroundDimmed.setScaleY(1f);
+                        mBackgroundDimmed.setAlpha(1f);
+                    }
+                })
+                .start();
+    }
+
+    /**
+     * Fades the background when the dimmed state changes.
+     */
+    private void fadeDimmedBackground() {
+        mBackgroundDimmed.animate().cancel();
         mBackgroundNormal.animate().cancel();
         if (mDimmed) {
             mBackgroundDimmed.setVisibility(View.VISIBLE);
@@ -443,11 +473,14 @@
     }
 
     private void updateBackground() {
-        if (mDimmed) {
+        cancelFadeAnimations();
+        if (mDark) {
+            mBackgroundDimmed.setVisibility(View.INVISIBLE);
+            mBackgroundNormal.setVisibility(View.INVISIBLE);
+        } else if (mDimmed) {
             mBackgroundDimmed.setVisibility(View.VISIBLE);
             mBackgroundNormal.setVisibility(View.INVISIBLE);
         } else {
-            cancelFadeAnimations();
             mBackgroundDimmed.setVisibility(View.INVISIBLE);
             mBackgroundNormal.setVisibility(View.VISIBLE);
             mBackgroundNormal.setAlpha(1f);
@@ -459,6 +492,7 @@
         if (mBackgroundAnimator != null) {
             mBackgroundAnimator.cancel();
         }
+        mBackgroundDimmed.animate().cancel();
         mBackgroundNormal.animate().cancel();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7345440..8ad8406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -207,11 +207,11 @@
     }
 
     @Override
-    public void setDark(boolean dark, boolean fade) {
-        super.setDark(dark, fade);
+    public void setDark(boolean dark, boolean fade, long delay) {
+        super.setDark(dark, fade, delay);
         final NotificationContentView showing = getShowingLayout();
         if (showing != null) {
-            showing.setDark(dark, fade);
+            showing.setDark(dark, fade, delay);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index bf1e78e..ebc663c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -39,6 +39,7 @@
     private int mActualHeight;
     protected int mClipTopAmount;
     private boolean mActualHeightInitialized;
+    private boolean mDark;
     private ArrayList<View> mMatchParentViews = new ArrayList<View>();
 
     public ExpandableView(Context context, AttributeSet attrs) {
@@ -185,8 +186,14 @@
      *
      * @param dark Whether the notification should be dark.
      * @param fade Whether an animation should be played to change the state.
+     * @param delay If fading, the delay of the animation.
      */
-    public void setDark(boolean dark, boolean fade) {
+    public void setDark(boolean dark, boolean fade, long delay) {
+        mDark = dark;
+    }
+
+    public boolean isDark() {
+        return mDark;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 99214a0..27da6fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -42,14 +42,14 @@
 public class NotificationContentView extends FrameLayout {
 
     private static final long ANIMATION_DURATION_LENGTH = 170;
-    private static final Paint INVERT_PAINT = createInvertPaint();
-    private static final ColorFilter NO_COLOR_FILTER = new ColorFilter();
 
     private final Rect mClipBounds = new Rect();
 
     private View mContractedChild;
     private View mExpandedChild;
 
+    private NotificationViewWrapper mContractedWrapper;
+
     private int mSmallHeight;
     private int mClipTopAmount;
     private int mActualHeight;
@@ -84,8 +84,8 @@
     }
 
     @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
         updateVisibility();
     }
 
@@ -122,6 +122,7 @@
         sanitizeContractedLayoutParams(child);
         addView(child);
         mContractedChild = child;
+        mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child);
         selectLayout(false /* animate */, true /* force */);
     }
 
@@ -249,38 +250,10 @@
         return mExpandedChild != null;
     }
 
-    public void setDark(boolean dark, boolean fade) {
+    public void setDark(boolean dark, boolean fade, long delay) {
         if (mDark == dark || mContractedChild == null) return;
         mDark = dark;
-        setImageViewDark(dark, fade, com.android.internal.R.id.right_icon);
-        setImageViewDark(dark, fade, com.android.internal.R.id.icon);
-    }
-
-    private void setImageViewDark(boolean dark, boolean fade, int imageViewId) {
-        // TODO: implement fade
-        final ImageView v = (ImageView) mContractedChild.findViewById(imageViewId);
-        if (v == null) return;
-        final Drawable d = v.getBackground();
-        if (dark) {
-            v.setLayerType(LAYER_TYPE_HARDWARE, INVERT_PAINT);
-            if (d != null) {
-                v.setTag(R.id.doze_saved_filter_tag, d.getColorFilter() != null ? d.getColorFilter()
-                        : NO_COLOR_FILTER);
-                d.setColorFilter(getResources().getColor(R.color.doze_small_icon_background_color),
-                        PorterDuff.Mode.SRC_ATOP);
-                v.setImageAlpha(getResources().getInteger(R.integer.doze_small_icon_alpha));
-            }
-        } else {
-            v.setLayerType(LAYER_TYPE_NONE, null);
-            if (d != null)  {
-                final ColorFilter filter = (ColorFilter) v.getTag(R.id.doze_saved_filter_tag);
-                if (filter != null) {
-                    d.setColorFilter(filter == NO_COLOR_FILTER ? null : filter);
-                    v.setTag(R.id.doze_saved_filter_tag, null);
-                }
-                v.setImageAlpha(0xff);
-            }
-        }
+        mContractedWrapper.setDark(dark, fade, delay);
     }
 
     @Override
@@ -290,16 +263,4 @@
         // layout, and saves us some layers.
         return false;
     }
-
-    private static Paint createInvertPaint() {
-        final Paint p = new Paint();
-        final float[] invert = {
-            -1f,  0f,  0f, 1f, 1f,
-             0f, -1f,  0f, 1f, 1f,
-             0f,  0f, -1f, 1f, 1f,
-             0f,  0f,  0f, 1f, 0f
-        };
-        p.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(invert)));
-        return p;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java
new file mode 100644
index 0000000..045be3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationCustomViewWrapper.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.view.View;
+
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+/**
+ * Wraps a notification containing a custom view.
+ */
+public class NotificationCustomViewWrapper extends NotificationViewWrapper {
+
+    private final ViewInvertHelper mInvertHelper;
+    private boolean mDark;
+
+    protected NotificationCustomViewWrapper(View view) {
+        super(view);
+        mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION);
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mDark != dark) {
+            mDark = dark;
+            if (fade) {
+                mInvertHelper.fade(dark, delay);
+            } else {
+                mInvertHelper.update(dark);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java
new file mode 100644
index 0000000..8f63a79
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaViewWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.view.View;
+
+/**
+ * Wraps a media notification.
+ */
+public class NotificationMediaViewWrapper extends NotificationTemplateViewWrapper {
+
+    private boolean mDark;
+
+    protected NotificationMediaViewWrapper(Context ctx, View view) {
+        super(ctx, view);
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mDark != dark) {
+            mDark = dark;
+
+            // Only update the large icon, because the rest is already inverted.
+            setPictureGrayscale(dark, fade, delay);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index edfd205..bfa3aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -21,6 +21,8 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 /**
  * Container view for overflowing notification icons on Keyguard.
@@ -28,6 +30,8 @@
 public class NotificationOverflowContainer extends ActivatableNotificationView {
 
     private NotificationOverflowIconsView mIconsView;
+    private ViewInvertHelper mViewInvertHelper;
+    private boolean mDark;
 
     public NotificationOverflowContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -39,6 +43,20 @@
         mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
         mIconsView.setMoreText((TextView) findViewById(R.id.more_text));
         mIconsView.setOverflowIndicator(findViewById(R.id.more_icon_overflow));
+        mViewInvertHelper = new ViewInvertHelper(findViewById(R.id.content),
+                NotificationPanelView.DOZE_ANIMATION_DURATION);
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        super.setDark(dark, fade, delay);
+        if (mDark == dark) return;
+        mDark = dark;
+        if (fade) {
+            mViewInvertHelper.fade(dark, delay);
+        } else {
+            mViewInvertHelper.update(dark);
+        }
     }
 
     public NotificationOverflowIconsView getIconsView() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
new file mode 100644
index 0000000..8dc14b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+/**
+ * Wraps a notification view inflated from a template.
+ */
+public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
+
+    private final ViewInvertHelper mInvertHelper;
+    private final ImageView mIcon;
+    protected final ImageView mPicture;
+    private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
+    private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
+            0, PorterDuff.Mode.SRC_ATOP);
+    private final int mIconDarkAlpha;
+    private final int mIconBackgroundColor;
+    private final int mIconBackgroundDarkColor;
+    private final Interpolator mLinearOutSlowInInterpolator;
+
+    private boolean mDark;
+
+    protected NotificationTemplateViewWrapper(Context ctx, View view) {
+        super(view);
+        mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+        mIconBackgroundDarkColor =
+                ctx.getResources().getColor(R.color.doze_small_icon_background_color);
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
+                android.R.interpolator.linear_out_slow_in);
+        View mainColumn = view.findViewById(com.android.internal.R.id.notification_main_column);
+        mInvertHelper = mainColumn != null
+                ? new ViewInvertHelper(mainColumn, NotificationPanelView.DOZE_ANIMATION_DURATION)
+                : null;
+        ImageView largeIcon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
+        ImageView rightIcon = (ImageView) view.findViewById(com.android.internal.R.id.right_icon);
+        mIcon = resolveIcon(largeIcon, rightIcon);
+        mPicture = resolvePicture(largeIcon);
+        mIconBackgroundColor = resolveBackgroundColor(mIcon);
+    }
+
+    private ImageView resolveIcon(ImageView largeIcon, ImageView rightIcon) {
+        return largeIcon != null && largeIcon.getBackground() != null ? largeIcon
+                : rightIcon != null && rightIcon.getBackground() != null ? rightIcon
+                : null;
+    }
+
+    private ImageView resolvePicture(ImageView largeIcon) {
+        return largeIcon != null && largeIcon.getBackground() == null
+                ? largeIcon
+                : null;
+    }
+
+    private int resolveBackgroundColor(ImageView icon) {
+        if (icon != null && icon.getBackground() != null) {
+            ColorFilter filter = icon.getBackground().getColorFilter();
+            if (filter instanceof PorterDuffColorFilter) {
+                return ((PorterDuffColorFilter) filter).getColor();
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mDark != dark) {
+            mDark = dark;
+            if (mInvertHelper != null) {
+                if (fade) {
+                    mInvertHelper.fade(dark, delay);
+                } else {
+                    mInvertHelper.update(dark);
+                }
+            }
+            if (mIcon != null) {
+                if (fade) {
+                    fadeIconColorFilter(mIcon, dark, delay);
+                    fadeIconAlpha(mIcon, dark, delay);
+                } else {
+                    updateIconColorFilter(mIcon, dark);
+                    updateIconAlpha(mIcon, dark);
+                }
+            }
+            setPictureGrayscale(dark, fade, delay);
+        }
+    }
+
+    protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
+        if (mPicture != null) {
+            if (fade) {
+                fadeGrayscale(mPicture, grayscale, delay);
+            } else {
+                updateGrayscale(mPicture, grayscale);
+            }
+        }
+    }
+
+    private void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+            boolean dark, long delay, Animator.AnimatorListener listener) {
+        float startIntensity = dark ? 0f : 1f;
+        float endIntensity = dark ? 1f : 0f;
+        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+        animator.addUpdateListener(updateListener);
+        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+        animator.setInterpolator(mLinearOutSlowInInterpolator);
+        animator.setStartDelay(delay);
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        animator.start();
+    }
+
+    private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateIconColorFilter(target, (Float) animation.getAnimatedValue());
+            }
+        }, dark, delay, null /* listener */);
+    }
+
+    private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float t = (float) animation.getAnimatedValue();
+                target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
+            }
+        }, dark, delay, null /* listener */);
+    }
+
+    protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateGrayscaleMatrix((float) animation.getAnimatedValue());
+                target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+            }
+        }, dark, delay, new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!dark) {
+                    target.setColorFilter(null);
+                }
+            }
+        });
+    }
+
+    private void updateIconColorFilter(ImageView target, boolean dark) {
+        updateIconColorFilter(target, dark ? 1f : 0f);
+    }
+
+    private void updateIconColorFilter(ImageView target, float intensity) {
+        int color = interpolateColor(mIconBackgroundColor, mIconBackgroundDarkColor, intensity);
+        mIconColorFilter.setColor(color);
+        target.getBackground().mutate().setColorFilter(mIconColorFilter);
+    }
+
+    private void updateIconAlpha(ImageView target, boolean dark) {
+        target.setImageAlpha(dark ? mIconDarkAlpha : 255);
+    }
+
+    protected void updateGrayscale(ImageView target, boolean dark) {
+        if (dark) {
+            updateGrayscaleMatrix(1f);
+            target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+        } else {
+            target.setColorFilter(null);
+        }
+    }
+
+    private void updateGrayscaleMatrix(float intensity) {
+        mGrayscaleColorMatrix.setSaturation(1 - intensity);
+    }
+
+    private static int interpolateColor(int source, int target, float t) {
+        int aSource = Color.alpha(source);
+        int rSource = Color.red(source);
+        int gSource = Color.green(source);
+        int bSource = Color.blue(source);
+        int aTarget = Color.alpha(target);
+        int rTarget = Color.red(target);
+        int gTarget = Color.green(target);
+        int bTarget = Color.blue(target);
+        return Color.argb(
+                (int) (aSource * (1f - t) + aTarget * t),
+                (int) (rSource * (1f - t) + rTarget * t),
+                (int) (gSource * (1f - t) + gTarget * t),
+                (int) (bSource * (1f - t) + bTarget * t));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
new file mode 100644
index 0000000..0a02573
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.internal.R;
+
+/**
+ * Wraps the actual notification content view; used to implement behaviors which are different for
+ * the individual templates and custom views.
+ */
+public abstract class NotificationViewWrapper {
+
+    protected final View mView;
+
+    public static NotificationViewWrapper wrap(Context ctx, View v) {
+
+        // TODO: Figure out a better way to find out which template the view is.
+        if (v.findViewById(com.android.internal.R.id.media_actions) != null) {
+            return new NotificationMediaViewWrapper(ctx, v);
+        } else if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
+            return new NotificationTemplateViewWrapper(ctx, v);
+        } else {
+            return new NotificationCustomViewWrapper(v);
+        }
+    }
+
+    protected NotificationViewWrapper(View view) {
+        mView = view;
+    }
+
+    /**
+     * In dark mode, we draw as little as possible, assuming a black background.
+     *
+     * @param dark whether we should display ourselves in dark mode
+     * @param fade whether to animate the transition if the mode changes
+     * @param delay if fading, the delay of the animation
+     */
+    public abstract void setDark(boolean dark, boolean fade, long delay);
+}
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 9a1ac49..3fca56d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3708,14 +3708,11 @@
         if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
             return;
         }
-        mNotificationPanel.setDozing(mDozing, mDozeScrimController.isPulsing() /*animate*/);
-        if (mDozing) {
-            mStackScroller.setDark(true, false /*animate*/);
-        } else {
-            mStackScroller.setDark(false, false /*animate*/);
-        }
+        boolean animate = !mDozing && mDozeScrimController.isPulsing();
+        mNotificationPanel.setDozing(mDozing, animate);
+        mStackScroller.setDark(mDozing, animate);
         mScrimController.setDozing(mDozing);
-        mDozeScrimController.setDozing(mDozing, mDozeScrimController.isPulsing() /* animate */);
+        mDozeScrimController.setDozing(mDozing, animate);
     }
 
     public void updateStackScrollerState(boolean goingToFullShade) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 3c93b19..3d4cda6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -33,6 +33,7 @@
     boolean animateHideSensitive;
     boolean hasDelays;
     boolean hasGoToFullShadeEvent;
+    boolean hasDarkEvent;
 
     public AnimationFilter animateAlpha() {
         animateAlpha = true;
@@ -98,6 +99,10 @@
                     NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_GO_TO_FULL_SHADE) {
                 hasGoToFullShadeEvent = true;
             }
+            if (events.get(i).animationType ==
+                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_DARK) {
+                hasDarkEvent = true;
+            }
         }
     }
 
@@ -126,5 +131,6 @@
         animateHideSensitive = false;
         hasDelays = false;
         hasGoToFullShadeEvent = false;
+        hasDarkEvent = false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index e63be97..c5f1161 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2455,7 +2455,8 @@
 
                 // ANIMATION_TYPE_DARK
                 new AnimationFilter()
-                        .animateDark(),
+                        .animateDark()
+                        .hasDelays(),
 
                 // ANIMATION_TYPE_GO_TO_FULL_SHADE
                 new AnimationFilter()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 4611370..0b1ce8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -154,7 +154,7 @@
                 child.setDimmed(state.dimmed, false /* animate */);
 
                 // apply dark
-                child.setDark(state.dark, false /* animate */);
+                child.setDark(state.dark, false /* animate */, 0 /* delay */);
 
                 // apply hiding sensitive
                 child.setHideSensitive(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index a56440c..05077bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -46,6 +46,7 @@
     public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
     public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
     public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
+    public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24;
     private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
 
     private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
@@ -161,11 +162,12 @@
         boolean scaleChanging = child.getScaleX() != viewState.scale;
         boolean alphaChanging = alpha != child.getAlpha();
         boolean heightChanging = viewState.height != child.getActualHeight();
+        boolean darkChanging = viewState.dark != child.isDark();
         boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
         boolean wasAdded = mNewAddChildren.contains(child);
         boolean hasDelays = mAnimationFilter.hasDelays;
         boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
-                alphaChanging || heightChanging || topInsetChanging;
+                alphaChanging || heightChanging || topInsetChanging || darkChanging;
         boolean noAnimation = wasAdded;
         long delay = 0;
         long duration = mCurrentLength;
@@ -242,7 +244,7 @@
                 && !noAnimation);
 
         // start dark animation
-        child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation);
+        child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation, delay);
 
         // apply speed bump state
         child.setBelowSpeedBump(viewState.belowSpeedBump);
@@ -262,6 +264,9 @@
 
     private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
             StackScrollState finalState) {
+        if (mAnimationFilter.hasDarkEvent) {
+            return calculateDelayDark(viewState);
+        }
         if (mAnimationFilter.hasGoToFullShadeEvent) {
             return calculateDelayGoToFullShade(viewState);
         }
@@ -309,6 +314,10 @@
         return minDelay;
     }
 
+    private long calculateDelayDark(StackScrollState.ViewState viewState) {
+        return viewState.notGoneIndex * ANIMATION_DELAY_PER_ELEMENT_DARK;
+    }
+
     private long calculateDelayGoToFullShade(StackScrollState.ViewState viewState) {
         float index = viewState.notGoneIndex;
         index = (float) Math.pow(index, 0.7f);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fd54892..f0fb9e6 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -838,10 +838,12 @@
      * indirect content-provider access.
      */
     private class Identity {
-        public int pid;
-        public int uid;
+        public final IBinder token;
+        public final int pid;
+        public final int uid;
 
-        Identity(int _pid, int _uid) {
+        Identity(IBinder _token, int _pid, int _uid) {
+            token = _token;
             pid = _pid;
             uid = _uid;
         }
@@ -2275,15 +2277,19 @@
      * process when the bindApplication() IPC is sent to the process. They're
      * lazily setup to make sure the services are running when they're asked for.
      */
-    private HashMap<String, IBinder> getCommonServicesLocked() {
+    private HashMap<String, IBinder> getCommonServicesLocked(boolean isolated) {
         if (mAppBindArgs == null) {
-            mAppBindArgs = new HashMap<String, IBinder>();
+            mAppBindArgs = new HashMap<>();
 
-            // Setup the application init args
-            mAppBindArgs.put("package", ServiceManager.getService("package"));
-            mAppBindArgs.put("window", ServiceManager.getService("window"));
-            mAppBindArgs.put(Context.ALARM_SERVICE,
-                    ServiceManager.getService(Context.ALARM_SERVICE));
+            // Isolated processes won't get this optimization, so that we don't
+            // violate the rules about which services they have access to.
+            if (!isolated) {
+                // Setup the application init args
+                mAppBindArgs.put("package", ServiceManager.getService("package"));
+                mAppBindArgs.put("window", ServiceManager.getService("window"));
+                mAppBindArgs.put(Context.ALARM_SERVICE,
+                        ServiceManager.getService(Context.ALARM_SERVICE));
+            }
         }
         return mAppBindArgs;
     }
@@ -5906,7 +5912,8 @@
                     profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                     app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                     isRestrictedBackupMode || !normalMode, app.persistent,
-                    new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
+                    new Configuration(mConfiguration), app.compat,
+                    getCommonServicesLocked(app.isolated),
                     mCoreSettingsObserver.getCoreSettingsLocked());
             updateLruProcessLocked(app, false, null);
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
@@ -6726,21 +6733,9 @@
      */
     int checkComponentPermission(String permission, int pid, int uid,
             int owningUid, boolean exported) {
-        // We might be performing an operation on behalf of an indirect binder
-        // invocation, e.g. via {@link #openContentUri}.  Check and adjust the
-        // client identity accordingly before proceeding.
-        Identity tlsIdentity = sCallerIdentity.get();
-        if (tlsIdentity != null) {
-            Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
-                    + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
-            uid = tlsIdentity.uid;
-            pid = tlsIdentity.pid;
-        }
-
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
-
         return ActivityManager.checkComponentPermission(permission, uid,
                 owningUid, exported);
     }
@@ -6762,6 +6757,26 @@
         return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
     }
 
+    @Override
+    public int checkPermissionWithToken(String permission, int pid, int uid, IBinder callerToken) {
+        if (permission == null) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
+        // We might be performing an operation on behalf of an indirect binder
+        // invocation, e.g. via {@link #openContentUri}.  Check and adjust the
+        // client identity accordingly before proceeding.
+        Identity tlsIdentity = sCallerIdentity.get();
+        if (tlsIdentity != null && tlsIdentity.token == callerToken) {
+            Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
+                    + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
+            uid = tlsIdentity.uid;
+            pid = tlsIdentity.pid;
+        }
+
+        return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
+    }
+
     /**
      * Binder IPC calls go through the public entry point.
      * This can be called with or without the global lock held.
@@ -6967,13 +6982,13 @@
      */
     @Override
     public int checkUriPermission(Uri uri, int pid, int uid,
-            final int modeFlags, int userId) {
+            final int modeFlags, int userId, IBinder callerToken) {
         enforceNotIsolatedCaller("checkUriPermission");
 
         // Another redirected-binder-call permissions check as in
-        // {@link checkComponentPermission}.
+        // {@link checkPermissionWithToken}.
         Identity tlsIdentity = sCallerIdentity.get();
-        if (tlsIdentity != null) {
+        if (tlsIdentity != null && tlsIdentity.token == callerToken) {
             uid = tlsIdentity.uid;
             pid = tlsIdentity.pid;
         }
@@ -9880,10 +9895,11 @@
             // we do the check against the caller's permissions even though it looks
             // to the content provider like the Activity Manager itself is making
             // the request.
+            Binder token = new Binder();
             sCallerIdentity.set(new Identity(
-                    Binder.getCallingPid(), Binder.getCallingUid()));
+                    token, Binder.getCallingPid(), Binder.getCallingUid()));
             try {
-                pfd = cph.provider.openFile(null, uri, "r", null);
+                pfd = cph.provider.openFile(null, uri, "r", null, token);
             } catch (FileNotFoundException e) {
                 // do nothing; pfd will be returned null
             } finally {
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 921b68b..9440697 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -90,6 +90,7 @@
     private final SessionStub mSession;
     private final SessionCb mSessionCb;
     private final MediaSessionService mService;
+    private final boolean mUseMasterVolume;
 
     private final Object mLock = new Object();
     private final ArrayList<ISessionControllerCallback> mControllerCallbacks =
@@ -139,6 +140,8 @@
         mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
         mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
         mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
+        mUseMasterVolume = service.getContext().getResources().getBoolean(
+                com.android.internal.R.bool.config_useMasterVolume);
     }
 
     /**
@@ -248,6 +251,12 @@
             direction = -1;
         }
         if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
+            if (mUseMasterVolume) {
+                // If this device only uses master volume and playback is local
+                // just adjust the master volume and return.
+                mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName, uid);
+                return;
+            }
             int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
             if (useSuggested) {
                 if (AudioSystem.isStreamActive(stream, 0)) {
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index ba18f48..98a3970 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -86,6 +86,7 @@
     private final Object mLock = new Object();
     private final MessageHandler mHandler = new MessageHandler();
     private final PowerManager.WakeLock mMediaEventWakeLock;
+    private final boolean mUseMasterVolume;
 
     private KeyguardManager mKeyguardManager;
     private IAudioService mAudioService;
@@ -104,6 +105,8 @@
         mPriorityStack = new MediaSessionStack();
         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
+        mUseMasterVolume = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_useMasterVolume);
     }
 
     @Override
@@ -819,8 +822,13 @@
                     return;
                 }
                 try {
-                    mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags,
-                            getContext().getOpPackageName());
+                    if (mUseMasterVolume) {
+                        mAudioService.adjustMasterVolume(direction, flags,
+                                getContext().getOpPackageName());
+                    } else {
+                        mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream, flags,
+                                getContext().getOpPackageName());
+                    }
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error adjusting default volume.", e);
                 }
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 50c12f6..716487c 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -299,6 +299,13 @@
         return -1;
     }
 
+    private static boolean intArrayContains(int[] array, int value) {
+        for (int element : array) {
+            if (element == value) return true;
+        }
+        return false;
+    }
+
     public void addHdmiTvInput(int id, TvInputInfo info) {
         if (info.getType() != TvInputInfo.TYPE_HDMI) {
             throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
@@ -762,20 +769,64 @@
             AudioPortConfig sinkConfig = mAudioSink.activeConfig();
             AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
             boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
+
+            int sinkSamplingRate = mDesiredSamplingRate;
+            int sinkChannelMask = mDesiredChannelMask;
+            int sinkFormat = mDesiredFormat;
+            // If sinkConfig != null and values are set to default, fill in the sinkConfig values.
+            if (sinkConfig != null) {
+                if (sinkSamplingRate == 0) {
+                    sinkSamplingRate = sinkConfig.samplingRate();
+                }
+                if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
+                    sinkChannelMask = sinkConfig.channelMask();
+                }
+                if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
+                    sinkChannelMask = sinkConfig.format();
+                }
+            }
+
             if (sinkConfig == null
-                    || (mDesiredSamplingRate != 0
-                            && sinkConfig.samplingRate() != mDesiredSamplingRate)
-                    || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
-                            && sinkConfig.channelMask() != mDesiredChannelMask)
-                    || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
-                            && sinkConfig.format() != mDesiredFormat)) {
-                sinkConfig = mAudioSink.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
-                        mDesiredFormat, null);
+                    || sinkConfig.samplingRate() != sinkSamplingRate
+                    || sinkConfig.channelMask() != sinkChannelMask
+                    || sinkConfig.format() != sinkFormat) {
+                // Check for compatibility and reset to default if necessary.
+                if (!intArrayContains(mAudioSink.samplingRates(), sinkSamplingRate)
+                        && mAudioSink.samplingRates().length > 0) {
+                    sinkSamplingRate = mAudioSink.samplingRates()[0];
+                }
+                if (!intArrayContains(mAudioSink.channelMasks(), sinkChannelMask)) {
+                    sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
+                }
+                if (!intArrayContains(mAudioSink.formats(), sinkFormat)) {
+                    sinkFormat = AudioFormat.ENCODING_DEFAULT;
+                }
+                sinkConfig = mAudioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
+                        sinkFormat, null);
                 shouldRecreateAudioPatch = true;
             }
             if (sourceConfig == null || sourceGainConfig != null) {
-                sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
-                        sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
+                int sourceSamplingRate = 0;
+                if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
+                    sourceSamplingRate = sinkConfig.samplingRate();
+                } else if (mAudioSource.samplingRates().length > 0) {
+                    // Use any sampling rate and hope audio patch can handle resampling...
+                    sourceSamplingRate = mAudioSource.samplingRates()[0];
+                }
+                int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT;
+                for (int inChannelMask : mAudioSource.channelMasks()) {
+                    if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask())
+                            == AudioFormat.channelCountFromInChannelMask(inChannelMask)) {
+                        sourceChannelMask = inChannelMask;
+                        break;
+                    }
+                }
+                int sourceFormat = AudioFormat.ENCODING_DEFAULT;
+                if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) {
+                    sourceFormat = sinkConfig.format();
+                }
+                sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask,
+                        sourceFormat, sourceGainConfig);
                 shouldRecreateAudioPatch = true;
             }
             if (shouldRecreateAudioPatch) {
@@ -785,6 +836,9 @@
                         new AudioPortConfig[] { sourceConfig },
                         new AudioPortConfig[] { sinkConfig });
                 mAudioPatch = audioPatchArray[0];
+                if (sourceGainConfig != null) {
+                    mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
+                }
             }
         }
 
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index c63eb18..23ba3b6 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -209,8 +209,13 @@
         mUseUsbNotification = !massStorageSupported;
 
         // make sure the ADB_ENABLED setting value matches the current state
-        Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
-
+        try {
+            Settings.Global.putInt(mContentResolver,
+                    Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+        } catch (SecurityException e) {
+            // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
+            Slog.d(TAG, "ADB_ENABLED is restricted.");
+        }
         mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
     }
 
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 215c682..6e404de 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -30,7 +30,7 @@
  * @hide
  */
 @SystemApi
-public abstract class Conference {
+public abstract class Conference implements IConferenceable {
 
     /** @hide */
     public abstract static class Listener {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 63b44a6..fb63c85 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -47,7 +47,7 @@
  * @hide
  */
 @SystemApi
-public abstract class Connection {
+public abstract class Connection implements IConferenceable {
 
     public static final int STATE_INITIALIZING = 0;
 
@@ -82,8 +82,8 @@
                 Connection c, VideoProvider videoProvider) {}
         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
-        public void onConferenceableConnectionsChanged(
-                Connection c, List<Connection> conferenceableConnections) {}
+        public void onConferenceablesChanged(
+                Connection c, List<IConferenceable> conferenceables) {}
         public void onConferenceChanged(Connection c, Conference conference) {}
         /** @hide */
         public void onConferenceParticipantsChanged(Connection c,
@@ -449,7 +449,16 @@
     private final Listener mConnectionDeathListener = new Listener() {
         @Override
         public void onDestroyed(Connection c) {
-            if (mConferenceableConnections.remove(c)) {
+            if (mConferenceables.remove(c)) {
+                fireOnConferenceableConnectionsChanged();
+            }
+        }
+    };
+
+    private final Conference.Listener mConferenceDeathListener = new Conference.Listener() {
+        @Override
+        public void onDestroyed(Conference c) {
+            if (mConferenceables.remove(c)) {
                 fireOnConferenceableConnectionsChanged();
             }
         }
@@ -462,9 +471,9 @@
      */
     private final Set<Listener> mListeners = Collections.newSetFromMap(
             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
-    private final List<Connection> mConferenceableConnections = new ArrayList<>();
-    private final List<Connection> mUnmodifiableConferenceableConnections =
-            Collections.unmodifiableList(mConferenceableConnections);
+    private final List<IConferenceable> mConferenceables = new ArrayList<>();
+    private final List<IConferenceable> mUnmodifiableConferenceables =
+            Collections.unmodifiableList(mConferenceables);
 
     private int mState = STATE_NEW;
     private AudioState mAudioState;
@@ -864,19 +873,44 @@
         for (Connection c : conferenceableConnections) {
             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
             // small amount of items here.
-            if (!mConferenceableConnections.contains(c)) {
+            if (!mConferenceables.contains(c)) {
                 c.addConnectionListener(mConnectionDeathListener);
-                mConferenceableConnections.add(c);
+                mConferenceables.add(c);
             }
         }
         fireOnConferenceableConnectionsChanged();
     }
 
     /**
-     * Returns the connections with which this connection can be conferenced.
+     * Similar to {@link #setConferenceableConnections(java.util.List)}, sets a list of connections
+     * or conferences with which this connection can be conferenced.
+     *
+     * @param conferenceables The conferenceables.
      */
-    public final List<Connection> getConferenceableConnections() {
-        return mUnmodifiableConferenceableConnections;
+    public final void setConferenceables(List<IConferenceable> conferenceables) {
+        clearConferenceableList();
+        for (IConferenceable c : conferenceables) {
+            // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
+            // small amount of items here.
+            if (!mConferenceables.contains(c)) {
+                if (c instanceof Connection) {
+                    Connection connection = (Connection) c;
+                    connection.addConnectionListener(mConnectionDeathListener);
+                } else if (c instanceof Conference) {
+                    Conference conference = (Conference) c;
+                    conference.addListener(mConferenceDeathListener);
+                }
+                mConferenceables.add(c);
+            }
+        }
+        fireOnConferenceableConnectionsChanged();
+    }
+
+    /**
+     * Returns the connections or conferences with which this connection can be conferenced.
+     */
+    public final List<IConferenceable> getConferenceables() {
+        return mUnmodifiableConferenceables;
     }
 
     /*
@@ -1109,7 +1143,7 @@
 
     private final void  fireOnConferenceableConnectionsChanged() {
         for (Listener l : mListeners) {
-            l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
+            l.onConferenceablesChanged(this, getConferenceables());
         }
     }
 
@@ -1120,10 +1154,16 @@
     }
 
     private final void clearConferenceableList() {
-        for (Connection c : mConferenceableConnections) {
-            c.removeConnectionListener(mConnectionDeathListener);
+        for (IConferenceable c : mConferenceables) {
+            if (c instanceof Connection) {
+                Connection connection = (Connection) c;
+                connection.removeConnectionListener(mConnectionDeathListener);
+            } else if (c instanceof Conference) {
+                Conference conference = (Conference) c;
+                conference.removeListener(mConferenceDeathListener);
+            }
         }
-        mConferenceableConnections.clear();
+        mConferenceables.clear();
     }
 
     /**
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 48e6ff3..08f3853 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -515,11 +515,11 @@
         }
 
         @Override
-        public void onConferenceableConnectionsChanged(
-                Connection connection, List<Connection> conferenceableConnections) {
+        public void onConferenceablesChanged(
+                Connection connection, List<IConferenceable> conferenceables) {
             mAdapter.setConferenceableConnections(
                     mIdByConnection.get(connection),
-                    createConnectionIdList(conferenceableConnections));
+                    createIdList(conferenceables));
         }
 
         @Override
@@ -602,7 +602,7 @@
                         connection.getAudioModeIsVoip(),
                         connection.getStatusHints(),
                         connection.getDisconnectCause(),
-                        createConnectionIdList(connection.getConferenceableConnections())));
+                        createIdList(connection.getConferenceables())));
     }
 
     private void abort(String callId) {
@@ -682,12 +682,19 @@
     private void conference(String callId1, String callId2) {
         Log.d(this, "conference %s, %s", callId1, callId2);
 
+        // Attempt to get second connection or conference.
         Connection connection2 = findConnectionForAction(callId2, "conference");
+        Conference conference2 = getNullConference();
         if (connection2 == getNullConnection()) {
-            Log.w(this, "Connection2 missing in conference request %s.", callId2);
-            return;
+            conference2 = findConferenceForAction(callId2, "conference");
+            if (conference2 == getNullConference()) {
+                Log.w(this, "Connection2 or Conference2 missing in conference request %s.",
+                        callId2);
+                return;
+            }
         }
 
+        // Attempt to get first connection or conference and perform merge.
         Connection connection1 = findConnectionForAction(callId1, "conference");
         if (connection1 == getNullConnection()) {
             Conference conference1 = findConferenceForAction(callId1, "addConnection");
@@ -696,10 +703,26 @@
                         "Connection1 or Conference1 missing in conference request %s.",
                         callId1);
             } else {
-                conference1.onMerge(connection2);
+                // Call 1 is a conference.
+                if (connection2 != getNullConnection()) {
+                    // Call 2 is a connection so merge via call 1 (conference).
+                    conference1.onMerge(connection2);
+                } else {
+                    // Call 2 is ALSO a conference; this should never happen.
+                    Log.wtf(this, "There can only be one conference and an attempt was made to " +
+                            "merge two conferences.");
+                    return;
+                }
             }
         } else {
-            onConference(connection1, connection2);
+            // Call 1 is a connection.
+            if (conference2 != getNullConference()) {
+                // Call 2 is a conference, so merge via call 2.
+                conference2.onMerge(connection1);
+            } else {
+                // Call 2 is a connection, so merge together.
+                onConference(connection1, connection2);
+            }
         }
     }
 
@@ -1111,6 +1134,33 @@
         return ids;
     }
 
+    /**
+     * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of
+     * {@link IConferenceable}s passed in.
+     *
+     * @param conferenceables The {@link IConferenceable} connections and conferences.
+     * @return List of string conference and call Ids.
+     */
+    private List<String> createIdList(List<IConferenceable> conferenceables) {
+        List<String> ids = new ArrayList<>();
+        for (IConferenceable c : conferenceables) {
+            // Only allow Connection and Conference conferenceables.
+            if (c instanceof Connection) {
+                Connection connection = (Connection) c;
+                if (mIdByConnection.containsKey(connection)) {
+                    ids.add(mIdByConnection.get(connection));
+                }
+            } else if (c instanceof Conference) {
+                Conference conference = (Conference) c;
+                if (mIdByConference.containsKey(conference)) {
+                    ids.add(mIdByConference.get(conference));
+                }
+            }
+        }
+        Collections.sort(ids);
+        return ids;
+    }
+
     private Conference getNullConference() {
         if (sNullConference == null) {
             sNullConference = new Conference(null) {};
diff --git a/telecomm/java/android/telecom/IConferenceable.java b/telecomm/java/android/telecom/IConferenceable.java
new file mode 100644
index 0000000..095d7cb
--- /dev/null
+++ b/telecomm/java/android/telecom/IConferenceable.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telecom;
+
+import android.annotation.SystemApi;
+
+/**
+ * Interface used to identify entities with which another entity can participate in a conference
+ * call with.  The {@link ConnectionService} implementation will only recognize
+ * {@link IConferenceable}s which are {@link Connection}s or {@link Conference}s.
+ *
+ * @hide
+ */
+@SystemApi
+public interface IConferenceable {
+
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f2d859f..7c03d42 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1797,10 +1797,9 @@
      * for display purpose only, for example, displayed in Phone Status. It won't
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
@@ -1814,10 +1813,9 @@
      * for display purpose only, for example, displayed in Phone Status. It won't
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param subId the subscriber that the alphatag and dialing number belongs to.
      * @param alphaTag alpha-tagging of the dailing nubmer
@@ -1974,10 +1972,9 @@
 
     /**
      * Sets the voice mail number.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param alphaTag The alpha tag to display.
      * @param number The voicemail number.
@@ -1988,10 +1985,9 @@
 
     /**
      * Sets the voicemail number for the given subscriber.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param subId The subscriber id.
      * @param alphaTag The alpha tag to display.
@@ -3072,9 +3068,8 @@
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
      * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @return true on success; false on any failure.
      */
@@ -3085,9 +3080,13 @@
     /**
      * Values used to return status for hasCarrierPrivileges call.
      */
+    /** @hide */
     public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1;
+    /** @hide */
     public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0;
+    /** @hide */
     public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1;
+    /** @hide */
     public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2;
 
     /**
@@ -3099,21 +3098,18 @@
      *
      * TODO: Add a link to documentation.
      *
-     * @return CARRIER_PRIVILEGE_STATUS_HAS_ACCESS if the app has carrier privileges.
-     *         CARRIER_PRIVILEGE_STATUS_NO_ACCESS if the app does not have carrier privileges.
-     *         CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED if the carrier rules are not loaded.
-     *         CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES if there was an error loading carrier
-     *             rules (or if there are no rules).
+     * @return true if the app has carrier privileges.
      */
-    public int hasCarrierPrivileges() {
+    public boolean hasCarrierPrivileges() {
         try {
-            return getITelephony().hasCarrierPrivileges();
+            return getITelephony().getCarrierPrivilegeStatus() ==
+                CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
         } catch (RemoteException ex) {
             Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
         } catch (NullPointerException ex) {
             Rlog.e(TAG, "hasCarrierPrivileges NPE", ex);
         }
-        return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+        return false;
     }
 
     /**
@@ -3124,9 +3120,8 @@
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
      *
-     * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     *  or has to be carrier app - see #hasCarrierPrivileges.
+     * <p>Requires that the calling app has carrier privileges.
+     * @see #hasCarrierPrivileges
      *
      * @param brand The brand name to display/set.
      * @return true if the operation was executed correctly.
@@ -3188,9 +3183,9 @@
         try {
             return getITelephony().checkCarrierPrivilegesForPackage(pkgname);
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "hasCarrierPrivileges RemoteException", ex);
+            Rlog.e(TAG, "checkCarrierPrivilegesForPackage RemoteException", ex);
         } catch (NullPointerException ex) {
-            Rlog.e(TAG, "hasCarrierPrivileges NPE", ex);
+            Rlog.e(TAG, "checkCarrierPrivilegesForPackage NPE", ex);
         }
         return CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
     }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index cbfa9f6..adb3bc4 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -713,7 +713,7 @@
      *
      * @return carrier privilege status defined in TelephonyManager.
      */
-    int hasCarrierPrivileges();
+    int getCarrierPrivilegeStatus();
 
     /**
      * Similar to above, but check for pkg whose name is pkgname.
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 28d52b0..5ef71df 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -91,8 +91,8 @@
 
         @Override
         public ParcelFileDescriptor openFile(
-                String callingPackage, Uri url, String mode, ICancellationSignal signal)
-                throws RemoteException, FileNotFoundException {
+                String callingPackage, Uri url, String mode, ICancellationSignal signal,
+                IBinder callerToken) throws RemoteException, FileNotFoundException {
             return MockContentProvider.this.openFile(url, mode);
         }
 
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 46c81b6..3378872 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -37,6 +37,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.view.DisplayAdjustments;
@@ -483,6 +484,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+        return checkPermission(permission, pid, uid);
+    }
+
     @Override
     public int checkCallingPermission(String permission) {
         throw new UnsupportedOperationException();
@@ -524,6 +531,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+        return checkUriPermission(uri, pid, uid, modeFlags);
+    }
+
     @Override
     public int checkCallingUriPermission(Uri uri, int modeFlags) {
         throw new UnsupportedOperationException();
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index c0dc7c3..ee8c376 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -62,7 +62,8 @@
     }
 
     public ParcelFileDescriptor openFile(
-            String callingPackage, Uri url, String mode, ICancellationSignal signal) {
+            String callingPackage, Uri url, String mode, ICancellationSignal signal,
+            IBinder callerToken) {
         throw new UnsupportedOperationException("unimplemented mock method");
     }
 
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
index 3930fd6..513f622 100644
--- a/tests/ActivityTests/AndroidManifest.xml
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -57,6 +57,16 @@
         </service>
         <receiver android:name="UserTarget">
         </receiver>
+        <receiver android:name="StartEmpty" android:exported="true">
+            <intent-filter>
+                <action android:name="com.example.START_EMPTY" />
+            </intent-filter>
+        </receiver>
+        <service android:name="EmptyService" android:exported="true">
+            <intent-filter>
+                <action android:name="com.example.START_EMPTY" />
+            </intent-filter>
+        </service>
         <receiver android:name="SingleUserReceiver"
             android:singleUser="true" android:exported="true" >
         </receiver>
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/EmptyService.java b/tests/ActivityTests/src/com/google/android/test/activity/EmptyService.java
new file mode 100644
index 0000000..1134d90
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/EmptyService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activity;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+public class EmptyService extends Service {
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i("StartEmpty", "STARTED!");
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+}
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/StartEmpty.java b/tests/ActivityTests/src/com/google/android/test/activity/StartEmpty.java
new file mode 100644
index 0000000..5e74ead
--- /dev/null
+++ b/tests/ActivityTests/src/com/google/android/test/activity/StartEmpty.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class StartEmpty extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.i("StartEmpty", "STARTED!");
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index 89288bf..e4cbb2f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -90,7 +90,7 @@
 
     @Override
     public ParcelFileDescriptor openFile(
-            String callingPackage, Uri arg0, String arg1, ICancellationSignal signal)
+            String callingPackage, Uri arg0, String arg1, ICancellationSignal signal, IBinder token)
             throws RemoteException, FileNotFoundException {
         // TODO Auto-generated method stub
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index aeb70e9..2c3736f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -16,6 +16,7 @@
 
 package com.android.layoutlib.bridge.android;
 
+import android.os.IBinder;
 import com.android.annotations.Nullable;
 import com.android.ide.common.rendering.api.ILayoutPullParser;
 import com.android.ide.common.rendering.api.IProjectCallback;
@@ -938,12 +939,24 @@
     }
 
     @Override
+    public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) {
+        // pass
+        return 0;
+    }
+
+    @Override
     public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3) {
         // pass
         return 0;
     }
 
     @Override
+    public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3, IBinder arg4) {
+        // pass
+        return 0;
+    }
+
+    @Override
     public int checkUriPermission(Uri arg0, String arg1, String arg2, int arg3,
             int arg4, int arg5) {
         // pass