Merge "Show misc channel with pre-upgrade fields" into oc-dev
diff --git a/api/removed.txt b/api/removed.txt
index e7b573b..f52b39a 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -73,7 +73,6 @@
     method public deprecated java.lang.String getDeviceInitializerApp();
     method public deprecated android.content.ComponentName getDeviceInitializerComponent();
     method public void setAffiliationIds(android.content.ComponentName, java.util.List<java.lang.String>);
-    field public static final deprecated int FLAG_EVICT_CE_KEY = 1; // 0x1
   }
 
 }
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 15de5c4..9f3970d 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -71,7 +71,6 @@
     method public deprecated android.os.UserHandle createAndInitializeUser(android.content.ComponentName, java.lang.String, java.lang.String, android.content.ComponentName, android.os.Bundle);
     method public deprecated android.os.UserHandle createUser(android.content.ComponentName, java.lang.String);
     method public void setAffiliationIds(android.content.ComponentName, java.util.List<java.lang.String>);
-    field public static final deprecated int FLAG_EVICT_CE_KEY = 1; // 0x1
   }
 
 }
diff --git a/api/test-removed.txt b/api/test-removed.txt
index e7b573b..f52b39a 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -73,7 +73,6 @@
     method public deprecated java.lang.String getDeviceInitializerApp();
     method public deprecated android.content.ComponentName getDeviceInitializerComponent();
     method public void setAffiliationIds(android.content.ComponentName, java.util.List<java.lang.String>);
-    field public static final deprecated int FLAG_EVICT_CE_KEY = 1; // 0x1
   }
 
 }
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 588a1bf..b60aed6 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -51,6 +51,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.IUserManager;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -347,7 +348,7 @@
 
         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
-            public void send(int code, Intent intent, String resolvedType,
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
                 try {
                     mResult.offer(intent, 5, TimeUnit.SECONDS);
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 000420f..5fedc9e 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -84,6 +84,11 @@
 
 int main(int argc, char** argv)
 {
+    // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
+    // not allowed to spawn any additional threads, but we still spawn
+    // a binder thread from userspace when we call startThreadPool().
+    // See b/36066697 for rationale
+    ProcessState::self()->setThreadPoolMaxThreadCount(0);
     ProcessState::self()->startThreadPool();
 
     const char* pname = argv[0];
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6fa0a6d..a8183f2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -976,7 +976,7 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
 
-        if (getApplicationInfo().targetSdkVersion >= O && mActivityInfo.isFixedOrientation()) {
+        if (getApplicationInfo().targetSdkVersion > O && mActivityInfo.isFixedOrientation()) {
             final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
             final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
             ta.recycle();
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0dfaf6a..e9ee1386 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -169,7 +169,8 @@
      *  Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions
      *  such as Power Save mode.
      */
-    public abstract void setPendingIntentWhitelistDuration(IIntentSender target, long duration);
+    public abstract void setPendingIntentWhitelistDuration(IIntentSender target,
+            IBinder whitelistToken, long duration);
 
     /**
      * Allow DeviceIdleController to tell us about what apps are whitelisted.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 424e783..2de205b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -65,6 +65,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -376,7 +377,9 @@
         checkMode(mode);
         if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
             if (isCredentialProtectedStorage()
-                    && !getSystemService(UserManager.class).isUserUnlocked() && !isBuggy()) {
+                    && !getSystemService(StorageManager.class).isUserKeyUnlocked(
+                            UserHandle.myUserId())
+                    && !isBuggy()) {
                 throw new IllegalStateException("SharedPreferences in credential encrypted "
                         + "storage are not available until after user is unlocked");
             }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 68fce75..9552d17 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -562,8 +562,8 @@
     void notifyLockedProfile(int userId);
     void startConfirmDeviceCredentialIntent(in Intent intent, in Bundle options);
     void sendIdleJobTrigger();
-    int sendIntentSender(in IIntentSender target, int code, in Intent intent,
-            in String resolvedType, in IIntentReceiver finishedReceiver,
+    int sendIntentSender(in IIntentSender target, in IBinder whitelistToken, int code,
+            in Intent intent, in String resolvedType, in IIntentReceiver finishedReceiver,
             in String requiredPermission, in Bundle options);
 
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 06509ae..4294eab 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -42,8 +42,10 @@
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.BadParcelableException;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -835,6 +837,22 @@
     public ArraySet<PendingIntent> allPendingIntents;
 
     /**
+     * Token identifying the notification that is applying doze/bgcheck whitelisting to the
+     * pending intents inside of it, so only those will get the behavior.
+     *
+     * @hide
+     */
+    static public IBinder whitelistToken;
+
+    /**
+     * Must be set by a process to start associating tokens with Notification objects
+     * coming in to it.  This is set by NotificationManagerService.
+     *
+     * @hide
+     */
+    static public IBinder processWhitelistToken;
+
+    /**
      * {@link #extras} key: this is the title of the notification,
      * as supplied to {@link Builder#setContentTitle(CharSequence)}.
      */
@@ -1823,6 +1841,13 @@
     {
         int version = parcel.readInt();
 
+        whitelistToken = parcel.readStrongBinder();
+        if (whitelistToken == null) {
+            whitelistToken = processWhitelistToken;
+        }
+        // Propagate this token to all pending intents that are unmarshalled from the parcel.
+        parcel.setClassCookie(PendingIntent.class, whitelistToken);
+
         when = parcel.readLong();
         creationTime = parcel.readLong();
         if (parcel.readInt() != 0) {
@@ -1929,6 +1954,7 @@
      * @hide
      */
     public void cloneInto(Notification that, boolean heavy) {
+        that.whitelistToken = this.whitelistToken;
         that.when = this.when;
         that.creationTime = this.creationTime;
         that.mSmallIcon = this.mSmallIcon;
@@ -2158,6 +2184,7 @@
     private void writeToParcelImpl(Parcel parcel, int flags) {
         parcel.writeInt(1);
 
+        parcel.writeStrongBinder(whitelistToken);
         parcel.writeLong(when);
         parcel.writeLong(creationTime);
         if (mSmallIcon == null && icon != 0) {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 704e912..bc7fcf5 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -101,21 +101,11 @@
     /**
      * @hide
      */
-    public static final int USER_LOCKED_ALLOWED = 0x00000040;
-
-    /**
-     * @hide
-     */
     public static final int USER_LOCKED_SHOW_BADGE = 0x00000080;
 
     /**
      * @hide
      */
-    public static final int USER_LOCKED_AUDIO_ATTRIBUTES = 0x00000100;
-
-    /**
-     * @hide
-     */
     public static final int[] LOCKABLE_FIELDS = new int[] {
             USER_LOCKED_PRIORITY,
             USER_LOCKED_VISIBILITY,
@@ -123,9 +113,7 @@
             USER_LOCKED_LIGHTS,
             USER_LOCKED_VIBRATION,
             USER_LOCKED_SOUND,
-            USER_LOCKED_ALLOWED,
             USER_LOCKED_SHOW_BADGE,
-            USER_LOCKED_AUDIO_ATTRIBUTES
     };
 
     private static final int DEFAULT_LIGHT_COLOR = 0;
@@ -273,6 +261,13 @@
     /**
      * @hide
      */
+    public void unlockFields(int field) {
+        mUserLockedFields &= ~field;
+    }
+
+    /**
+     * @hide
+     */
     public void setDeleted(boolean deleted) {
         mDeleted = deleted;
     }
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index dc432af..11d9b5e 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -93,6 +93,7 @@
  */
 public final class PendingIntent implements Parcelable {
     private final IIntentSender mTarget;
+    private IBinder mWhitelistToken;
 
     /** @hide */
     @IntDef(flag = true,
@@ -656,7 +657,7 @@
      *
      */
     public IntentSender getIntentSender() {
-        return new IntentSender(mTarget);
+        return new IntentSender(mTarget, mWhitelistToken);
     }
 
     /**
@@ -870,7 +871,7 @@
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
             int res = ActivityManager.getService().sendIntentSender(
-                    mTarget, code, intent, resolvedType,
+                    mTarget, mWhitelistToken, code, intent, resolvedType,
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
                             : null,
@@ -1085,7 +1086,9 @@
             = new Parcelable.Creator<PendingIntent>() {
         public PendingIntent createFromParcel(Parcel in) {
             IBinder target = in.readStrongBinder();
-            return target != null ? new PendingIntent(target) : null;
+            return target != null
+                    ? new PendingIntent(target, in.getClassCookie(PendingIntent.class))
+                    : null;
         }
 
         public PendingIntent[] newArray(int size) {
@@ -1108,31 +1111,39 @@
     }
 
     /**
-     * Convenience function for reading either a Messenger or null pointer from
-     * a Parcel.  You must have previously written the Messenger with
+     * Convenience function for reading either a PendingIntent or null pointer from
+     * a Parcel.  You must have previously written the PendingIntent with
      * {@link #writePendingIntentOrNullToParcel}.
      *
-     * @param in The Parcel containing the written Messenger.
+     * @param in The Parcel containing the written PendingIntent.
      *
-     * @return Returns the Messenger read from the Parcel, or null if null had
+     * @return Returns the PendingIntent read from the Parcel, or null if null had
      * been written.
      */
     @Nullable
     public static PendingIntent readPendingIntentOrNullFromParcel(@NonNull Parcel in) {
         IBinder b = in.readStrongBinder();
-        return b != null ? new PendingIntent(b) : null;
+        return b != null ? new PendingIntent(b, in.getClassCookie(PendingIntent.class)) : null;
     }
 
     /*package*/ PendingIntent(IIntentSender target) {
         mTarget = target;
     }
 
-    /*package*/ PendingIntent(IBinder target) {
+    /*package*/ PendingIntent(IBinder target, Object cookie) {
         mTarget = IIntentSender.Stub.asInterface(target);
+        if (cookie != null) {
+            mWhitelistToken = (IBinder)cookie;
+        }
     }
 
     /** @hide */
     public IIntentSender getTarget() {
         return mTarget;
     }
+
+    /** @hide */
+    public IBinder getWhitelistToken() {
+        return mWhitelistToken;
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 109b5bb..de80c36 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3038,13 +3038,6 @@
      */
     public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1;
 
-    /**
-     * Instead use {@link #FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY}.
-     * @removed
-     */
-    @Deprecated
-    public static final int FLAG_EVICT_CE_KEY = 1;
-
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag=true, value={FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY})
diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl
index 45c62d4..21ea2fe 100644
--- a/core/java/android/content/IIntentSender.aidl
+++ b/core/java/android/content/IIntentSender.aidl
@@ -22,6 +22,6 @@
 
 /** @hide */
 oneway interface IIntentSender {
-    void send(int code, in Intent intent, String resolvedType,
+    void send(int code, in Intent intent, String resolvedType, in IBinder whitelistToken,
             IIntentReceiver finishedReceiver, String requiredPermission, in Bundle options);
 }
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 4adb5b7..0a456b5 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -56,6 +56,7 @@
  */
 public class IntentSender implements Parcelable {
     private final IIntentSender mTarget;
+    IBinder mWhitelistToken;
 
     /**
      * Exception thrown when trying to send through a PendingIntent that
@@ -187,7 +188,7 @@
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
-            int res = ActivityManager.getService().sendIntentSender(mTarget,
+            int res = ActivityManager.getService().sendIntentSender(mTarget, mWhitelistToken,
                     code, intent, resolvedType,
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
@@ -365,6 +366,12 @@
     }
 
     /** @hide */
+    public IntentSender(IIntentSender target, IBinder whitelistToken) {
+        mTarget = target;
+        mWhitelistToken = whitelistToken;
+    }
+
+    /** @hide */
     public IntentSender(IBinder target) {
         mTarget = IIntentSender.Stub.asInterface(target);
     }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d2468d9..1a4a1e5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -157,6 +157,7 @@
             MATCH_DISABLED_COMPONENTS,
             MATCH_DISABLED_UNTIL_USED_COMPONENTS,
             MATCH_INSTANT,
+            MATCH_STATIC_SHARED_LIBRARIES,
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
     })
@@ -177,6 +178,7 @@
             MATCH_SYSTEM_ONLY,
             MATCH_UNINSTALLED_PACKAGES,
             MATCH_INSTANT,
+            MATCH_STATIC_SHARED_LIBRARIES,
             GET_DISABLED_COMPONENTS,
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
@@ -475,6 +477,16 @@
     public static final int MATCH_EXPLICITLY_VISIBLE_ONLY = 0x02000000;
 
     /**
+     * Internal {@link PackageInfo} flag: include static shared libraries.
+     * Apps that depend on static shared libs can always access the version
+     * of the lib they depend on. System/shell/root can access all shared
+     * libs regardless of dependency but need to explicitly ask for them
+     * via this flag.
+     * @hide
+     */
+    public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
+
+    /**
      * Internal flag used to indicate that a system component has done their
      * homework and verified that they correctly handle packages and components
      * that come and go over time. In particular:
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 0b0f048..f33c751 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -103,7 +103,8 @@
     }
 
     /**
-     * Return the number of indices in the array that actually have data.
+     * Returns the number of indices in the array that actually have data. Attributes with a value
+     * of @empty are included, as this is an explicit indicator.
      *
      * @throws RuntimeException if the TypedArray has already been recycled.
      */
@@ -116,7 +117,8 @@
     }
 
     /**
-     * Returns an index in the array that has data.
+     * Returns an index in the array that has data. Attributes with a value of @empty are included,
+     * as this is an explicit indicator.
      *
      * @param at The index you would like to returned, ranging from 0 to
      *           {@link #getIndexCount()}.
@@ -1017,7 +1019,7 @@
      * @param outValue TypedValue object in which to place the attribute's
      *                 data.
      *
-     * @return {@code true} if the value was retrieved, false otherwise.
+     * @return {@code true} if the value was retrieved and not @empty, {@code false} otherwise.
      * @throws RuntimeException if the TypedArray has already been recycled.
      */
     public boolean getValue(@StyleableRes int index, TypedValue outValue) {
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 65025fb..8d85880 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -1548,6 +1548,7 @@
         Parcel p = Parcel.obtain();
         p.setDataPosition(0);
         p.appendFrom(parcel, offset, length);
+        p.adoptClassCookies(parcel);
         if (DEBUG) Log.d(TAG, "Retrieving "  + Integer.toHexString(System.identityHashCode(this))
                 + ": " + length + " bundle bytes starting at " + offset);
         p.setDataPosition(0);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index c5c743b..38a5395 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -204,6 +204,8 @@
     private boolean mOwnsNativeParcelObject;
     private long mNativeSize;
 
+    private ArrayMap<Class, Object> mClassCookies;
+
     private RuntimeException mStack;
 
     private static final int POOL_SIZE = 6;
@@ -497,6 +499,24 @@
         return nativeCompareData(mNativePtr, other.mNativePtr);
     }
 
+    /** @hide */
+    public final void setClassCookie(Class clz, Object cookie) {
+        if (mClassCookies == null) {
+            mClassCookies = new ArrayMap<>();
+        }
+        mClassCookies.put(clz, cookie);
+    }
+
+    /** @hide */
+    public final Object getClassCookie(Class clz) {
+        return mClassCookies != null ? mClassCookies.get(clz) : null;
+    }
+
+    /** @hide */
+    public final void adoptClassCookies(Parcel from) {
+        mClassCookies = from.mClassCookies;
+    }
+
     /**
      * Report whether the parcel contains any marshalled file descriptors.
      */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cf44c7d..ba41933 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6946,6 +6946,13 @@
         public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg";
 
         /**
+         * Whether the launcher should show any notification badges.
+         * The value is boolean (1 or 0).
+         * @hide
+         */
+        public static final String NOTIFICATION_BADGING = "notification_badging";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -7040,7 +7047,8 @@
             AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
             ASSIST_GESTURE_ENABLED,
             ASSIST_GESTURE_SENSITIVITY,
-            VR_DISPLAY_MODE
+            VR_DISPLAY_MODE,
+            NOTIFICATION_BADGING
         };
 
         /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d8eb950..07258fc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2858,6 +2858,14 @@
      */
     static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
 
+    /**
+     * Flag indicating that when layout is completed we should notify
+     * that the view was entered for autofill purposes. To minimize
+     * showing autofill for views not visible to the user we evaluate
+     * user visibility which cannot be done until the view is laid out.
+     */
+    static final int PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT = 0x4000;
+
     static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
 
     static final int SCROLL_INDICATORS_NONE = 0x0000;
@@ -6496,7 +6504,7 @@
 
             if (mParent != null) {
                 mParent.requestChildFocus(this, this);
-                setFocusedInCluster();
+                updateFocusedInCluster(oldFocus, direction);
             }
 
             if (mAttachInfo != null) {
@@ -6845,8 +6853,15 @@
         if (isAutofillable() && isAttachedToWindow()) {
             AutofillManager afm = getAutofillManager();
             if (afm != null) {
-                if (enter && hasWindowFocus() && isFocused() && isVisibleToUser()) {
-                    afm.notifyViewEntered(this);
+                if (enter && hasWindowFocus() && isFocused()) {
+                    // We have not been laid out yet, hence cannot evaluate
+                    // whether this view is visible to the user, we will do
+                    // the evaluation once layout is complete.
+                    if (!isLaidOut()) {
+                        mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
+                    } else if (isVisibleToUser()) {
+                        afm.notifyViewEntered(this);
+                    }
                 } else if (!hasWindowFocus() || !isFocused()) {
                     afm.notifyViewExited(this);
                 }
@@ -9912,22 +9927,47 @@
      * @hide
      */
     public final void setFocusedInCluster() {
-        View top = findKeyboardNavigationCluster();
-        if (top == this) {
+        setFocusedInCluster(findKeyboardNavigationCluster());
+    }
+
+    private void setFocusedInCluster(View cluster) {
+        if (this instanceof ViewGroup) {
+            ((ViewGroup) this).mFocusedInCluster = null;
+        }
+        if (cluster == this) {
             return;
         }
         ViewParent parent = mParent;
         View child = this;
         while (parent instanceof ViewGroup) {
-            ((ViewGroup) parent).setFocusedInCluster(child);
-            if (parent == top) {
-                return;
+            ((ViewGroup) parent).mFocusedInCluster = child;
+            if (parent == cluster) {
+                break;
             }
             child = (View) parent;
             parent = parent.getParent();
         }
     }
 
+    private void updateFocusedInCluster(View oldFocus, @FocusDirection int direction) {
+        if (oldFocus != null) {
+            View oldCluster = oldFocus.findKeyboardNavigationCluster();
+            View cluster = findKeyboardNavigationCluster();
+            if (oldCluster != cluster) {
+                // Going from one cluster to another, so save last-focused.
+                // This covers cluster jumps because they are always FOCUS_DOWN
+                oldFocus.setFocusedInCluster(oldCluster);
+                if (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD) {
+                    // This is a result of ordered navigation so consider navigation through
+                    // the previous cluster "complete" and clear its last-focused memory.
+                    if (oldFocus.mParent instanceof ViewGroup) {
+                        ((ViewGroup) oldFocus.mParent).clearFocusedInCluster(oldFocus);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Returns whether this View should receive focus when the focus is restored for the view
      * hierarchy containing this view.
@@ -19305,6 +19345,11 @@
 
         mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
         mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
+
+        if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
+            mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
+            notifyEnterOrExitForAutoFillIfNeeded(true);
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 18c1b8c..50593f2 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -142,7 +142,7 @@
     // that is or contains a default-focus view.
     private View mDefaultFocus;
     // The last child of this ViewGroup which held focus within the current cluster
-    private View mFocusedInCluster;
+    View mFocusedInCluster;
 
     /**
      * A Transformation used when drawing children, to
@@ -806,10 +806,6 @@
         return mDefaultFocus != null || super.hasDefaultFocus();
     }
 
-    void setFocusedInCluster(View child) {
-        mFocusedInCluster = child;
-    }
-
     /**
      * Removes {@code child} (and associated focusedInCluster chain) from the cluster containing
      * it.
@@ -825,8 +821,11 @@
         ViewParent parent = this;
         do {
             ((ViewGroup) parent).mFocusedInCluster = null;
+            if (parent == top) {
+                break;
+            }
             parent = parent.getParent();
-        } while (parent != top && parent instanceof ViewGroup);
+        } while (parent instanceof ViewGroup);
     }
 
     @Override
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 1f3be84..32fae73 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -140,4 +140,14 @@
     @WorkerThread
     LinksInfo getLinks(
             @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales);
+
+    /**
+     * Logs a TextClassifier event.
+     *
+     * @param source the text classifier used to generate this event
+     * @param event the text classifier related event
+     * @hide
+     */
+    @WorkerThread
+    default void logEvent(String source, String event) {}
 }
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 7362c70..5f72fc7 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,6 +40,7 @@
 import android.widget.TextViewMetrics;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.Preconditions;
 
 import java.io.File;
@@ -77,6 +78,8 @@
 
     private final Context mContext;
 
+    private final MetricsLogger mMetricsLogger = new MetricsLogger();
+
     private final Object mSmartSelectionLock = new Object();
     @GuardedBy("mSmartSelectionLock") // Do not access outside this lock.
     private Map<Locale, String> mModelFilePaths;
@@ -105,7 +108,8 @@
                 if (start <= end
                         && start >= 0 && end <= string.length()
                         && start <= selectionStartIndex && end >= selectionEndIndex) {
-                    final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
+                    final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end)
+                            .setLogSource(LOG_TAG);
                     final SmartSelection.ClassificationResult[] results =
                             smartSelection.classifyText(
                                     string, start, end,
@@ -173,6 +177,13 @@
         return TextClassifier.NO_OP.getLinks(text, linkMask, defaultLocales);
     }
 
+    @Override
+    public void logEvent(String source, String event) {
+        if (LOG_TAG.equals(source)) {
+            mMetricsLogger.count(event, 1);
+        }
+    }
+
     private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
         synchronized (mSmartSelectionLock) {
             localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 3172c13..9a66693 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -34,13 +34,16 @@
     private final int mEndIndex;
     @NonNull private final EntityConfidence<String> mEntityConfidence;
     @NonNull private final List<String> mEntities;
+    @NonNull private final String mLogSource;
 
     private TextSelection(
-            int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence) {
+            int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence,
+            @NonNull String logSource) {
         mStartIndex = startIndex;
         mEndIndex = endIndex;
         mEntityConfidence = new EntityConfidence<>(entityConfidence);
         mEntities = mEntityConfidence.getEntities();
+        mLogSource = logSource;
     }
 
     /**
@@ -87,6 +90,14 @@
         return mEntityConfidence.getConfidenceScore(entity);
     }
 
+    /**
+     * Returns a tag for the source classifier used to generate this result.
+     * @hide
+     */
+    public String getSourceClassifier() {
+        return mLogSource;
+    }
+
     @Override
     public String toString() {
         return String.format("TextSelection {%d, %d, %s}",
@@ -102,6 +113,7 @@
         private final int mEndIndex;
         @NonNull private final EntityConfidence<String> mEntityConfidence =
                 new EntityConfidence<>();
+        @NonNull private String mLogSource = "";
 
         /**
          * Creates a builder used to build {@link TextSelection} objects.
@@ -131,10 +143,19 @@
         }
 
         /**
+         * Sets a tag for the source classifier used to generate this result.
+         * @hide
+         */
+        Builder setLogSource(@NonNull String logSource) {
+            mLogSource = Preconditions.checkNotNull(logSource);
+            return this;
+        }
+
+        /**
          * Builds and returns {@link TextSelection} object.
          */
         public TextSelection build() {
-            return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence);
+            return new TextSelection(mStartIndex, mEndIndex, mEntityConfidence, mLogSource);
         }
     }
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index b0d6395..bb658c1 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3925,6 +3925,8 @@
 
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            getSelectionActionModeHelper().onSelectionAction();
+
             if (mProcessTextIntentActionsHandler.performMenuItemAction(item)) {
                 return true;
             }
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 16a1087..89182b0 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -56,13 +56,14 @@
     private TextClassification mTextClassification;
     private AsyncTask mTextClassificationAsyncTask;
 
-    private final SelectionInfo mSelectionInfo = new SelectionInfo();
+    private final SelectionTracker mSelectionTracker;
 
     SelectionActionModeHelper(@NonNull Editor editor) {
         mEditor = Preconditions.checkNotNull(editor);
         final TextView textView = mEditor.getTextView();
         mTextClassificationHelper = new TextClassificationHelper(
                 textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales());
+        mSelectionTracker = new SelectionTracker(textView.getTextClassifier());
     }
 
     public void startActionModeAsync(boolean adjustSelection) {
@@ -99,8 +100,13 @@
         }
     }
 
+    public void onSelectionAction() {
+        mSelectionTracker.onSelectionAction(mTextClassificationHelper.getClassifierTag());
+    }
+
     public boolean resetSelection(int textIndex) {
-        if (mSelectionInfo.resetSelection(textIndex, mEditor)) {
+        if (mSelectionTracker.resetSelection(
+                textIndex, mEditor, mTextClassificationHelper.getClassifierTag())) {
             invalidateActionModeAsync();
             return true;
         }
@@ -113,7 +119,7 @@
     }
 
     public void onDestroyActionMode() {
-        mSelectionInfo.onSelectionDestroyed();
+        mSelectionTracker.onSelectionDestroyed();
         cancelAsyncTask();
     }
 
@@ -137,7 +143,7 @@
     private void startActionMode(@Nullable SelectionResult result) {
         final TextView textView = mEditor.getTextView();
         final CharSequence text = textView.getText();
-        mSelectionInfo.setOriginalSelection(
+        mSelectionTracker.setOriginalSelection(
                 textView.getSelectionStart(), textView.getSelectionEnd());
         if (result != null && text instanceof Spannable) {
             Selection.setSelection((Spannable) text, result.mStart, result.mEnd);
@@ -151,7 +157,8 @@
                 controller.show();
             }
             if (result != null) {
-                mSelectionInfo.onSelectionStarted(result.mStart, result.mEnd);
+                mSelectionTracker.onSelectionStarted(
+                        result.mStart, result.mEnd, mTextClassificationHelper.getClassifierTag());
             }
         }
         mEditor.setRestartActionModeOnNextRefresh(false);
@@ -165,7 +172,9 @@
             actionMode.invalidate();
         }
         final TextView textView = mEditor.getTextView();
-        mSelectionInfo.onSelectionUpdated(textView.getSelectionStart(), textView.getSelectionEnd());
+        mSelectionTracker.onSelectionUpdated(
+                textView.getSelectionStart(), textView.getSelectionEnd(),
+                mTextClassificationHelper.getClassifierTag());
         mTextClassificationAsyncTask = null;
     }
 
@@ -177,49 +186,111 @@
     }
 
     /**
-     * Holds information about the selection and uses it to decide on whether or not to update
-     * the selection when resetSelection is called.
-     * The expected UX here is to allow the user to select a word inside of the "smart selection" on
-     * a single tap.
+     * Tracks and logs smart selection changes.
+     * It is important to trigger this object's methods at the appropriate event so that it tracks
+     * smart selection events appropriately.
      */
-    private static final class SelectionInfo {
+    private static final class SelectionTracker {
+
+        // Log event: Smart selection happened.
+        private static final String LOG_EVENT_MULTI_SELECTION =
+                "textClassifier_multiSelection";
+
+        // Log event: Smart selection acted upon.
+        private static final String LOG_EVENT_MULTI_SELECTION_ACTION =
+                "textClassifier_multiSelection_action";
+
+        // Log event: Smart selection was reset to original selection.
+        private static final String LOG_EVENT_MULTI_SELECTION_RESET =
+                "textClassifier_multiSelection_reset";
+
+        // Log event: Smart selection was user modified.
+        private static final String LOG_EVENT_MULTI_SELECTION_MODIFIED =
+                "textClassifier_multiSelection_modified";
+
+        private final TextClassifier mClassifier;
 
         private int mOriginalStart;
         private int mOriginalEnd;
         private int mSelectionStart;
         private int mSelectionEnd;
 
-        private boolean mResetOriginal;
+        private boolean mSmartSelectionActive;
 
+        SelectionTracker(TextClassifier classifier) {
+            mClassifier = classifier;
+        }
+
+        /**
+         * Called to initialize the original selection before smart selection is triggered.
+         */
         public void setOriginalSelection(int selectionStart, int selectionEnd) {
             mOriginalStart = selectionStart;
             mOriginalEnd = selectionEnd;
-            mResetOriginal = false;
+            mSmartSelectionActive = false;
         }
 
-        public void onSelectionStarted(int selectionStart, int selectionEnd) {
-            // Set the reset flag to true if the selection changed.
+        /**
+         * Called when selection action mode is started.
+         * If the selection indices are different from the original selection indices, we have a
+         * smart selection.
+         */
+        public void onSelectionStarted(int selectionStart, int selectionEnd, String logTag) {
             mSelectionStart = selectionStart;
             mSelectionEnd = selectionEnd;
-            mResetOriginal = mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
+            // If the started selection is different from the original selection, we have a
+            // smart selection.
+            mSmartSelectionActive =
+                    mSelectionStart != mOriginalStart || mSelectionEnd != mOriginalEnd;
+            if (mSmartSelectionActive) {
+                mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION);
+            }
         }
 
-        public void onSelectionUpdated(int selectionStart, int selectionEnd) {
-            // If the selection did not change, maintain the reset state. Otherwise, disable reset.
-            mResetOriginal &= selectionStart == mSelectionStart && selectionEnd == mSelectionEnd;
+        /**
+         * Called when selection bounds change.
+         */
+        public void onSelectionUpdated(int selectionStart, int selectionEnd, String logTag) {
+            final boolean selectionChanged =
+                    selectionStart != mSelectionStart || selectionEnd != mSelectionEnd;
+            if (selectionChanged) {
+                if (mSmartSelectionActive) {
+                    mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_MODIFIED);
+                }
+                mSmartSelectionActive = false;
+            }
         }
 
+        /**
+         * Called when the selection action mode is destroyed.
+         */
         public void onSelectionDestroyed() {
-            mResetOriginal = false;
+            mSmartSelectionActive = false;
         }
 
-        public boolean resetSelection(int textIndex, Editor editor) {
+        /**
+         * Logs if the action was taken on a smart selection.
+         */
+        public void onSelectionAction(String logTag) {
+            if (mSmartSelectionActive) {
+                mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_ACTION);
+            }
+        }
+
+        /**
+         * Returns true if the current smart selection should be reset to normal selection based on
+         * information that has been recorded about the original selection and the smart selection.
+         * The expected UX here is to allow the user to select a word inside of the smart selection
+         * on a single tap.
+         */
+        public boolean resetSelection(int textIndex, Editor editor, String logTag) {
             final CharSequence text = editor.getTextView().getText();
-            if (mResetOriginal
+            if (mSmartSelectionActive
                     && textIndex >= mSelectionStart && textIndex <= mSelectionEnd
                     && text instanceof Spannable) {
                 // Only allow a reset once.
-                mResetOriginal = false;
+                mSmartSelectionActive = false;
+                mClassifier.logEvent(logTag, LOG_EVENT_MULTI_SELECTION_RESET);
                 return editor.selectCurrentWord();
             }
             return false;
@@ -301,6 +372,7 @@
         /** End index relative to mText. */
         private int mSelectionEnd;
         private LocaleList mLocales;
+        private String mClassifierTag = "";
 
         /** Trimmed text starting from mTrimStart in mText. */
         private CharSequence mTrimmedText;
@@ -364,9 +436,14 @@
                     mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
             mSelectionStart = Math.max(0, sel.getSelectionStartIndex() + mTrimStart);
             mSelectionEnd = Math.min(mText.length(), sel.getSelectionEndIndex() + mTrimStart);
+            mClassifierTag = sel.getSourceClassifier();
             return classifyText();
         }
 
+        String getClassifierTag() {
+            return mClassifierTag;
+        }
+
         private void trimText() {
             mTrimStart = Math.max(0, mSelectionStart - TRIM_DELTA);
             final int referenceEnd = Math.min(mText.length(), mSelectionEnd + TRIM_DELTA);
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index fef85da..797cf2b 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -40,6 +40,7 @@
     public static String UPDATES = "UPDATES";
     public static String NETWORK_STATUS = "NETWORK_STATUS";
     public static String NETWORK_ALERTS = "NETWORK_ALERTS";
+    public static String NETWORK_AVAILABLE = "NETWORK_AVAILABLE";
     public static String VPN = "VPN";
     public static String DEVICE_ADMIN = "DEVICE_ADMIN";
     public static String ALERTS = "ALERTS";
@@ -99,6 +100,11 @@
         channelsList.add(networkAlertsChannel);
 
         channelsList.add(new NotificationChannel(
+                NETWORK_AVAILABLE,
+                context.getString(R.string.notification_channel_network_available),
+                NotificationManager.IMPORTANCE_LOW));
+
+        channelsList.add(new NotificationChannel(
                 VPN,
                 context.getString(R.string.notification_channel_vpn),
                 NotificationManager.IMPORTANCE_LOW));
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 748272600..6b18591 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -9812,10 +9812,9 @@
                     }
                 });
 
-        // TODO: STOPSHIP, remove the "true" below after b/34961340 is fixed
-        if (DEBUG_ENERGY_CPU || true) {
-            Slog.d(TAG, "Reading cpu stats took " + (mClocks.elapsedRealtime() - startTimeMs) +
-                    " ms");
+        final long elapse = (mClocks.elapsedRealtime() - startTimeMs);
+        if (DEBUG_ENERGY_CPU || (elapse >= 100)) {
+            Slog.d(TAG, "Reading cpu stats took " + elapse + " ms");
         }
 
         if (mOnBatteryInternal && numWakelocks > 0) {
diff --git a/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java b/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java
index b5915aa..aa56e93 100644
--- a/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java
+++ b/core/java/com/android/internal/os/KernelMemoryBandwidthStats.java
@@ -8,6 +8,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.BufferedReader;
+import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
 
@@ -24,14 +25,25 @@
 public class KernelMemoryBandwidthStats {
     private static final String TAG = "KernelMemoryBandwidthStats";
 
-    final protected LongSparseLongArray mBandwidthEntries = new LongSparseLongArray();
     private static final String mSysfsFile = "/sys/kernel/memory_state_time/show_stat";
     private static final boolean DEBUG = false;
 
+    protected final LongSparseLongArray mBandwidthEntries = new LongSparseLongArray();
+    private boolean mStatsDoNotExist = false;
+
     public void updateStats() {
+        if (mStatsDoNotExist) {
+            // Skip reading.
+            return;
+        }
+
         StrictMode.ThreadPolicy policy = StrictMode.allowThreadDiskReads();
         try (BufferedReader reader = new BufferedReader(new FileReader(mSysfsFile))) {
             parseStats(reader);
+        } catch (FileNotFoundException e) {
+            Slog.w(TAG, "No kernel memory bandwidth stats available");
+            mBandwidthEntries.clear();
+            mStatsDoNotExist = true;
         } catch (IOException e) {
             Slog.e(TAG, "Failed to read memory bandwidth: " + e.getMessage());
             mBandwidthEntries.clear();
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index ea40fd5..8b73daf 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -503,6 +503,7 @@
     SettingProto demo_user_setup_complete = 165;
     SettingProto instant_apps_enabled = 166;
     SettingProto device_paired = 167;
+    SettingProto notification_badging = 168;
 }
 
 message SystemSettingsProto {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b7e8467..6640102 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1013,6 +1013,9 @@
     <!-- Is the notification LED intrusive? Used to decide if there should be a disable option -->
     <bool name="config_intrusiveNotificationLed">false</bool>
 
+    <!-- De we do icon badges? Used to decide if there should be a disable option-->
+    <bool name="config_notificationBadging">true</bool>
+
     <!-- Default value for LED off time when the battery is low on charge in miliseconds -->
     <integer name="config_notificationsBatteryLedOff">2875</integer>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cb1851b..98356a2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -629,6 +629,9 @@
     <!-- Text shown when viewing channel settings for notifications related to network alerts -->
     <string name="notification_channel_network_alerts">Network alerts</string>
 
+    <!-- Text shown when viewing the channel settings for notification about open nearby wireless networks. -->
+    <string name="notification_channel_network_available">Network available</string>
+
     <!-- Text shown when viewing channel settings for notifications related to vpn status -->
     <string name="notification_channel_vpn">VPN status</string>
 
@@ -2993,6 +2996,17 @@
     <!-- If there is ever a ringtone set for some setting, but that ringtone can no longer be resolved, t his is shown instead.  For example, if the ringtone was on a SD card and it had been removed, this woudl be shown for ringtones on that SD card. -->
     <string name="ringtone_unknown">Unknown</string>
 
+    <!-- A notification is shown when there are open wireless networks nearby.  This is the notification's title. -->
+    <plurals name="wifi_available">
+        <item quantity="one">Wi-Fi network available</item>
+        <item quantity="other">Wi-Fi networks available</item>
+    </plurals>
+    <!-- A notification is shown when there are open wireless networks nearby.  This is the notification's message. -->
+    <plurals name="wifi_available_detailed">
+        <item quantity="one">Open Wi-Fi network available</item>
+        <item quantity="other">Open Wi-Fi networks available</item>
+    </plurals>
+
     <!-- A notification is shown when a wifi captive portal network is detected.  This is the notification's title. -->
     <string name="wifi_available_sign_in">Sign in to Wi-Fi network</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 31d13c9..74779f3 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1861,6 +1861,8 @@
   <java-symbol type="layout" name="safe_mode" />
   <java-symbol type="layout" name="simple_list_item_2_single_choice" />
   <java-symbol type="layout" name="app_error_dialog" />
+  <java-symbol type="plurals" name="wifi_available" />
+  <java-symbol type="plurals" name="wifi_available_detailed" />
   <java-symbol type="string" name="accessibility_binding_label" />
   <java-symbol type="string" name="adb_active_notification_message" />
   <java-symbol type="string" name="adb_active_notification_title" />
@@ -2087,6 +2089,7 @@
   <java-symbol type="string" name="config_wifi_tether_enable" />
   <java-symbol type="integer" name="config_wifi_wakeup_available" />
   <java-symbol type="bool" name="config_intrusiveNotificationLed" />
+  <java-symbol type="bool" name="config_notificationBadging" />
   <java-symbol type="dimen" name="preference_fragment_padding_bottom" />
   <java-symbol type="dimen" name="preference_fragment_padding_side" />
   <java-symbol type="drawable" name="expander_ic_maximized" />
@@ -2969,6 +2972,7 @@
   <java-symbol type="string" name="notification_channel_updates" />
   <java-symbol type="string" name="notification_channel_network_status" />
   <java-symbol type="string" name="notification_channel_network_alerts" />
+  <java-symbol type="string" name="notification_channel_network_available" />
   <java-symbol type="string" name="notification_channel_vpn" />
   <java-symbol type="string" name="notification_channel_device_admin" />
   <java-symbol type="string" name="notification_channel_alerts" />
diff --git a/core/tests/coretests/src/android/content/pm/AppCacheTest.java b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
index 1567046..15dbddf 100644
--- a/core/tests/coretests/src/android/content/pm/AppCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/AppCacheTest.java
@@ -25,11 +25,11 @@
 import android.os.ServiceManager;
 import android.os.StatFs;
 import android.os.UserHandle;
+import android.support.test.filters.LargeTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.filters.Suppress;
 import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Log;
 
 import java.io.File;
@@ -48,14 +48,14 @@
     public final long WAIT_TIME_INCR=10*1000;
     private static final long THRESHOLD=5;
     private static final long ACTUAL_THRESHOLD=10;
-    
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         if(localLOGV) Log.i(TAG, "Cleaning up cache directory first");
         cleanUpCacheDirectory();
     }
-    
+
     void cleanUpDirectory(File pDir, String dirName) {
        File testDir = new File(pDir,  dirName);
        if(!testDir.exists()) {
@@ -72,13 +72,13 @@
         }
         testDir.delete();
     }
-    
+
     void cleanUpCacheDirectory() {
         File testDir = mContext.getCacheDir();
         if(!testDir.exists()) {
             return;
         }
-        
+
          String fList[] = testDir.list();
          if(fList == null) {
              testDir.delete();
@@ -93,7 +93,7 @@
              }
          }
      }
-    
+
     @SmallTest
     public void testDeleteAllCacheFiles() {
         String testName="testDeleteAllCacheFiles";
@@ -160,9 +160,9 @@
                     +(blks1-blks3));
         }
     }
-    
+
     /**
-     * This method opens an output file writes to it, opens the same file as an input 
+     * This method opens an output file writes to it, opens the same file as an input
      * stream, reads the contents and verifies the data that was written earlier can be read
      */
     public void openOutFileInAppFilesDir(File pFile, String pFileOut) {
@@ -180,7 +180,7 @@
             failStr(e.getMessage());
         } catch (IOException e) {
             failStr(e.getMessage());
-        } 
+        }
         int count = pFileOut.getBytes().length;
         byte[] buffer = new byte[count];
         try {
@@ -194,8 +194,8 @@
         }
         String str = new String(buffer);
         assertEquals(str, pFileOut);
-    } 
-    
+    }
+
     /*
      * This test case verifies that output written to a file
      * using Context.openFileOutput has executed successfully.
@@ -215,7 +215,7 @@
             failStr(e);
         }
     }
-    
+
     @SmallTest
     public void testAppCacheCreateFile() {
         String fileName = "testFile1.txt";
@@ -225,7 +225,7 @@
         openOutFileInAppFilesDir(file, fileOut);
         cleanUpCacheDirectory();
     }
-    
+
     @MediumTest
     public void testAppCreateCacheFiles() {
         File cacheDir = mContext.getCacheDir();
@@ -261,7 +261,7 @@
             }
         }
     }
-    
+
     byte[] getBuffer() {
         String sbuffer = "a";
         for(int i = 0; i < 10; i++) {
@@ -360,7 +360,7 @@
         }
         assertTrue("Files should have been removed", removedFlag);
     }
-    
+
     //createTestFiles(new File(super.getContext().getCacheDir(), "testtmp", "dir", 3)
     void createTestFiles1(File cacheDir, String testFilePrefix, int numTestFiles) {
         byte buffer[] = getBuffer();
@@ -439,7 +439,7 @@
             }
         }
     }
-    
+
     class PackageDataObserver extends IPackageDataObserver.Stub {
         public boolean retValue = false;
         private boolean doneFlag = false;
@@ -455,11 +455,11 @@
             return doneFlag;
         }
     }
-    
+
     IPackageManager getPm() {
         return  IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
     }
-    
+
     boolean invokePMDeleteAppCacheFiles() throws Exception {
         try {
             String packageName = mContext.getPackageName();
@@ -485,7 +485,7 @@
             return false;
         }
     }
-    
+
     boolean invokePMFreeApplicationCache(long idealStorageSize) throws Exception {
         try {
             String packageName = mContext.getPackageName();
@@ -512,7 +512,7 @@
         }
     }
 
-    boolean invokePMFreeStorage(long idealStorageSize, FreeStorageReceiver r, 
+    boolean invokePMFreeStorage(long idealStorageSize, FreeStorageReceiver r,
             PendingIntent pi) throws Exception {
         try {
             // Spin lock waiting for call back
@@ -536,7 +536,7 @@
             return false;
         }
     }
-    
+
     @LargeTest
     public void testDeleteAppCacheFiles() throws Exception {
         String testName="testDeleteAppCacheFiles";
@@ -551,7 +551,7 @@
         public boolean retValue = false;
         public PackageStats stats;
         private boolean doneFlag = false;
-        
+
         public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
                 throws RemoteException {
             synchronized(this) {
@@ -565,7 +565,7 @@
             return doneFlag;
         }
     }
-    
+
     public PackageStats invokePMGetPackageSizeInfo() throws Exception {
         try {
             String packageName = mContext.getPackageName();
@@ -593,7 +593,7 @@
             return null;
         }
     }
-    
+
     @SmallTest
     public void testGetPackageSizeInfo() throws Exception {
         String testName="testGetPackageSizeInfo";
@@ -603,7 +603,7 @@
         if(localLOGV) Log.i(TAG, "code="+stats.codeSize+", data="+stats.dataSize+
                 ", cache="+stats.cacheSize);
     }
-    
+
     @SmallTest
     public void testGetSystemSharedLibraryNames() throws Exception {
         try {
@@ -615,17 +615,17 @@
             }
         } catch (RemoteException e) {
             fail("Failed invoking getSystemSharedLibraryNames with exception:" + e);
-        }   
+        }
     }
-    
+
     class FreeStorageReceiver extends BroadcastReceiver {
         public static final String ACTION_FREE = "com.android.unit_tests.testcallback";
         private boolean doneFlag = false;
-        
+
         public boolean isDone() {
             return doneFlag;
         }
-        
+
         @Override
         public void onReceive(Context context, Intent intent) {
             if(intent.getAction().equalsIgnoreCase(ACTION_FREE)) {
@@ -637,7 +637,7 @@
             }
         }
     }
-    
+
     // TODO: flaky test, omit from LargeTest for now
     //@LargeTest
     public void testFreeStorage() throws Exception {
@@ -664,10 +664,10 @@
         if(localLOGV || TRACKING) Log.i(TAG, "Available blocks after freeing cache"+blks3);
         assertEquals(receiver.getResultCode(), 1);
         mContext.unregisterReceiver(receiver);
-        // Verify result  
+        // Verify result
         verifyTestFiles1(cacheDir, "testtmpdir", 5);
     }
-    
+
     /* utility method used to create observer and check async call back from PackageManager.
      * ClearApplicationUserData
      */
@@ -696,7 +696,7 @@
             return false;
         }
     }
-    
+
     void verifyUserDataCleared(File pDir) {
         if(localLOGV) Log.i(TAG, "Verifying "+pDir);
         if(pDir == null) {
@@ -717,7 +717,7 @@
             fail(pDir+" should be empty or contain only lib subdirectory. Found "+fileList[i]);
         }
     }
-    
+
     File getDataDir() {
         try {
             ApplicationInfo appInfo = getPm().getApplicationInfo(mContext.getPackageName(), 0,
@@ -727,7 +727,8 @@
             throw new RuntimeException("Pacakge manager dead", e);
         }
     }
-    
+
+    @Suppress
     @LargeTest
     public void testClearApplicationUserDataWithTestData() throws Exception {
         File cacheDir = mContext.getCacheDir();
@@ -740,14 +741,16 @@
         //confirm files dont exist
         verifyUserDataCleared(getDataDir());
     }
-    
+
+    @Suppress
     @SmallTest
     public void testClearApplicationUserDataWithNoTestData() throws Exception {
         assertTrue(invokePMClearApplicationUserData());
         //confirm files dont exist
         verifyUserDataCleared(getDataDir());
     }
-    
+
+    @Suppress
     @LargeTest
     public void testClearApplicationUserDataNoObserver() throws Exception {
         getPm().clearApplicationUserData(mContext.getPackageName(), null, UserHandle.myUserId());
@@ -756,5 +759,5 @@
         //confirm files dont exist
         verifyUserDataCleared(getDataDir());
     }
-    
+
 }
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 86ab3dc..344f3c8 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -181,9 +181,6 @@
     <allow-in-power-save package="com.android.cellbroadcastreceiver" />
     <allow-in-power-save package="com.android.shell" />
 
-    <!-- STOPSHIP(b/36856786): Revert this once it is fixed properly -->
-    <allow-in-power-save package="com.google.android.apps.enterprise.dmagent" />
-
     <!-- These are the packages that are white-listed to be able to run as system user -->
     <system-user-whitelisted-app package="com.android.settings" />
 
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index 2771ade..60e3845 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -44,8 +44,7 @@
 };
 
 class BagAttributeFinder
-    : public BackTrackingAttributeFinder<BagAttributeFinder,
-                                         const ResTable::bag_entry*> {
+    : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
  public:
   BagAttributeFinder(const ResTable::bag_entry* start,
                      const ResTable::bag_entry* end)
@@ -76,8 +75,7 @@
   uint32_t def_style_bag_type_set_flags = 0;
   if (def_style_attr != 0) {
     Res_value value;
-    if (theme->getAttribute(def_style_attr, &value,
-                            &def_style_bag_type_set_flags) >= 0) {
+    if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
       if (value.dataType == Res_value::TYPE_REFERENCE) {
         def_style_res = value.data;
       }
@@ -127,18 +125,14 @@
         ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType,
               value.data);
       }
-    }
-
-    if (value.dataType == Res_value::TYPE_NULL) {
-      const ResTable::bag_entry* const def_style_entry =
-          def_style_attr_finder.Find(cur_ident);
+    } else {
+      const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident);
       if (def_style_entry != def_style_end) {
         block = def_style_entry->stringBlock;
         type_set_flags = def_style_type_set_flags;
         value = def_style_entry->map.value;
         if (kDebugStyles) {
-          ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType,
-                value.data);
+          ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
       }
     }
@@ -146,29 +140,24 @@
     uint32_t resid = 0;
     if (value.dataType != Res_value::TYPE_NULL) {
       // Take care of resolving the found resource to its final value.
-      ssize_t new_block = theme->resolveAttributeReference(
-          &value, block, &resid, &type_set_flags, &config);
+      ssize_t new_block =
+          theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
       if (new_block >= 0) block = new_block;
       if (kDebugStyles) {
-        ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType,
-              value.data);
+        ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
       }
-    } else {
+    } else if (value.data != Res_value::DATA_NULL_EMPTY) {
       // If we still don't have a value for this attribute, try to find
       // it in the theme!
-      ssize_t new_block =
-          theme->getAttribute(cur_ident, &value, &type_set_flags);
+      ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
       if (new_block >= 0) {
         if (kDebugStyles) {
-          ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType,
-                value.data);
+          ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
-        new_block = res.resolveReference(&value, new_block, &resid,
-                                         &type_set_flags, &config);
+        new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
         if (new_block >= 0) block = new_block;
         if (kDebugStyles) {
-          ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType,
-                value.data);
+          ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
       }
     }
@@ -184,8 +173,7 @@
     }
 
     if (kDebugStyles) {
-      ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident,
-            value.dataType, value.data);
+      ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
     }
 
     // Write the final value back to Java.
@@ -198,7 +186,8 @@
     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
     out_values[STYLE_DENSITY] = config.density;
 
-    if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) {
+    if (out_indices != nullptr &&
+        (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
       indices_idx++;
       out_indices[indices_idx] = ii;
     }
@@ -247,8 +236,7 @@
     ssize_t idx = xml_parser->indexOfStyle();
     if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
       if (value.dataType == value.TYPE_ATTRIBUTE) {
-        if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) <
-            0) {
+        if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) {
           value.dataType = Res_value::TYPE_NULL;
         }
       }
@@ -318,41 +306,34 @@
       // We found the attribute we were looking for.
       xml_parser->getAttributeValue(xml_attr_idx, &value);
       if (kDebugStyles) {
-        ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType,
-              value.data);
+        ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
       }
     }
 
-    if (value.dataType == Res_value::TYPE_NULL) {
-      // Walk through the style class values looking for the requested
-      // attribute.
-      const ResTable::bag_entry* const style_attr_entry =
-          style_attr_finder.Find(cur_ident);
+    if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+      // Walk through the style class values looking for the requested attribute.
+      const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident);
       if (style_attr_entry != style_attr_end) {
         // We found the attribute we were looking for.
         block = style_attr_entry->stringBlock;
         type_set_flags = style_type_set_flags;
         value = style_attr_entry->map.value;
         if (kDebugStyles) {
-          ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType,
-                value.data);
+          ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
       }
     }
 
-    if (value.dataType == Res_value::TYPE_NULL) {
-      // Walk through the default style values looking for the requested
-      // attribute.
-      const ResTable::bag_entry* const def_style_attr_entry =
-          def_style_attr_finder.Find(cur_ident);
+    if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
+      // Walk through the default style values looking for the requested attribute.
+      const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident);
       if (def_style_attr_entry != def_style_attr_end) {
         // We found the attribute we were looking for.
         block = def_style_attr_entry->stringBlock;
         type_set_flags = style_type_set_flags;
         value = def_style_attr_entry->map.value;
         if (kDebugStyles) {
-          ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType,
-                value.data);
+          ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
       }
     }
@@ -360,35 +341,29 @@
     uint32_t resid = 0;
     if (value.dataType != Res_value::TYPE_NULL) {
       // Take care of resolving the found resource to its final value.
-      ssize_t new_block = theme->resolveAttributeReference(
-          &value, block, &resid, &type_set_flags, &config);
+      ssize_t new_block =
+          theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
       if (new_block >= 0) {
         block = new_block;
       }
 
       if (kDebugStyles) {
-        ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType,
-              value.data);
+        ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
       }
-    } else {
-      // If we still don't have a value for this attribute, try to find
-      // it in the theme!
-      ssize_t new_block =
-          theme->getAttribute(cur_ident, &value, &type_set_flags);
+    } else if (value.data != Res_value::DATA_NULL_EMPTY) {
+      // If we still don't have a value for this attribute, try to find it in the theme!
+      ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
       if (new_block >= 0) {
         if (kDebugStyles) {
-          ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType,
-                value.data);
+          ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
-        new_block = res.resolveReference(&value, new_block, &resid,
-                                         &type_set_flags, &config);
+        new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
         if (new_block >= 0) {
           block = new_block;
         }
 
         if (kDebugStyles) {
-          ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType,
-                value.data);
+          ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
         }
       }
     }
@@ -404,8 +379,7 @@
     }
 
     if (kDebugStyles) {
-      ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident,
-            value.dataType, value.data);
+      ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
     }
 
     // Write the final value back to Java.
@@ -418,7 +392,7 @@
     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
     out_values[STYLE_DENSITY] = config.density;
 
-    if (value.dataType != Res_value::TYPE_NULL) {
+    if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
       indices_idx++;
 
       // out_indices must NOT be nullptr.
@@ -502,7 +476,8 @@
     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
     out_values[STYLE_DENSITY] = config.density;
 
-    if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) {
+    if (out_indices != nullptr &&
+        (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
       indices_idx++;
       out_indices[indices_idx] = ii;
     }
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index f661f29b..bab8883 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3538,7 +3538,8 @@
                     attrRes, bag->map.value.dataType, bag->map.value.data,
                     curEntry->value.dataType);
         }
-        if (force || curEntry->value.dataType == Res_value::TYPE_NULL) {
+        if (force || (curEntry->value.dataType == Res_value::TYPE_NULL
+                && curEntry->value.data != Res_value::DATA_NULL_EMPTY)) {
             curEntry->stringBlock = bag->stringBlock;
             curEntry->typeSpecFlags |= bagTypeSpecFlags;
             curEntry->value = bag->map.value;
@@ -3674,7 +3675,8 @@
                             }
                             ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID);
                             return BAD_INDEX;
-                        } else if (type != Res_value::TYPE_NULL) {
+                        } else if (type != Res_value::TYPE_NULL
+                                || te.value.data == Res_value::DATA_NULL_EMPTY) {
                             *outValue = te.value;
                             return te.stringBlock;
                         }
@@ -5997,16 +5999,14 @@
     char locale[RESTABLE_MAX_LOCALE_LEN];
 
     forEachConfiguration(false, false, includeSystemLocales, [&](const ResTable_config& cfg) {
-        if (cfg.locale != 0) {
-            cfg.getBcp47Locale(locale, mergeEquivalentLangs /* canonicalize if merging */);
+        cfg.getBcp47Locale(locale, mergeEquivalentLangs /* canonicalize if merging */);
 
-            const auto beginIter = locales->begin();
-            const auto endIter = locales->end();
+        const auto beginIter = locales->begin();
+        const auto endIter = locales->end();
 
-            auto iter = std::lower_bound(beginIter, endIter, locale, compareString8AndCString);
-            if (iter == endIter || strcmp(iter->string(), locale) != 0) {
-                locales->insertAt(String8(locale), std::distance(beginIter, iter));
-            }
+        auto iter = std::lower_bound(beginIter, endIter, locale, compareString8AndCString);
+        if (iter == endIter || strcmp(iter->string(), locale) != 0) {
+            locales->insertAt(String8(locale), std::distance(beginIter, iter));
         }
     });
 }
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index d8e5abf..fcae53b 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -264,7 +264,7 @@
 
   const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo);
   ASSERT_NE(nullptr, bag_two);
-  ASSERT_EQ(5u, bag_two->entry_count);
+  ASSERT_EQ(6u, bag_two->entry_count);
 
   // attr_one is inherited from StyleOne.
   EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key);
@@ -295,6 +295,11 @@
   EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType);
   EXPECT_EQ(3u, bag_two->entries[4].value.data);
   EXPECT_EQ(0, bag_two->entries[4].cookie);
+
+  EXPECT_EQ(app::R::attr::attr_empty, bag_two->entries[5].key);
+  EXPECT_EQ(Res_value::TYPE_NULL, bag_two->entries[5].value.dataType);
+  EXPECT_EQ(Res_value::DATA_NULL_EMPTY, bag_two->entries[5].value.data);
+  EXPECT_EQ(0, bag_two->entries[5].cookie);
 }
 
 TEST_F(AssetManager2Test, ResolveReferenceToResource) {
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index 1ff2ed4..2d73ce8 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -69,8 +69,8 @@
   ResTable::Theme theme(table_);
   ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
 
-  std::array<uint32_t, 4> attrs{
-      {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four}};
+  std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+                                 R::attr::attr_four, R::attr::attr_empty}};
   std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
 
   ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
@@ -109,11 +109,21 @@
   EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
   EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
   EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+  // @empty comes from the theme, so it has the same asset cookie and changing configurations flags
+  // as the theme.
+  values_cursor += STYLE_NUM_ENTRIES;
+  EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+  EXPECT_EQ(Res_value::DATA_NULL_EMPTY, values_cursor[STYLE_DATA]);
+  EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+  EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+  EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+  EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
 }
 
 TEST_F(AttributeResolutionXmlTest, XmlParser) {
-  std::array<uint32_t, 4> attrs{
-      {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four}};
+  std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+                                 R::attr::attr_four, R::attr::attr_empty}};
   std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
 
   ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(),
@@ -121,7 +131,7 @@
 
   uint32_t* values_cursor = values.data();
   EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
-  EXPECT_EQ(0u, values_cursor[STYLE_DATA]);
+  EXPECT_EQ(Res_value::DATA_NULL_EMPTY, values_cursor[STYLE_DATA]);
   EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
   EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
   EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
@@ -150,16 +160,24 @@
   EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
   EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
   EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+  values_cursor += STYLE_NUM_ENTRIES;
+  EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+  EXPECT_EQ(Res_value::DATA_NULL_UNDEFINED, values_cursor[STYLE_DATA]);
+  EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+  EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
+  EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+  EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
 }
 
 TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
   ResTable::Theme theme(table_);
   ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
 
-  std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
-                                 R::attr::attr_four, R::attr::attr_five}};
+  std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
+                                 R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}};
   std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
-  std::array<uint32_t, attrs.size()> indices;
+  std::array<uint32_t, attrs.size() + 1> indices;
 
   ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(),
              attrs.size(), values.data(), indices.data());
@@ -167,12 +185,12 @@
   const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
 
   uint32_t* values_cursor = values.data();
-  EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
-  EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
+  EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+  EXPECT_EQ(Res_value::DATA_NULL_EMPTY, values_cursor[STYLE_DATA]);
   EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
-  EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+  EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
   EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
-  EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+  EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
 
   values_cursor += STYLE_NUM_ENTRIES;
   EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
@@ -203,6 +221,20 @@
   EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
   EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
   EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+  // @empty comes from the theme, so it has the same asset cookie and changing configurations flags
+  // as the theme.
+  values_cursor += STYLE_NUM_ENTRIES;
+  EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
+  EXPECT_EQ(Res_value::DATA_NULL_EMPTY, values_cursor[STYLE_DATA]);
+  EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
+  EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
+  EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
+  EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
+
+  // The first element of indices contains the number of indices.
+  std::array<uint32_t, 7> expected_indices = {{6u, 0u, 1u, 2u, 3u, 4u, 5u}};
+  EXPECT_EQ(expected_indices, indices);
 }
 
 } // namespace android
diff --git a/libs/androidfw/tests/data/styles/R.h b/libs/androidfw/tests/data/styles/R.h
index 68527c7..05073a8 100644
--- a/libs/androidfw/tests/data/styles/R.h
+++ b/libs/androidfw/tests/data/styles/R.h
@@ -33,6 +33,7 @@
       attr_five = 0x7f010004u,
       attr_indirect = 0x7f010005u,
       attr_six = 0x7f010006u,
+      attr_empty = 0x7f010007u,
     };
   };
 
diff --git a/libs/androidfw/tests/data/styles/build b/libs/androidfw/tests/data/styles/build
index 81f78b1..1ef8e6e 100755
--- a/libs/androidfw/tests/data/styles/build
+++ b/libs/androidfw/tests/data/styles/build
@@ -2,4 +2,5 @@
 
 set -e
 
-aapt package -F styles.apk -M AndroidManifest.xml -S res -f
+aapt2 compile -o compiled.flata --dir res
+aapt2 link -o styles.apk --manifest AndroidManifest.xml compiled.flata
diff --git a/libs/androidfw/tests/data/styles/res/layout/layout.xml b/libs/androidfw/tests/data/styles/res/layout/layout.xml
index f3aa0f8..2c5e947 100644
--- a/libs/androidfw/tests/data/styles/res/layout/layout.xml
+++ b/libs/androidfw/tests/data/styles/res/layout/layout.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <View xmlns:app="http://schemas.android.com/apk/res-auto"
     app:attr_four="?attr/attr_indirect"
-    app:attr_three="10" />
-
+    app:attr_three="10"
+    app:attr_one="@empty" />
diff --git a/libs/androidfw/tests/data/styles/res/values/styles.xml b/libs/androidfw/tests/data/styles/res/values/styles.xml
index da592f8..3c90317 100644
--- a/libs/androidfw/tests/data/styles/res/values/styles.xml
+++ b/libs/androidfw/tests/data/styles/res/values/styles.xml
@@ -33,6 +33,12 @@
     <public type="attr" name="attr_indirect" id="0x7f010005" />
     <attr name="attr_indirect" />
 
+    <public type="attr" name="attr_six" id="0x7f010006" />
+    <attr name="attr_six" />
+
+    <public type="attr" name="attr_empty" id="0x7f010007" />
+    <attr name="attr_empty" />
+
     <public type="string" name="string_one" id="0x7f030000" />
     <string name="string_one">Hi</string>
 
@@ -48,11 +54,9 @@
         <item name="attr_two">"string"</item>
         <item name="attr_three">?attr/attr_indirect</item>
         <item name="attr_five">@string/string_one</item>
+        <item name="attr_empty">@empty</item>
     </style>
-    
-    <public type="attr" name="attr_six" id="0x7f010006" />
-    <attr name="attr_six" />
-    
+
     <public type="style" name="StyleThree" id="0x7f020002" />
     <style name="StyleThree">
         <item name="attr_six">6</item>
diff --git a/libs/androidfw/tests/data/styles/styles.apk b/libs/androidfw/tests/data/styles/styles.apk
index d4ccb83..72abf8f 100644
--- a/libs/androidfw/tests/data/styles/styles.apk
+++ b/libs/androidfw/tests/data/styles/styles.apk
Binary files differ
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index c157a47..a19726c 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1067,7 +1067,7 @@
         private void applyLevelLimits() {
             int[] sampleRates = null;
             Range<Integer> sampleRateRange = null, bitRates = null;
-            int maxChannels = 0;
+            int maxChannels = MAX_INPUT_CHANNEL_COUNT;
             String mime = mParent.getMimeType();
 
             if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_MPEG)) {
@@ -1160,6 +1160,8 @@
             if (info.containsKey("max-channel-count")) {
                 maxInputChannels = Utils.parseIntSafely(
                         info.getString("max-channel-count"), maxInputChannels);
+            } else if ((mParent.mError & ERROR_UNSUPPORTED) != 0) {
+                maxInputChannels = 0;
             }
             if (info.containsKey("bitrate-range")) {
                 bitRates = bitRates.intersect(
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/LinkAccessibilityHelper.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/LinkAccessibilityHelper.java
deleted file mode 100644
index 74b0c6b..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/LinkAccessibilityHelper.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settingslib.accessibility;
-
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.widget.ExploreByTouchHelper;
-import android.text.Layout;
-import android.text.Spanned;
-import android.text.style.ClickableSpan;
-import android.util.Log;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.TextView;
-
-import java.util.List;
-
-/**
- * COPIED FROM SETUP WIZARD An accessibility delegate that allows {@link
- * android.text.style.ClickableSpan} to be focused and clicked by accessibility services.
- *
- * <p>Sample usage:
- *
- * <pre>
- * LinkAccessibilityHelper mAccessibilityHelper;
- *
- * private void init() {
- *     mAccessibilityHelper = new LinkAccessibilityHelper(myTextView);
- *     ViewCompat.setAccessibilityDelegate(myTextView, mLinkHelper);
- * }
- *
- * {@literal @}Override
- * protected boolean dispatchHoverEvent({@literal @}NonNull MotionEvent event) {
- *     if (mAccessibilityHelper != null && mAccessibilityHelper.dispatchHoverEvent(event)) {
- *         return true;
- *     }
- *     return super.dispatchHoverEvent(event);
- * }
- * </pre>
- *
- * @see android.support.v4.widget.ExploreByTouchHelper
- */
-public class LinkAccessibilityHelper extends ExploreByTouchHelper {
-
-    private static final String TAG = "LinkAccessibilityHelper";
-
-    private final TextView mView;
-    private final Rect mTempRect = new Rect();
-
-    public LinkAccessibilityHelper(TextView view) {
-        super(view);
-        mView = view;
-    }
-
-    @Override
-    protected int getVirtualViewAt(float x, float y) {
-        final CharSequence text = mView.getText();
-        if (text instanceof Spanned) {
-            final Spanned spannedText = (Spanned) text;
-            final int offset = getOffsetForPosition(mView, x, y);
-            ClickableSpan[] linkSpans = spannedText.getSpans(offset, offset, ClickableSpan.class);
-            if (linkSpans.length == 1) {
-                ClickableSpan linkSpan = linkSpans[0];
-                return spannedText.getSpanStart(linkSpan);
-            }
-        }
-        return INVALID_ID;
-    }
-
-    @Override
-    protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
-        final CharSequence text = mView.getText();
-        if (text instanceof Spanned) {
-            final Spanned spannedText = (Spanned) text;
-            ClickableSpan[] linkSpans =
-                    spannedText.getSpans(0, spannedText.length(), ClickableSpan.class);
-            for (ClickableSpan span : linkSpans) {
-                virtualViewIds.add(spannedText.getSpanStart(span));
-            }
-        }
-    }
-
-    @Override
-    protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
-        final ClickableSpan span = getSpanForOffset(virtualViewId);
-        if (span != null) {
-            event.setContentDescription(getTextForSpan(span));
-        } else {
-            Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
-            event.setContentDescription(mView.getText());
-        }
-    }
-
-    @Override
-    protected void onPopulateNodeForVirtualView(
-            int virtualViewId, AccessibilityNodeInfoCompat info) {
-        final ClickableSpan span = getSpanForOffset(virtualViewId);
-        if (span != null) {
-            info.setContentDescription(getTextForSpan(span));
-        } else {
-            Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
-            info.setContentDescription(mView.getText());
-        }
-        info.setFocusable(true);
-        info.setClickable(true);
-        getBoundsForSpan(span, mTempRect);
-        if (mTempRect.isEmpty()) {
-            Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
-            mTempRect.set(0, 0, 1, 1);
-        }
-        info.setBoundsInParent(mTempRect);
-        info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
-    }
-
-    @Override
-    protected boolean onPerformActionForVirtualView(
-            int virtualViewId, int action, Bundle arguments) {
-        if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
-            ClickableSpan span = getSpanForOffset(virtualViewId);
-            if (span != null) {
-                span.onClick(mView);
-                return true;
-            } else {
-                Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
-            }
-        }
-        return false;
-    }
-
-    private ClickableSpan getSpanForOffset(int offset) {
-        CharSequence text = mView.getText();
-        if (text instanceof Spanned) {
-            Spanned spannedText = (Spanned) text;
-            ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
-            if (spans.length == 1) {
-                return spans[0];
-            }
-        }
-        return null;
-    }
-
-    private CharSequence getTextForSpan(ClickableSpan span) {
-        CharSequence text = mView.getText();
-        if (text instanceof Spanned) {
-            Spanned spannedText = (Spanned) text;
-            return spannedText.subSequence(
-                    spannedText.getSpanStart(span), spannedText.getSpanEnd(span));
-        }
-        return text;
-    }
-
-    // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for the
-    // section on the first line.
-    private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
-        CharSequence text = mView.getText();
-        outRect.setEmpty();
-        if (text instanceof Spanned) {
-            final Layout layout = mView.getLayout();
-            if (layout != null) {
-                Spanned spannedText = (Spanned) text;
-                final int spanStart = spannedText.getSpanStart(span);
-                final int spanEnd = spannedText.getSpanEnd(span);
-                final float xStart = layout.getPrimaryHorizontal(spanStart);
-                final float xEnd = layout.getPrimaryHorizontal(spanEnd);
-                final int lineStart = layout.getLineForOffset(spanStart);
-                final int lineEnd = layout.getLineForOffset(spanEnd);
-                layout.getLineBounds(lineStart, outRect);
-                if (lineEnd == lineStart) {
-                    // If the span is on a single line, adjust both the left and right bounds
-                    // so outrect is exactly bounding the span.
-                    outRect.left = (int) Math.min(xStart, xEnd);
-                    outRect.right = (int) Math.max(xStart, xEnd);
-                } else {
-                    // If the span wraps across multiple lines, only use the first line (as returned
-                    // by layout.getLineBounds above), and adjust the "start" of outrect to where
-                    // the span starts, leaving the "end" of outrect at the end of the line.
-                    // ("start" being left for LTR, and right for RTL)
-                    if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
-                        outRect.right = (int) xStart;
-                    } else {
-                        outRect.left = (int) xStart;
-                    }
-                }
-
-                // Offset for padding
-                outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop());
-            }
-        }
-        return outRect;
-    }
-
-    // Compat implementation of TextView#getOffsetForPosition().
-
-    private static int getOffsetForPosition(TextView view, float x, float y) {
-        if (view.getLayout() == null) return -1;
-        final int line = getLineAtCoordinate(view, y);
-        return getOffsetAtCoordinate(view, line, x);
-    }
-
-    private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
-        x -= view.getTotalPaddingLeft();
-        // Clamp the position to inside of the view.
-        x = Math.max(0.0f, x);
-        x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
-        x += view.getScrollX();
-        return x;
-    }
-
-    private static int getLineAtCoordinate(TextView view, float y) {
-        y -= view.getTotalPaddingTop();
-        // Clamp the position to inside of the view.
-        y = Math.max(0.0f, y);
-        y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
-        y += view.getScrollY();
-        return view.getLayout().getLineForVertical((int) y);
-    }
-
-    private static int getOffsetAtCoordinate(TextView view, int line, float x) {
-        x = convertToLocalHorizontalCoordinate(view, x);
-        return view.getLayout().getOffsetForHorizontal(line, x);
-    }
-}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/LinkTextView.java b/packages/SettingsLib/src/com/android/settingslib/widget/LinkTextView.java
index da86536..8b9315c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/widget/LinkTextView.java
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/LinkTextView.java
@@ -16,32 +16,25 @@
 package com.android.settingslib.widget;
 
 import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.v4.view.ViewCompat;
 import android.text.Spanned;
 import android.text.method.LinkMovementMethod;
 import android.text.style.ClickableSpan;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
 import android.widget.TextView;
-import com.android.settingslib.accessibility.LinkAccessibilityHelper;
+
 /**
- * Copied from setup wizard. This TextView performs two functions. The first is to make it so the
- * link behaves properly and becomes clickable. The second is that it makes the link visible to
- * accessibility services.
+ * Copied from setup wizard. This TextView performed two functions. The first is to make it so the
+ * link behaves properly and becomes clickable. The second was that it made the link visible to
+ * accessibility services, but from O forward support for links is provided natively.
  */
 public class LinkTextView extends TextView {
 
-    private LinkAccessibilityHelper mAccessibilityHelper;
-
     public LinkTextView(Context context) {
         this(context, null);
     }
 
     public LinkTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mAccessibilityHelper = new LinkAccessibilityHelper(this);
-        ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
     }
 
     @Override
@@ -55,12 +48,4 @@
             }
         }
     }
-
-    @Override
-    protected boolean dispatchHoverEvent(@NonNull MotionEvent event) {
-        if (mAccessibilityHelper.dispatchHoverEvent(event)) {
-            return true;
-        }
-        return super.dispatchHoverEvent(event);
-    }
 }
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f475361..9309359 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1434,6 +1434,9 @@
         dumpSetting(s, p,
                 Settings.Secure.DEVICE_PAIRED,
                 SecureSettingsProto.DEVICE_PAIRED);
+        dumpSetting(s, p,
+                Settings.Secure.NOTIFICATION_BADGING,
+                SecureSettingsProto.NOTIFICATION_BADGING);
     }
 
     private static void dumpProtoSystemSettingsLocked(
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 290ce1f..1d743b4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -300,7 +300,6 @@
                   android:excludeFromRecents="true"
                   android:stateNotNeeded="true"
                   android:resumeWhilePausing="true"
-                  android:screenOrientation="behind"
                   android:resizeableActivity="true"
                   android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
                   android:theme="@style/RecentsTheme.Wallpaper">
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index bdc0871..da2d38f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -73,7 +73,8 @@
             mTouchHandler.onActivityPinned();
             mMediaController.onActivityPinned();
             mMenuController.onActivityPinned();
-            mNotificationController.onActivityPinned(packageName);
+            mNotificationController.onActivityPinned(packageName,
+                    true /* deferUntilAnimationEnds */);
 
             SystemServicesProxy.getInstance(mContext).setPipVisibility(true);
         }
@@ -104,6 +105,7 @@
             mTouchHandler.setTouchEnabled(true);
             mTouchHandler.onPinnedStackAnimationEnded();
             mMenuController.onPinnedStackAnimationEnded();
+            mNotificationController.onPinnedStackAnimationEnded();
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
index 53746e2..696fdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipNotificationController.java
@@ -60,6 +60,9 @@
 
     private PipMotionHelper mMotionHelper;
 
+    // Used when building a deferred notification
+    private String mDeferredNotificationPackageName;
+
     private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
         @Override
         public void onOpChanged(String op, String packageName) {
@@ -87,10 +90,47 @@
         mMotionHelper = motionHelper;
     }
 
-    public void onActivityPinned(String packageName) {
+    public void onActivityPinned(String packageName, boolean deferUntilAnimationEnds) {
         // Clear any existing notification
         mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
 
+        if (deferUntilAnimationEnds) {
+            mDeferredNotificationPackageName = packageName;
+        } else {
+            showNotificationForApp(mDeferredNotificationPackageName);
+        }
+
+        // Register for changes to the app ops setting for this package while it is in PiP
+        registerAppOpsListener(packageName);
+    }
+
+    public void onPinnedStackAnimationEnded() {
+        if (mDeferredNotificationPackageName != null) {
+            showNotificationForApp(mDeferredNotificationPackageName);
+            mDeferredNotificationPackageName = null;
+        }
+    }
+
+    public void onActivityUnpinned(ComponentName topPipActivity) {
+        // Unregister for changes to the previously PiP'ed package
+        unregisterAppOpsListener();
+
+        // Reset the deferred notification package
+        mDeferredNotificationPackageName = null;
+
+        if (topPipActivity != null) {
+            // onActivityUnpinned() is only called after the transition is complete, so we don't
+            // need to defer until the animation ends to update the notification
+            onActivityPinned(topPipActivity.getPackageName(), false /* deferUntilAnimationEnds */);
+        } else {
+            mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
+        }
+    }
+
+    /**
+     * Builds and shows the notification for the given app.
+     */
+    private void showNotificationForApp(String packageName) {
         // Build a new notification
         final Notification.Builder builder =
                 new Notification.Builder(mContext, NotificationChannels.GENERAL)
@@ -105,20 +145,6 @@
             // Show the new notification
             mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build());
         }
-
-        // Register for changes to the app ops setting for this package while it is in PiP
-        registerAppOpsListener(packageName);
-    }
-
-    public void onActivityUnpinned(ComponentName topPipActivity) {
-        // Unregister for changes to the previously PiP'ed package
-        unregisterAppOpsListener();
-
-        if (topPipActivity != null) {
-            onActivityPinned(topPipActivity.getPackageName());
-        } else {
-            mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
-        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 90c65580..8f24ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -767,17 +767,22 @@
             mDockedStackMinimized = minimized;
         } else if (mDockedStackMinimized != minimized) {
             mIsInMinimizeInteraction = true;
-            if (minimized && (mCurrentAnimator == null || !mCurrentAnimator.isRunning())) {
+            if (minimized && (mCurrentAnimator == null || !mCurrentAnimator.isRunning())
+                    && (mDividerPositionBeforeMinimized <= 0 || !mAdjustedForIme)) {
                 mDividerPositionBeforeMinimized = getCurrentPosition();
             }
             mMinimizedSnapAlgorithm = null;
             mDockedStackMinimized = minimized;
             initializeSnapAlgorithm();
-            stopDragging(getCurrentPosition(), minimized ?
-                            mMinimizedSnapAlgorithm.getMiddleTarget() :
-                            mSnapAlgorithm.calculateNonDismissingSnapTarget(
+            stopDragging(minimized
+                            ? mDividerPositionBeforeMinimized
+                            : getCurrentPosition(),
+                    minimized
+                            ? mMinimizedSnapAlgorithm.getMiddleTarget()
+                            : mSnapAlgorithm.calculateNonDismissingSnapTarget(
                                     mDividerPositionBeforeMinimized),
                     animDuration, Interpolators.FAST_OUT_SLOW_IN, 0);
+            setAdjustedForIme(false, animDuration);
         }
         if (!minimized) {
             mBackground.animate().withEndAction(mResetBackgroundRunnable);
@@ -820,6 +825,9 @@
                 .setDuration(animDuration)
                 .start();
         mAdjustedForIme = adjustedForIme;
+        if (mHomeStackResizable && adjustedForIme) {
+            mDividerPositionBeforeMinimized = getCurrentPosition();
+        }
     }
 
     private void resetBackground() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 30ff30f..be221bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -341,14 +341,12 @@
 
     public void appTransitionPending(boolean forced) {
         synchronized (mLock) {
-            mHandler.removeMessages(MSG_APP_TRANSITION_PENDING);
             mHandler.obtainMessage(MSG_APP_TRANSITION_PENDING, forced ? 1 : 0, 0).sendToTarget();
         }
     }
 
     public void appTransitionCancelled() {
         synchronized (mLock) {
-            mHandler.removeMessages(MSG_APP_TRANSITION_CANCELLED);
             mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
         }
     }
@@ -359,7 +357,6 @@
 
     public void appTransitionStarting(long startTime, long duration, boolean forced) {
         synchronized (mLock) {
-            mHandler.removeMessages(MSG_APP_TRANSITION_STARTING);
             mHandler.obtainMessage(MSG_APP_TRANSITION_STARTING, forced ? 1 : 0, 0,
                     Pair.create(startTime, duration)).sendToTarget();
         }
@@ -368,7 +365,6 @@
     @Override
     public void appTransitionFinished() {
         synchronized (mLock) {
-            mHandler.removeMessages(MSG_APP_TRANSITION_FINISHED);
             mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c5853ca..6a9bfae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2499,7 +2499,7 @@
         if (animate) {
             mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, darkAmount);
             mDarkAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-            mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
             mDarkAnimator.start();
         } else {
             setDarkAmount(darkAmount);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b2712ff..aad4431 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -4365,7 +4365,7 @@
                                     }
                                     addWidgetLocked(id);
                                 }
-                                if (id.provider.info != null) {
+                                if (id.provider != null && id.provider.info != null) {
                                     stashProviderRestoreUpdateLocked(id.provider,
                                             restoredId, id.appWidgetId);
                                 } else {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 0999580..41a78a7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -20,6 +20,7 @@
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
 
 import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sPartitionMaxCount;
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.bundleToString;
 
@@ -397,6 +398,21 @@
         }
     }
 
+    // Called by Shell command.
+    public int getMaxPartitions() {
+        synchronized (mLock) {
+            return sPartitionMaxCount;
+        }
+    }
+
+    // Called by Shell command.
+    public void setMaxPartitions(int max) {
+        Slog.i(TAG, "setMaxPartitions(): " + max);
+        synchronized (mLock) {
+            sPartitionMaxCount = max;
+        }
+    }
+
     private void setDebugLocked(boolean debug) {
         com.android.server.autofill.Helper.sDebug = debug;
         android.view.autofill.Helper.sDebug = debug;
@@ -628,6 +644,7 @@
                     pw.print("Debug mode: "); pw.println(oldDebug);
                     pw.print("Verbose mode: "); pw.println(sVerbose);
                     pw.print("Disabled users: "); pw.println(mDisabledUsers);
+                    pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
                     final int size = mServicesCache.size();
                     pw.print("Cached services: ");
                     if (size == 0) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 1b9c86e..f3de557 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -70,9 +70,15 @@
             pw.println("  get log_level ");
             pw.println("    Gets the Autofill log level (off | debug | verbose).");
             pw.println("");
+            pw.println("  get max_partitions");
+            pw.println("    Gets the maximum number of partitions per session.");
+            pw.println("");
             pw.println("  set log_level [off | debug | verbose]");
             pw.println("    Sets the Autofill log level.");
             pw.println("");
+            pw.println("  set max_partitions number");
+            pw.println("    Sets the maximum number of partitions per session.");
+            pw.println("");
             pw.println("  list sessions [--user USER_ID]");
             pw.println("    List all pending sessions.");
             pw.println("");
@@ -86,9 +92,33 @@
     }
 
     private int requestGet(PrintWriter pw) {
-        if (!isNextArgLogLevel(pw, "get")) {
-            return -1;
+        final String what = getNextArgRequired();
+        switch(what) {
+            case "log_level":
+                return getLogLevel(pw);
+            case "max_partitions":
+                return getMaxPartitions(pw);
+            default:
+                pw.println("Invalid set: " + what);
+                return -1;
         }
+    }
+
+    private int requestSet(PrintWriter pw) {
+        final String what = getNextArgRequired();
+
+        switch(what) {
+            case "log_level":
+                return setLogLevel(pw);
+            case "max_partitions":
+                return setMaxPartitions();
+            default:
+                pw.println("Invalid set: " + what);
+                return -1;
+        }
+    }
+
+    private int getLogLevel(PrintWriter pw) {
         final int logLevel = mService.getLogLevel();
         switch (logLevel) {
             case AutofillManager.FLAG_ADD_CLIENT_VERBOSE:
@@ -106,11 +136,8 @@
         }
     }
 
-    private int requestSet(PrintWriter pw) {
-        if (!isNextArgLogLevel(pw, "set")) {
-            return -1;
-        }
-        final String logLevel = getNextArg();
+    private int setLogLevel(PrintWriter pw) {
+        final String logLevel = getNextArgRequired();
         switch (logLevel.toLowerCase()) {
             case "verbose":
                 mService.setLogLevel(AutofillManager.FLAG_ADD_CLIENT_VERBOSE);
@@ -127,6 +154,16 @@
         }
     }
 
+    private int getMaxPartitions(PrintWriter pw) {
+        pw.println(mService.getMaxPartitions());
+        return 0;
+    }
+
+    private int setMaxPartitions() {
+        mService.setMaxPartitions(Integer.parseInt(getNextArgRequired()));
+        return 0;
+    }
+
     private int requestDestroy(PrintWriter pw) {
         if (!isNextArgSessions(pw)) {
             return -1;
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index ffcde8d..0281f73 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -26,16 +26,23 @@
 
     /**
      * Defines a logging flag that can be dynamically changed at runtime using
-     * {@code cmd autofill debug [on|off]}.
+     * {@code cmd autofill set log_level debug}.
      */
     public static boolean sDebug = false;
 
     /**
      * Defines a logging flag that can be dynamically changed at runtime using
-     * {@code cmd autofill verbose [on|off]}.
+     * {@code cmd autofill set log_level verbose}.
      */
     public static boolean sVerbose = false;
 
+    /**
+     * Maximum number of partitions that can be allowed in a session.
+     *
+     * <p>Can be modified using {@code cmd autofill set max_partitions}.
+     */
+    static int sPartitionMaxCount = 10;
+
     private Helper() {
         throw new UnsupportedOperationException("contains static members only");
     }
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index a12ebb2..35f4fae 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -27,11 +27,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.ICancellationSignal;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.service.autofill.AutofillService;
 import android.service.autofill.FillRequest;
@@ -43,6 +45,7 @@
 import android.text.format.DateUtils;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.HandlerCaller;
 import com.android.server.FgThread;
 
@@ -63,6 +66,9 @@
     // How long after the last interaction with the service we would unbind
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
+    // How long after we make a remote request to a fill service we timeout
+    private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
+
     private final Context mContext;
 
     private final ComponentName mComponentName;
@@ -413,12 +419,18 @@
 
     private static final class PendingFillRequest extends PendingRequest {
         private final Object mLock = new Object();
+
         private final WeakReference<RemoteFillService> mWeakService;
         private final FillRequest mRequest;
         private final IFillCallback mCallback;
         private ICancellationSignal mCancellation;
+
+        @GuardedBy("mLock")
         private boolean mCancelled;
 
+        @GuardedBy("mLock")
+        private boolean mCompleted;
+
         public PendingFillRequest(FillRequest request, RemoteFillService service) {
             mRequest = request;
             mWeakService = new WeakReference<>(service);
@@ -443,8 +455,15 @@
 
                 @Override
                 public void onSuccess(FillResponse response) {
+                    synchronized (mLock) {
+                        if (mCompleted) {
+                            return;
+                        }
+                        mCompleted = true;
+                    }
                     RemoteFillService remoteService = mWeakService.get();
                     if (remoteService != null) {
+                        service.mHandler.getHandler().removeCallbacks(PendingFillRequest.this);
                         remoteService.dispatchOnFillRequestSuccess(PendingFillRequest.this,
                                 getCallingUid(), request.getFlags(), response);
                     }
@@ -452,13 +471,29 @@
 
                 @Override
                 public void onFailure(CharSequence message) {
+                    synchronized (mLock) {
+                        if (mCompleted) {
+                            return;
+                        }
+                        mCompleted = true;
+                    }
                     RemoteFillService remoteService = mWeakService.get();
                     if (remoteService != null) {
+                        service.mHandler.getHandler().removeCallbacks(PendingFillRequest.this);
                         remoteService.dispatchOnFillRequestFailure(
                                 PendingFillRequest.this, message);
                     }
                 }
             };
+            service.mHandler.getHandler().postAtTime(() -> {
+                cancel();
+                try {
+                    mCallback.onFailure(null);
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+            }, PendingFillRequest.this,
+                    SystemClock.uptimeMillis() + TIMEOUT_REMOTE_REQUEST_MILLIS);
         }
 
         @Override
@@ -496,10 +531,15 @@
     }
 
     private static final class PendingSaveRequest extends PendingRequest {
+        private final Object mLock = new Object();
+
         private final WeakReference<RemoteFillService> mWeakService;
         private final SaveRequest mRequest;
         private final ISaveCallback mCallback;
 
+        @GuardedBy("mLock")
+        private boolean mCompleted;
+
         public PendingSaveRequest(@NonNull SaveRequest request,
                 @NonNull RemoteFillService service) {
             mRequest = request;
@@ -507,8 +547,16 @@
             mCallback = new ISaveCallback.Stub() {
                 @Override
                 public void onSuccess() {
+                    synchronized (mLock) {
+                        if (mCompleted) {
+                            return;
+                        }
+                        mCompleted = true;
+                    }
+                    cancel();
                     RemoteFillService service = mWeakService.get();
                     if (service != null) {
+                        service.mHandler.getHandler().removeCallbacks(PendingSaveRequest.this);
                         service.dispatchOnSaveRequestSuccess(
                                 PendingSaveRequest.this);
                     }
@@ -516,13 +564,29 @@
 
                 @Override
                 public void onFailure(CharSequence message) {
+                    synchronized (mLock) {
+                        if (mCompleted) {
+                            return;
+                        }
+                        mCompleted = true;
+                    }
                     RemoteFillService service = mWeakService.get();
                     if (service != null) {
+                        service.mHandler.getHandler().removeCallbacks(PendingSaveRequest.this);
                         service.dispatchOnSaveRequestFailure(
                                 PendingSaveRequest.this, message);
                     }
                 }
             };
+            service.mHandler.getHandler().postAtTime(() -> {
+                cancel();
+                try {
+                    mCallback.onFailure(null);
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+            }, PendingSaveRequest.this,
+                    SystemClock.uptimeMillis() + TIMEOUT_REMOTE_REQUEST_MILLIS);
         }
 
         @Override
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ef5cdd1..4746ee9 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -26,6 +26,7 @@
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
 
 import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sPartitionMaxCount;
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.ViewState.STATE_AUTOFILLED;
 import static com.android.server.autofill.ViewState.STATE_RESTARTED_SESSION;
@@ -162,6 +163,7 @@
     @GuardedBy("mLock")
     private boolean mIsSaving;
 
+
     /**
      * Receiver of assist data from the app's {@link Activity}.
      */
@@ -933,7 +935,6 @@
         }
     }
 
-    private static final int PARTITION_MAX_COUNT = 64;
     /**
      * Determines if a new partition should be started for an id.
      *
@@ -947,8 +948,9 @@
         }
 
         final int numResponses = mResponses.size();
-        if (numResponses >= PARTITION_MAX_COUNT) {
-            Slog.e(TAG, "Cannot create more than 64 partitions. Not creating a new partition.");
+        if (numResponses >= sPartitionMaxCount) {
+            Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
+                    + " reached maximum of " + sPartitionMaxCount);
             return false;
         }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1ed46a0..5e03508 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1493,12 +1493,13 @@
                     mAm.updateOomAdjLocked(r.binding.service.app, false);
                 }
             }
+
+            mAm.updateOomAdjLocked();
+
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
 
-        mAm.updateOomAdjLocked();
-
         return true;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7304c22..5283924 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1472,6 +1472,20 @@
     boolean mOrigWaitForDebugger = false;
     boolean mAlwaysFinishActivities = false;
     boolean mForceResizableActivities;
+    /**
+     * Flag that indicates if multi-window is enabled.
+     *
+     * For any particular form of multi-window to be enabled, generic multi-window must be enabled
+     * in {@link com.android.internal.R.bool.config_supportsMultiWindow} config or
+     * {@link Settings.Global#DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES} development option set.
+     * At least one of the forms of multi-window must be enabled in order for this flag to be
+     * initialized to 'true'.
+     *
+     * @see #mSupportsSplitScreenMultiWindow
+     * @see #mSupportsFreeformWindowManagement
+     * @see #mSupportsPictureInPicture
+     * @see #mSupportsMultiDisplay
+     */
     boolean mSupportsMultiWindow;
     boolean mSupportsSplitScreenMultiWindow;
     boolean mSupportsFreeformWindowManagement;
@@ -4598,7 +4612,7 @@
                 mAppSwitchesAllowedTime = 0;
             }
         }
-        int ret = pir.sendInner(0, fillInIntent, resolvedType, null, null,
+        int ret = pir.sendInner(0, fillInIntent, resolvedType, null, null, null,
                 resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions, null);
         return ret;
     }
@@ -7504,11 +7518,12 @@
     }
 
     @Override
-    public int sendIntentSender(IIntentSender target, int code, Intent intent, String resolvedType,
+    public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code,
+            Intent intent, String resolvedType,
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
         if (target instanceof PendingIntentRecord) {
             return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType,
-                    finishedReceiver, requiredPermission, options);
+                    whitelistToken, finishedReceiver, requiredPermission, options);
         } else {
             if (intent == null) {
                 // Weird case: someone has given us their own custom IIntentSender, and now
@@ -7520,7 +7535,8 @@
                 intent = new Intent(Intent.ACTION_MAIN);
             }
             try {
-                target.send(code, intent, resolvedType, null, requiredPermission, options);
+                target.send(code, intent, resolvedType, whitelistToken, null,
+                        requiredPermission, options);
             } catch (RemoteException e) {
             }
             // Platform code can rely on getting a result back when the send is done, but if
@@ -7817,11 +7833,15 @@
     // be guarded by permission checking.
     int getUidState(int uid) {
         synchronized (this) {
-            UidRecord uidRec = mActiveUids.get(uid);
-            return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState;
+            return getUidStateLocked(uid);
         }
     }
 
+    int getUidStateLocked(int uid) {
+        UidRecord uidRec = mActiveUids.get(uid);
+        return uidRec == null ? ActivityManager.PROCESS_STATE_NONEXISTENT : uidRec.curProcState;
+    }
+
     @Override
     public boolean isInMultiWindowMode(IBinder token) {
         final long origId = Binder.clearCallingIdentity();
@@ -13387,8 +13407,12 @@
     public void setRenderThread(int tid) {
         synchronized (this) {
             ProcessRecord proc;
+            int pid = Binder.getCallingPid();
+            if (pid == Process.myPid()) {
+                demoteSystemServerRenderThread(tid);
+                return;
+            }
             synchronized (mPidsSelfLocked) {
-                int pid = Binder.getCallingPid();
                 proc = mPidsSelfLocked.get(pid);
                 if (proc != null && proc.renderThreadTid == 0 && tid > 0) {
                     // ensure the tid belongs to the process
@@ -13421,6 +13445,16 @@
         }
     }
 
+    /**
+     * We only use RenderThread in system_server to store task snapshots to the disk, which should
+     * happen in the background. Thus, demote render thread from system_server to a lower priority.
+     *
+     * @param tid the tid of the RenderThread
+     */
+    private void demoteSystemServerRenderThread(int tid) {
+        setThreadPriority(tid, Process.THREAD_PRIORITY_BACKGROUND);
+    }
+
     @Override
     public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
@@ -13926,16 +13960,23 @@
             mAlwaysFinishActivities = alwaysFinishActivities;
             mSupportsLeanbackOnly = supportsLeanbackOnly;
             mForceResizableActivities = forceResizable;
-            if (supportsMultiWindow || forceResizable) {
+            final boolean multiWindowFormEnabled = freeformWindowManagement
+                    || supportsSplitScreenMultiWindow
+                    || supportsPictureInPicture
+                    || supportsMultiDisplay;
+            if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
                 mSupportsMultiWindow = true;
-                mSupportsFreeformWindowManagement = freeformWindowManagement || forceResizable;
+                mSupportsFreeformWindowManagement = freeformWindowManagement;
+                mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
+                mSupportsPictureInPicture = supportsPictureInPicture;
+                mSupportsMultiDisplay = supportsMultiDisplay;
             } else {
                 mSupportsMultiWindow = false;
                 mSupportsFreeformWindowManagement = false;
+                mSupportsSplitScreenMultiWindow = false;
+                mSupportsPictureInPicture = false;
+                mSupportsMultiDisplay = false;
             }
-            mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
-            mSupportsPictureInPicture = supportsPictureInPicture;
-            mSupportsMultiDisplay = supportsMultiDisplay;
             mWindowManager.setForceResizableTasks(mForceResizableActivities);
             mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
             // This happens before any activities are started, so we can change global configuration
@@ -23642,12 +23683,13 @@
         }
 
         @Override
-        public void setPendingIntentWhitelistDuration(IIntentSender target, long duration) {
+        public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder whitelistToken,
+                long duration) {
             if (!(target instanceof PendingIntentRecord)) {
                 Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target);
                 return;
             }
-            ((PendingIntentRecord) target).setWhitelistDurationLocked(duration);
+            ((PendingIntentRecord) target).setWhitelistDurationLocked(whitelistToken, duration);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index dab122f..6eae9e6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -22,11 +22,8 @@
 import android.app.IActivityContainer;
 import android.app.IActivityController;
 import android.app.IActivityManager;
-import android.app.IInstrumentationWatcher;
 import android.app.IStopUserCallback;
-import android.app.Instrumentation;
 import android.app.ProfilerInfo;
-import android.app.UiAutomationConnection;
 import android.app.WaitResult;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.IUsageStatsManager;
@@ -37,7 +34,6 @@
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
-import android.content.pm.InstrumentationInfo;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5c57be2..f13dad7 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -2180,7 +2180,7 @@
 
     void setRequestedOrientation(int requestedOrientation) {
         if (ActivityInfo.isFixedOrientation(requestedOrientation) && !fullscreen
-                && appInfo.targetSdkVersion >= O) {
+                && appInfo.targetSdkVersion > O) {
             throw new IllegalStateException("Only fullscreen activities can request orientation");
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8b7efff..4049500 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -4731,7 +4731,7 @@
             checkEmbeddedAllowedInner(userId, pendingIntent.key.requestIntent,
                     pendingIntent.key.requestResolvedType);
 
-            return pendingIntent.sendInner(0, null, null, null, null, null, null, 0,
+            return pendingIntent.sendInner(0, null, null, null, null, null, null, null, 0,
                     FORCE_NEW_TASK_FLAGS, FORCE_NEW_TASK_FLAGS, null, this);
         }
 
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 7ba67c5..6eca3fa 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -32,6 +32,7 @@
 import android.os.RemoteException;
 import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.TimeUtils;
 
@@ -51,7 +52,7 @@
     final WeakReference<PendingIntentRecord> ref;
     boolean sent = false;
     boolean canceled = false;
-    private long whitelistDuration = 0;
+    private ArrayMap<IBinder, Long> whitelistDuration;
     private RemoteCallbackList<IResultReceiver> mCancelCallbacks;
 
     String stringName;
@@ -194,8 +195,19 @@
         ref = new WeakReference<PendingIntentRecord>(this);
     }
 
-    void setWhitelistDurationLocked(long duration) {
-        this.whitelistDuration = duration;
+    void setWhitelistDurationLocked(IBinder whitelistToken, long duration) {
+        if (duration > 0) {
+            if (whitelistDuration == null) {
+                whitelistDuration = new ArrayMap<>();
+            }
+            whitelistDuration.put(whitelistToken, duration);
+        } else if (whitelistDuration != null) {
+            whitelistDuration.remove(whitelistToken);
+            if (whitelistDuration.size() <= 0) {
+                whitelistDuration = null;
+            }
+
+        }
         this.stringName = null;
     }
 
@@ -219,19 +231,20 @@
         return listeners;
     }
 
-    public void send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
-            String requiredPermission, Bundle options) {
-        sendInner(code, intent, resolvedType, finishedReceiver,
-                requiredPermission, null, null, 0, 0, 0, options, null);
-    }
-
-    public int sendWithResult(int code, Intent intent, String resolvedType,
+    public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-        return sendInner(code, intent, resolvedType, finishedReceiver,
+        sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver,
                 requiredPermission, null, null, 0, 0, 0, options, null);
     }
 
-    int sendInner(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
+    public int sendWithResult(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+            IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+        return sendInner(code, intent, resolvedType, whitelistToken, finishedReceiver,
+                requiredPermission, null, null, 0, 0, 0, options, null);
+    }
+
+    int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+            IIntentReceiver finishedReceiver,
             String requiredPermission, IBinder resultTo, String resultWho, int requestCode,
             int flagsMask, int flagsValues, Bundle options, IActivityContainer container) {
         if (intent != null) intent.setDefusable(true);
@@ -276,20 +289,29 @@
 
                 final long origId = Binder.clearCallingIdentity();
 
-                if (whitelistDuration > 0) {
-                    StringBuilder tag = new StringBuilder(64);
-                    tag.append("pendingintent:");
-                    UserHandle.formatUid(tag, callingUid);
-                    tag.append(":");
-                    if (finalIntent.getAction() != null) {
-                        tag.append(finalIntent.getAction());
-                    } else if (finalIntent.getComponent() != null) {
-                        finalIntent.getComponent().appendShortString(tag);
-                    } else if (finalIntent.getData() != null) {
-                        tag.append(finalIntent.getData());
+                if (whitelistDuration != null) {
+                    Long duration = whitelistDuration.get(whitelistToken);
+                    if (duration != null) {
+                        int procState = owner.getUidState(callingUid);
+                        if (!ActivityManager.isProcStateBackground(procState)) {
+                            StringBuilder tag = new StringBuilder(64);
+                            tag.append("pendingintent:");
+                            UserHandle.formatUid(tag, callingUid);
+                            tag.append(":");
+                            if (finalIntent.getAction() != null) {
+                                tag.append(finalIntent.getAction());
+                            } else if (finalIntent.getComponent() != null) {
+                                finalIntent.getComponent().appendShortString(tag);
+                            } else if (finalIntent.getData() != null) {
+                                tag.append(finalIntent.getData());
+                            }
+                            owner.tempWhitelistForPendingIntentLocked(callingPid,
+                                    callingUid, uid, duration, tag.toString());
+                        } else {
+                            Slog.w(TAG, "Not doing whitelist " + this + ": caller state="
+                                    + procState);
+                        }
                     }
-                    owner.tempWhitelistForPendingIntentLocked(callingPid,
-                            callingUid, uid, whitelistDuration, tag.toString());
                 }
 
                 boolean sendFinish = finishedReceiver != null;
@@ -425,10 +447,17 @@
             pw.print(prefix); pw.print("sent="); pw.print(sent);
                     pw.print(" canceled="); pw.println(canceled);
         }
-        if (whitelistDuration != 0) {
+        if (whitelistDuration != null) {
             pw.print(prefix);
             pw.print("whitelistDuration=");
-            TimeUtils.formatDuration(whitelistDuration, pw);
+            for (int i = 0; i < whitelistDuration.size(); i++) {
+                if (i != 0) {
+                    pw.print(", ");
+                }
+                pw.print(Integer.toHexString(System.identityHashCode(whitelistDuration.keyAt(i))));
+                pw.print(":");
+                TimeUtils.formatDuration(whitelistDuration.valueAt(i), pw);
+            }
             pw.println();
         }
         if (mCancelCallbacks != null) {
@@ -451,9 +480,16 @@
         sb.append(key.packageName);
         sb.append(' ');
         sb.append(key.typeName());
-        if (whitelistDuration > 0) {
+        if (whitelistDuration != null) {
             sb.append( " (whitelist: ");
-            TimeUtils.formatDuration(whitelistDuration, sb);
+            for (int i = 0; i < whitelistDuration.size(); i++) {
+                if (i != 0) {
+                    sb.append(",");
+                }
+                sb.append(Integer.toHexString(System.identityHashCode(whitelistDuration.keyAt(i))));
+                sb.append(":");
+                TimeUtils.formatDuration(whitelistDuration.valueAt(i), sb);
+            }
             sb.append(")");
         }
         sb.append('}');
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index e6edaf1..1bd2085 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -41,9 +41,10 @@
             if (DBG) Slog.d(TAG, "missing config");
             return null;
         }
+        boolean userWantsBadges = mConfig.badgingEnabled(record.sbn.getUser());
         boolean appCanShowBadge =
                 mConfig.canShowBadge(record.sbn.getPackageName(), record.sbn.getUid());
-        if (!appCanShowBadge) {
+        if (!userWantsBadges || !appCanShowBadge) {
             record.setShowBadge(false);
         } else {
             record.setShowBadge(mConfig.getNotificationChannel(record.sbn.getPackageName(),
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 343aab1..9cd0dff 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -344,6 +344,7 @@
 
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
+    private static final IBinder WHITELIST_TOKEN = new Binder();
     private RankingHandler mRankingHandler;
     private long mLastOverRateLogTime;
     private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
@@ -926,6 +927,8 @@
     };
 
     private final class SettingsObserver extends ContentObserver {
+        private final Uri NOTIFICATION_BADGING_URI
+                = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
         private final Uri NOTIFICATION_LIGHT_PULSE_URI
                 = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
         private final Uri NOTIFICATION_RATE_LIMIT_URI
@@ -937,6 +940,8 @@
 
         void observe() {
             ContentResolver resolver = getContext().getContentResolver();
+            resolver.registerContentObserver(NOTIFICATION_BADGING_URI,
+                    false, this, UserHandle.USER_ALL);
             resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
                     false, this, UserHandle.USER_ALL);
             resolver.registerContentObserver(NOTIFICATION_RATE_LIMIT_URI,
@@ -962,6 +967,9 @@
                 mMaxPackageEnqueueRate = Settings.Global.getFloat(resolver,
                             Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, mMaxPackageEnqueueRate);
             }
+            if (uri == null || NOTIFICATION_BADGING_URI.equals(uri)) {
+                mRankingHelper.updateBadgingEnabled();
+            }
         }
     }
 
@@ -983,6 +991,7 @@
 
     public NotificationManagerService(Context context) {
         super(context);
+        Notification.processWhitelistToken = WHITELIST_TOKEN;
     }
 
     // TODO - replace these methods with a single VisibleForTesting constructor
@@ -3262,7 +3271,8 @@
                 for (int i = 0; i < intentCount; i++) {
                     PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
                     if (pendingIntent != null) {
-                        am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
+                        am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
+                                WHITELIST_TOKEN, duration);
                     }
                 }
             }
@@ -4025,6 +4035,7 @@
 
     private void handleRankingSort(Message msg) {
         if (!(msg.obj instanceof Boolean)) return;
+        if (mRankingHelper == null) return;
         boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
         synchronized (mNotificationLock) {
             final int N = mNotificationList.size();
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 4d19b52..36da04d 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -18,6 +18,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.content.pm.ParceledListSlice;
+import android.os.UserHandle;
 
 import java.util.Collection;
 
@@ -27,6 +28,7 @@
     int getImportance(String packageName, int uid);
     void setShowBadge(String packageName, int uid, boolean showBadge);
     boolean canShowBadge(String packageName, int uid);
+    boolean badgingEnabled(UserHandle userHandle);
 
     Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
             int uid);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index d4b6461..e83d453 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -21,6 +21,8 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.util.Preconditions;
 
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -33,10 +35,14 @@
 import android.metrics.LogMaker;
 import android.os.Build;
 import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -48,11 +54,13 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 
 public class RankingHelper implements RankingConfig {
     private static final String TAG = "RankingHelper";
@@ -89,6 +97,7 @@
     private final Context mContext;
     private final RankingHandler mRankingHandler;
     private final PackageManager mPm;
+    private SparseBooleanArray mBadgingEnabled;
 
     public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
             NotificationUsageStats usageStats, String[] extractorNames) {
@@ -98,6 +107,8 @@
 
         mPreliminaryComparator = new NotificationComparator(mContext);
 
+        updateBadgingEnabled();
+
         final int N = extractorNames.length;
         mSignalExtractors = new NotificationSignalExtractor[N];
         for (int i = 0; i < N; i++) {
@@ -575,12 +586,8 @@
         updateConfig();
     }
 
-    private void clearLockedFields(NotificationChannel channel) {
-        int clearMask = 0;
-        for (int i = 0; i < NotificationChannel.LOCKABLE_FIELDS.length; i++) {
-            clearMask |= NotificationChannel.LOCKABLE_FIELDS[i];
-        }
-        channel.lockFields(~clearMask);
+    void clearLockedFields(NotificationChannel channel) {
+        channel.unlockFields(channel.getUserLockedFields());
     }
 
     @Override
@@ -598,6 +605,7 @@
         if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
             updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
         }
+        lockFieldsForUpdate(channel, updatedChannel);
         r.channels.put(updatedChannel.getId(), updatedChannel);
 
         if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) {
@@ -821,6 +829,35 @@
                 enabled ? DEFAULT_IMPORTANCE : NotificationManager.IMPORTANCE_NONE);
     }
 
+    @VisibleForTesting
+    void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
+        update.unlockFields(update.getUserLockedFields());
+        update.lockFields(original.getUserLockedFields());
+        if (original.canBypassDnd() != update.canBypassDnd()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
+        }
+        if (original.getLockscreenVisibility() != update.getLockscreenVisibility()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY);
+        }
+        if (original.getImportance() != update.getImportance()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+        }
+        if (original.shouldShowLights() != update.shouldShowLights()
+                || original.getLightColor() != update.getLightColor()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
+        }
+        if (!Objects.equals(original.getSound(), update.getSound())) {
+            update.lockFields(NotificationChannel.USER_LOCKED_SOUND);
+        }
+        if (!Arrays.equals(original.getVibrationPattern(), update.getVibrationPattern())
+                || original.shouldVibrate() != update.shouldVibrate()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
+        }
+        if (original.canShowBadge() != update.canShowBadge()) {
+            update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE);
+        }
+    }
+
     public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
         if (filter == null) {
             final int N = mSignalExtractors.length;
@@ -1099,6 +1136,38 @@
                         channel.getImportance());
     }
 
+    public void updateBadgingEnabled() {
+        if (mBadgingEnabled == null) {
+            mBadgingEnabled = new SparseBooleanArray();
+        }
+        boolean changed = false;
+        // update the cached values
+        for (int index = 0; index < mBadgingEnabled.size(); index++) {
+            int userId = mBadgingEnabled.keyAt(index);
+            final boolean oldValue = mBadgingEnabled.get(userId);
+            final boolean newValue = Secure.getIntForUser(mContext.getContentResolver(),
+                    Secure.NOTIFICATION_BADGING,
+                    DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0;
+            mBadgingEnabled.put(userId, newValue);
+            changed |= oldValue != newValue;
+        }
+        if (changed) {
+            mRankingHandler.requestSort(false);
+        }
+    }
+
+    public boolean badgingEnabled(UserHandle userHandle) {
+        int userId = userHandle.getIdentifier();
+        if (mBadgingEnabled.indexOfKey(userId) < 0) {
+            mBadgingEnabled.put(userId,
+                    Secure.getIntForUser(mContext.getContentResolver(),
+                            Secure.NOTIFICATION_BADGING,
+                            DEFAULT_SHOW_BADGE ? 1 : 0, userId) != 0);
+        }
+        return mBadgingEnabled.get(userId, DEFAULT_SHOW_BADGE);
+    }
+
+
     private static class Record {
         static int UNKNOWN_UID = UserHandle.USER_NULL;
 
diff --git a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
index bbd4048..575e0f9 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
@@ -21,6 +21,7 @@
 import android.os.ShellCommand;
 
 import java.io.PrintWriter;
+import java.util.Locale;
 
 class OtaDexoptShellCommand extends ShellCommand {
     final IOtaDexopt mInterface;
@@ -93,7 +94,10 @@
     private int runOtaProgress() throws RemoteException {
         final float progress = mInterface.getProgress();
         final PrintWriter pw = getOutPrintWriter();
-        pw.format("%.2f", progress);
+        // Note: The float output is parsed by update_engine. It does needs to be non-localized,
+        //       as it's always expected to be "0.xy," never "0,xy" or similar. So use the ROOT
+        //       Locale for formatting. (b/37760573)
+        pw.format(Locale.ROOT, "%.2f", progress);
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e4b5241..093cdbc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3615,7 +3615,7 @@
             if (matchFactoryOnly) {
                 final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
                 if (ps != null) {
-                    if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                    if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
                         return null;
                     }
                     return generatePackageInfo(ps, flags, userId);
@@ -3630,14 +3630,14 @@
                 Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
             if (p != null) {
                 if (filterSharedLibPackageLPr((PackageSetting) p.mExtras,
-                        Binder.getCallingUid(), userId)) {
+                        Binder.getCallingUid(), userId, flags)) {
                     return null;
                 }
                 return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
             }
             if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) {
                 final PackageSetting ps = mSettings.mPackages.get(packageName);
-                if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
                     return null;
                 }
                 return generatePackageInfo(ps, flags, userId);
@@ -3646,13 +3646,17 @@
         return null;
     }
 
-
-    private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId) {
-        // System/shell/root get to see all static libs
-        final int appId = UserHandle.getAppId(uid);
-        if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
-                || appId == Process.ROOT_UID) {
-            return false;
+    private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
+            int flags) {
+        // Callers can access only the libs they depend on, otherwise they need to explicitly
+        // ask for the shared libraries given the caller is allowed to access all static libs.
+        if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
+            // System/shell/root get to see all static libs
+            final int appId = UserHandle.getAppId(uid);
+            if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
+                    || appId == Process.ROOT_UID) {
+                return false;
+            }
         }
 
         // No package means no static lib as it is always on internal storage
@@ -3847,7 +3851,7 @@
         if (!sUserManager.exists(userId)) return null;
         PackageSetting ps = mSettings.mPackages.get(packageName);
         if (ps != null) {
-            if (filterSharedLibPackageLPr(ps, uid, userId)) {
+            if (filterSharedLibPackageLPr(ps, uid, userId, flags)) {
                 return null;
             }
             if (ps.pkg == null) {
@@ -3888,7 +3892,7 @@
             if (p != null) {
                 PackageSetting ps = mSettings.mPackages.get(packageName);
                 if (ps == null) return null;
-                if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
                     return null;
                 }
                 // Note: isEnabledLP() does not apply here - always return info
@@ -4349,7 +4353,6 @@
                     }
                     final long identity = Binder.clearCallingIdentity();
                     try {
-                        // TODO: We will change version code to long, so in the new API it is long
                         PackageInfo packageInfo = getPackageInfoVersioned(
                                 libInfo.getDeclaringPackage(), flags, userId);
                         if (packageInfo == null) {
@@ -4492,7 +4495,8 @@
                     }
                     PackageSetting ps = mSettings.getPackageLPr(libEntry.apk);
                     if (ps != null && !filterSharedLibPackageLPr(ps, Binder.getCallingUid(),
-                            UserHandle.getUserId(Binder.getCallingUid()))) {
+                            UserHandle.getUserId(Binder.getCallingUid()),
+                            PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) {
                         if (libs == null) {
                             libs = new ArraySet<>();
                         }
@@ -7547,7 +7551,7 @@
             if (listUninstalled) {
                 list = new ArrayList<>(mSettings.mPackages.size());
                 for (PackageSetting ps : mSettings.mPackages.values()) {
-                    if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                    if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
                         continue;
                     }
                     final PackageInfo pi = generatePackageInfo(ps, flags, userId);
@@ -7559,7 +7563,7 @@
                 list = new ArrayList<>(mPackages.size());
                 for (PackageParser.Package p : mPackages.values()) {
                     if (filterSharedLibPackageLPr((PackageSetting) p.mExtras,
-                            Binder.getCallingUid(), userId)) {
+                            Binder.getCallingUid(), userId, flags)) {
                         continue;
                     }
                     final PackageInfo pi = generatePackageInfo((PackageSetting)
@@ -7664,7 +7668,7 @@
                         effectiveFlags |= PackageManager.MATCH_ANY_USER;
                     }
                     if (ps.pkg != null) {
-                        if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                        if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
                             continue;
                         }
                         ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags,
@@ -7688,7 +7692,7 @@
                 for (PackageParser.Package p : mPackages.values()) {
                     if (p.mExtras != null) {
                         PackageSetting ps = (PackageSetting) p.mExtras;
-                        if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId)) {
+                        if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) {
                             continue;
                         }
                         ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c1d68b8..3e920d4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -48,6 +48,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ShellCommand;
 import android.os.SystemProperties;
@@ -1686,7 +1687,7 @@
 
         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
-            public void send(int code, Intent intent, String resolvedType,
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
                 try {
                     mResult.offer(intent, 5, TimeUnit.SECONDS);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6aff600..8e058ad 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5325,7 +5325,7 @@
     @Override
     public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
             WindowState attached, WindowState imeTarget) {
-        final boolean visible = !win.isGoneForLayoutLw() && win.getAttrs().alpha > 0f;
+        final boolean visible = win.isVisibleLw() && win.getAttrs().alpha > 0f;
         if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisible=" + visible);
         applyKeyguardPolicyLw(win, imeTarget);
         final int fl = PolicyControl.getWindowFlags(win, attrs);
@@ -5960,15 +5960,23 @@
                             result &= ~ACTION_PASS_TO_USER;
                             break;
                         }
-                        if (telecomManager.isInCall()
-                                && (result & ACTION_PASS_TO_USER) == 0) {
-                            // If we are in call but we decided not to pass the key to
-                            // the application, just pass it to the session service.
-                            MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
-                                    event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
-                            break;
-                        }
                     }
+                    int audioMode = AudioManager.MODE_NORMAL;
+                    try {
+                        audioMode = getAudioService().getMode();
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error getting AudioService in interceptKeyBeforeQueueing.", e);
+                    }
+                    boolean isInCall = (telecomManager != null && telecomManager.isInCall()) ||
+                            audioMode == AudioManager.MODE_IN_COMMUNICATION;
+                    if (isInCall && (result & ACTION_PASS_TO_USER) == 0) {
+                        // If we are in call but we decided not to pass the key to
+                        // the application, just pass it to the session service.
+                        MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
+                                event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
+                        break;
+                    }
+
                 }
                 if (mUseTvRouting || mHandleVolumeKeysInWM) {
                     // Defer special key handlings to
@@ -6313,7 +6321,7 @@
                 try {
                     getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
                             AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e);
                 }
                 break;
@@ -6321,7 +6329,7 @@
                 try {
                     getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
                             AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e);
                 }
                 break;
@@ -6332,7 +6340,7 @@
                                 AudioManager.ADJUST_TOGGLE_MUTE,
                                 AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
                     }
-                } catch (RemoteException e) {
+                } catch (Exception e) {
                     Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e);
                 }
                 break;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 423bc0c..a8d19e9 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4607,6 +4607,9 @@
         } catch (IOException e) {
             Slog.e(TAG, "Failed to read last_reboot_reason file", e);
         }
+        if (line == null) {
+            return PowerManager.SHUTDOWN_REASON_UNKNOWN;
+        }
         switch (line) {
             case REASON_SHUTDOWN:
                 return PowerManager.SHUTDOWN_REASON_SHUTDOWN;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6a18beb..39b902e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -59,6 +59,7 @@
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.FileUtils;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.Process;
@@ -663,13 +664,35 @@
         @Override
         public void onServiceDisconnected(ComponentName name) {
             synchronized (mLock) {
+                Slog.w(TAG, "Wallpaper service gone: " + name);
+                if (!Objects.equals(name, mWallpaper.wallpaperComponent)) {
+                    Slog.e(TAG, "Does not match expected wallpaper component "
+                            + mWallpaper.wallpaperComponent);
+                }
                 mService = null;
                 mEngine = null;
                 if (mWallpaper.connection == this) {
-                    // The wallpaper disappeared.  If this isn't a system-default one, track
-                    // crashes and fall back to default if it continues to misbehave.
+                    // There is an inherent ordering race between this callback and the
+                    // package monitor that receives notice that a package is being updated,
+                    // so we cannot quite trust at this moment that we know for sure that
+                    // this is not an update.  If we think this is a genuine non-update
+                    // wallpaper outage, we do our "wait for reset" work as a continuation,
+                    // a short time in the future, specifically to allow any pending package
+                    // update message on this same looper thread to be processed.
+                    if (!mWallpaper.wallpaperUpdating) {
+                        mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
+                                1000);
+                    }
+                }
+            }
+        }
+
+        private void processDisconnect(final ServiceConnection connection) {
+            synchronized (mLock) {
+                // The wallpaper disappeared.  If this isn't a system-default one, track
+                // crashes and fall back to default if it continues to misbehave.
+                if (connection == mWallpaper.connection) {
                     final ComponentName wpService = mWallpaper.wallpaperComponent;
-                    Slog.w(TAG, "Wallpaper service gone: " + wpService);
                     if (!mWallpaper.wallpaperUpdating
                             && mWallpaper.userId == mCurrentUserId
                             && !Objects.equals(mDefaultWallpaperComponent, wpService)
@@ -682,7 +705,7 @@
                         // during {@link #MIN_WALLPAPER_CRASH_TIME} millis.
                         if (mWallpaper.lastDiedTime != 0
                                 && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
-                                    > SystemClock.uptimeMillis()) {
+                                > SystemClock.uptimeMillis()) {
                             Slog.w(TAG, "Reverting to built-in wallpaper!");
                             clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
                         } else {
@@ -690,18 +713,22 @@
 
                             // If we didn't reset it right away, do so after we couldn't connect to
                             // it for an extended amount of time to avoid having a black wallpaper.
-                            FgThread.getHandler().removeCallbacks(mResetRunnable);
-                            FgThread.getHandler().postDelayed(mResetRunnable,
-                                    WALLPAPER_RECONNECT_TIMEOUT_MS);
+                            final Handler fgHandler = FgThread.getHandler();
+                            fgHandler.removeCallbacks(mResetRunnable);
+                            fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
                             if (DEBUG_LIVE) {
                                 Slog.i(TAG, "Started wallpaper reconnect timeout for " + wpService);
                             }
                         }
-                        final String flattened = name.flattenToString();
+                        final String flattened = wpService.flattenToString();
                         EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
                                 flattened.substring(0, Math.min(flattened.length(),
                                         MAX_WALLPAPER_COMPONENT_LOG_LENGTH)));
                     }
+                } else {
+                    if (DEBUG_LIVE) {
+                        Slog.i(TAG, "Wallpaper changed during disconnect tracking; ignoring");
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 982561c..96ea5e5 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1248,11 +1248,11 @@
      */
     @Override
     int getOrientation(int candidate) {
-        // We do not allow non-fullscreen apps to influence orientation at and beyond O. While we do
+        // We do not allow non-fullscreen apps to influence orientation beyond O. While we do
         // throw an exception in {@link Activity#onCreate} and
         // {@link Activity#setRequestedOrientation}, we also ignore the orientation here so that
         // other calculations aren't affected.
-        if (!fillsParent() && mTargetSdk >= O) {
+        if (!fillsParent() && mTargetSdk > O) {
             // Can't specify orientation if app doesn't fill parent.
             return SCREEN_ORIENTATION_UNSET;
         }
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index b927e67..9c44c14 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 
 import android.app.ActivityManager.StackId;
 import android.app.RemoteAction;
@@ -285,13 +286,16 @@
             if (StackId.tasksAreFloating(mStackId)) {
                 // Floating tasks should not be resized to the screen's bounds.
 
-                if (bounds.width() == mTmpDisplayBounds.width() &&
+                if (mStackId == PINNED_STACK_ID && bounds.width() == mTmpDisplayBounds.width() &&
                         bounds.height() == mTmpDisplayBounds.height()) {
                     // If the bounds we are animating is the same as the fullscreen stack
                     // dimensions, then apply the same inset calculations that we normally do for
                     // the fullscreen stack, without intersecting it with the display bounds
                     stableBounds.inset(mTmpStableInsets);
                     nonDecorBounds.inset(mTmpNonDecorInsets);
+                    // Move app bounds to zero to apply intersection with parent correctly. They are
+                    // used only for evaluating width and height, so it's OK to move them around.
+                    config.appBounds.offsetTo(0, 0);
                     intersectParentBounds = true;
                 }
                 width = (int) (stableBounds.width() / density);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index b79173c..1bbe1d0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -275,6 +275,15 @@
         mPersister.removeObsoleteFiles(persistentTaskIds, runningUserIds);
     }
 
+    /**
+     * Temporarily pauses/unpauses persisting of task snapshots.
+     *
+     * @param paused Whether task snapshot persisting should be paused.
+     */
+    void setPersisterPaused(boolean paused) {
+        mPersister.setPaused(paused);
+    }
+
     void dump(PrintWriter pw, String prefix) {
         mCache.dump(pw, prefix);
     }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index e5c7a72..0287070 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -60,6 +60,8 @@
     private final ArrayDeque<WriteQueueItem> mWriteQueue = new ArrayDeque<>();
     @GuardedBy("mLock")
     private boolean mQueueIdling;
+    @GuardedBy("mLock")
+    private boolean mPaused;
     private boolean mStarted;
     private final Object mLock = new Object();
     private final DirectoryResolver mDirectoryResolver;
@@ -127,6 +129,15 @@
         }
     }
 
+    void setPaused(boolean paused) {
+        synchronized (mLock) {
+            mPaused = paused;
+            if (!paused) {
+                mLock.notifyAll();
+            }
+        }
+    }
+
     @TestApi
     void waitForQueueEmpty() {
         while (true) {
@@ -142,7 +153,9 @@
     @GuardedBy("mLock")
     private void sendToQueueLocked(WriteQueueItem item) {
         mWriteQueue.offer(item);
-        mLock.notifyAll();
+        if (!mPaused) {
+            mLock.notifyAll();
+        }
     }
 
     private File getDirectory(int userId) {
@@ -185,7 +198,11 @@
             while (true) {
                 WriteQueueItem next;
                 synchronized (mLock) {
-                    next = mWriteQueue.poll();
+                    if (mPaused) {
+                        next = null;
+                    } else {
+                        next = mWriteQueue.poll();
+                    }
                 }
                 if (next != null) {
                     next.write();
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 067cc09..dd790a9 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -255,12 +255,19 @@
             mWindowPlacerLocked.requestTraversal();
         }
 
-        if (mAnimating && !wasAnimating && Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
-            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+        if (mAnimating && !wasAnimating) {
+
+            // Usually app transitions but quite a load onto the system already (with all the things
+            // happening in app), so pause task snapshot persisting to not increase the load.
+            mService.mTaskSnapshotController.setPersisterPaused(true);
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+                Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+            }
         }
 
         if (!mAnimating && wasAnimating) {
             mWindowPlacerLocked.requestTraversal();
+            mService.mTaskSnapshotController.setPersisterPaused(false);
             if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
                 Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
             }
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
index 0cf4994..262516d 100644
--- a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
@@ -18,20 +18,19 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.when;
 
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.app.Notification.Builder;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
-import android.content.Context;
 import android.os.UserHandle;
+import android.provider.Settings.Secure;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -43,7 +42,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class BadgeExtractorTest {
+public class BadgeExtractorTest extends NotificationTestCase {
 
     @Mock RankingConfig mConfig;
 
@@ -59,7 +58,11 @@
         MockitoAnnotations.initMocks(this);
     }
 
-    private NotificationRecord getNotificationRecord(NotificationChannel channel) {
+    private NotificationRecord getNotificationRecord(boolean showBadge, int importanceHigh) {
+        NotificationChannel channel = new NotificationChannel("a", "a", importanceHigh);
+        channel.setShowBadge(showBadge);
+        when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
+
         final Builder builder = new Builder(getContext())
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -73,10 +76,6 @@
         return r;
     }
 
-    private Context getContext() {
-        return InstrumentationRegistry.getTargetContext();
-    }
-
     //
     // Tests
     //
@@ -86,13 +85,9 @@
         BadgeExtractor extractor = new BadgeExtractor();
         extractor.setConfig(mConfig);
 
+        when(mConfig.badgingEnabled(mUser)).thenReturn(true);
         when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
-        NotificationChannel channel =
-                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
-        when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
-        channel.setShowBadge(false);
-
-        NotificationRecord r = getNotificationRecord(channel);
+        NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
 
         extractor.process(r);
 
@@ -104,13 +99,9 @@
         BadgeExtractor extractor = new BadgeExtractor();
         extractor.setConfig(mConfig);
 
+        when(mConfig.badgingEnabled(mUser)).thenReturn(true);
         when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
-        NotificationChannel channel =
-                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH);
-        channel.setShowBadge(true);
-        when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
-
-        NotificationRecord r = getNotificationRecord(channel);
+        NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
 
         extractor.process(r);
 
@@ -122,13 +113,9 @@
         BadgeExtractor extractor = new BadgeExtractor();
         extractor.setConfig(mConfig);
 
+        when(mConfig.badgingEnabled(mUser)).thenReturn(true);
         when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
-        NotificationChannel channel =
-                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
-        channel.setShowBadge(true);
-        when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
-
-        NotificationRecord r = getNotificationRecord(channel);
+        NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
 
         extractor.process(r);
 
@@ -140,13 +127,23 @@
         BadgeExtractor extractor = new BadgeExtractor();
         extractor.setConfig(mConfig);
 
+        when(mConfig.badgingEnabled(mUser)).thenReturn(true);
         when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(false);
-        NotificationChannel channel =
-                new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
-        channel.setShowBadge(false);
-        when(mConfig.getNotificationChannel(mPkg, mUid, "a", false)).thenReturn(channel);
+        NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
 
-        NotificationRecord r = getNotificationRecord(channel);
+        extractor.process(r);
+
+        assertFalse(r.canShowBadge());
+    }
+
+    @Test
+    public void testAppYesChannelYesUserNo() throws Exception {
+        BadgeExtractor extractor = new BadgeExtractor();
+        extractor.setConfig(mConfig);
+
+        when(mConfig.badgingEnabled(mUser)).thenReturn(false);
+        when(mConfig.canShowBadge(mPkg, mUid)).thenReturn(true);
+        NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
 
         extractor.process(r);
 
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index d4904f5..39caa3c 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -34,7 +34,6 @@
 import android.app.Notification;
 import android.app.Notification.Builder;
 import android.app.NotificationManager;
-import android.content.Context;
 import android.app.NotificationChannel;
 import android.graphics.Color;
 import android.media.AudioAttributes;
@@ -47,7 +46,6 @@
 import android.os.VibrationEffect;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -69,7 +67,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class BuzzBeepBlinkTest {
+public class BuzzBeepBlinkTest extends NotificationTestCase {
 
     @Mock AudioManager mAudioManager;
     @Mock Vibrator mVibrator;
@@ -328,10 +326,6 @@
                 eq(CUSTOM_LIGHT_COLOR), anyInt(), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF));
     }
 
-    private Context getContext() {
-        return InstrumentationRegistry.getTargetContext();
-    }
-
     //
     // Tests
     //
diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
index 24cb72e..f92bd84 100644
--- a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
@@ -25,7 +25,6 @@
 import android.app.NotificationManager;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -38,7 +37,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class GlobalSortKeyComparatorTest {
+public class GlobalSortKeyComparatorTest extends NotificationTestCase {
 
     private final String PKG = "PKG";
     private final int UID = 1111111;
@@ -46,24 +45,23 @@
 
     @Test
     public void testComparator() throws Exception {
-        Notification n = new Notification.Builder(
-                InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+        Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
                 .build();
-        NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+        NotificationRecord left = new NotificationRecord(getContext(),
                 new StatusBarNotification(PKG,
                         PKG, 1, "media", UID, UID, n,
                         new UserHandle(UserHandle.myUserId()),
                         "", 1499), getDefaultChannel());
         left.setGlobalSortKey("first");
 
-        NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+        NotificationRecord right = new NotificationRecord(getContext(),
                 new StatusBarNotification(PKG,
                         PKG, 1, "media", UID, UID, n,
                         new UserHandle(UserHandle.myUserId()),
                         "", 1499), getDefaultChannel());
         right.setGlobalSortKey("second");
 
-        NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(),
+        NotificationRecord last = new NotificationRecord(getContext(),
                 new StatusBarNotification(PKG,
                         PKG, 1, "media", UID, UID, n,
                         new UserHandle(UserHandle.myUserId()),
@@ -86,16 +84,15 @@
 
     @Test
     public void testNoCrash_leftNull() throws Exception {
-        Notification n = new Notification.Builder(
-                InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+        Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
                 .build();
-        NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+        NotificationRecord left = new NotificationRecord(getContext(),
                 new StatusBarNotification(PKG,
                         PKG, 1, "media", UID, UID, n,
                         new UserHandle(UserHandle.myUserId()),
                         "", 1499), getDefaultChannel());
 
-        NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+        NotificationRecord right = new NotificationRecord(getContext(),
                 new StatusBarNotification(PKG,
                         PKG, 1, "media", UID, UID, n,
                         new UserHandle(UserHandle.myUserId()),
@@ -117,17 +114,16 @@
 
     @Test
     public void testNoCrash_rightNull() throws Exception {
-        Notification n = new Notification.Builder(
-                InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+        Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
                 .build();
-        NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+        NotificationRecord left = new NotificationRecord(getContext(),
                 new StatusBarNotification(PKG,
                         PKG, 1, "media", UID, UID, n,
                         new UserHandle(UserHandle.myUserId()),
                         "", 1499), getDefaultChannel());
         left.setGlobalSortKey("not null");
 
-        NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+        NotificationRecord right = new NotificationRecord(getContext(),
                 new StatusBarNotification(PKG,
                         PKG, 1, "media", UID, UID, n,
                         new UserHandle(UserHandle.myUserId()),
diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
index 05c33a4..8dd1779 100644
--- a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
@@ -34,10 +34,8 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -46,15 +44,11 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class GroupHelperTest {
+public class GroupHelperTest extends NotificationTestCase {
     private @Mock GroupHelper.Callback mCallback;
 
     private GroupHelper mGroupHelper;
 
-    private Context getContext() {
-        return InstrumentationRegistry.getTargetContext();
-    }
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
index 3dbd803..d325e10 100644
--- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -24,10 +24,8 @@
 import android.app.Notification.Builder;
 import android.app.NotificationManager;
 import android.app.NotificationChannel;
-import android.content.Context;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -43,7 +41,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class ImportanceExtractorTest {
+public class ImportanceExtractorTest extends NotificationTestCase {
 
     @Mock RankingConfig mConfig;
 
@@ -75,10 +73,6 @@
         return r;
     }
 
-    private Context getContext() {
-        return InstrumentationRegistry.getTargetContext();
-    }
-
     //
     // Tests
     //
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
index dde08fc..1e5f96f 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
@@ -35,7 +35,6 @@
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.telecom.TelecomManager;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -51,7 +50,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class NotificationComparatorTest {
+public class NotificationComparatorTest extends NotificationTestCase {
     @Mock Context mContext;
     @Mock TelecomManager mTm;
     @Mock RankingHandler handler;
@@ -83,10 +82,8 @@
         MockitoAnnotations.initMocks(this);
         int userId = UserHandle.myUserId();
 
-        when(mContext.getResources()).thenReturn(
-                InstrumentationRegistry.getTargetContext().getResources());
-        when(mContext.getContentResolver()).thenReturn(
-                InstrumentationRegistry.getTargetContext().getContentResolver());
+        when(mContext.getResources()).thenReturn(getContext().getResources());
+        when(mContext.getContentResolver()).thenReturn(getContext().getContentResolver());
         when(mContext.getPackageManager()).thenReturn(mPm);
         when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
         when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
index f0f4c4d..725e8f2 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -38,7 +38,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class NotificationListenerServiceTest {
+public class NotificationListenerServiceTest extends NotificationTestCase {
 
     private String[] mKeys = new String[] { "key", "key1", "key2", "key3"};
 
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 177c02d..9afb2d2 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -52,9 +52,9 @@
 import android.os.Binder;
 import android.os.Process;
 import android.os.UserHandle;
+import android.provider.Settings.Secure;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -75,7 +75,7 @@
 
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
-public class NotificationManagerServiceTest {
+public class NotificationManagerServiceTest extends NotificationTestCase {
     private static final long WAIT_FOR_IDLE_TIMEOUT = 2;
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
     private final int uid = Binder.getCallingUid();
@@ -86,7 +86,7 @@
     private IPackageManager mPackageManager;
     @Mock
     private PackageManager mPackageManagerClient;
-    private Context mContext = InstrumentationRegistry.getTargetContext();
+    private Context mContext = getContext();
     private final String PKG = mContext.getPackageName();
     private TestableLooper mTestableLooper;
     @Mock
@@ -122,6 +122,12 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+
+        // most tests assume badging is enabled
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 1,
+                UserHandle.getUserHandleForUid(uid).getIdentifier());
+
         mNotificationManagerService = new TestableNotificationManagerService(mContext);
 
         // MockPackageManager - default returns ApplicationInfo with matching calling UID
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
index 1c8ca84..267d2a6 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -40,7 +40,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -56,7 +55,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class NotificationRecordTest {
+public class NotificationRecordTest extends NotificationTestCase {
 
     private final Context mMockContext = Mockito.mock(Context.class);
     @Mock PackageManager mPm;
@@ -96,8 +95,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        when(mMockContext.getResources()).thenReturn(
-                InstrumentationRegistry.getContext().getResources());
+        when(mMockContext.getResources()).thenReturn(getContext().getResources());
         when(mMockContext.getPackageManager()).thenReturn(mPm);
 
         legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java b/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java
new file mode 100644
index 0000000..cc30aab
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.server.notification;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.testing.TestableContext;
+
+import org.junit.Rule;
+
+
+public class NotificationTestCase {
+    @Rule
+    public final TestableContext mContext =
+            new TestableContext(InstrumentationRegistry.getContext(), null);
+
+    protected Context getContext() {
+        return mContext;
+    }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 73372d5..0f8c815 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -49,12 +49,16 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.UserHandle;
+import android.provider.Settings.Secure;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContext;
+import android.testing.TestableSettingsProvider;
 import android.util.ArrayMap;
+import android.util.Slog;
 import android.util.Xml;
 
 import java.io.BufferedInputStream;
@@ -80,16 +84,19 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class RankingHelperTest {
+public class RankingHelperTest extends NotificationTestCase {
     private static final String PKG = "com.android.server.notification";
     private static final int UID = 0;
+    private static final UserHandle USER = UserHandle.getUserHandleForUid(UID);
     private static final String UPDATED_PKG = "updatedPkg";
     private static final int UID2 = 1111111;
+    private static final UserHandle USER2 = UserHandle.getUserHandleForUid(UID2);
     private static final String TEST_CHANNEL_ID = "test_channel_id";
 
     @Mock NotificationUsageStats mUsageStats;
     @Mock RankingHandler mHandler;
     @Mock PackageManager mPm;
+    @Mock Context mContext;
 
     private Notification mNotiGroupGSortA;
     private Notification mNotiGroupGSortB;
@@ -104,69 +111,11 @@
     private RankingHelper mHelper;
     private AudioAttributes mAudioAttributes;
 
-    private Context getContext() {
-        return InstrumentationRegistry.getTargetContext();
-    }
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         UserHandle user = UserHandle.ALL;
 
-        mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats,
-                new String[] {ImportanceExtractor.class.getName()});
-
-        mNotiGroupGSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
-                .setContentTitle("A")
-                .setGroup("G")
-                .setSortKey("A")
-                .setWhen(1205)
-                .build();
-        mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, mNotiGroupGSortA, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
-
-        mNotiGroupGSortB = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
-                .setContentTitle("B")
-                .setGroup("G")
-                .setSortKey("B")
-                .setWhen(1200)
-                .build();
-        mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, mNotiGroupGSortB, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
-
-        mNotiNoGroup = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
-                .setContentTitle("C")
-                .setWhen(1201)
-                .build();
-        mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, mNotiNoGroup, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
-
-        mNotiNoGroup2 = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
-                .setContentTitle("D")
-                .setWhen(1202)
-                .build();
-        mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, mNotiNoGroup2, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
-
-        mNotiNoGroupSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
-                .setContentTitle("E")
-                .setWhen(1201)
-                .setSortKey("A")
-                .build();
-        mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
-                "package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user,
-                null, System.currentTimeMillis()), getDefaultChannel());
-
-        mAudioAttributes = new AudioAttributes.Builder()
-                .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
-                .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
-                .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
-                .build();
-
         final ApplicationInfo legacy = new ApplicationInfo();
         legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
         final ApplicationInfo upgrade = new ApplicationInfo();
@@ -174,6 +123,67 @@
         when(mPm.getApplicationInfoAsUser(eq(PKG), anyInt(), anyInt())).thenReturn(legacy);
         when(mPm.getApplicationInfoAsUser(eq(UPDATED_PKG), anyInt(), anyInt())).thenReturn(upgrade);
         when(mPm.getPackageUidAsUser(eq(PKG), anyInt())).thenReturn(UID);
+        when(mContext.getResources()).thenReturn(
+                InstrumentationRegistry.getContext().getResources());
+        when(mContext.getPackageManager()).thenReturn(mPm);
+        when(mContext.getApplicationInfo()).thenReturn(legacy);
+        // most tests assume badging is enabled
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID));
+
+        mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats,
+                new String[] {ImportanceExtractor.class.getName()});
+
+        mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+                .setContentTitle("A")
+                .setGroup("G")
+                .setSortKey("A")
+                .setWhen(1205)
+                .build();
+        mRecordGroupGSortA = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, null, 0, 0, mNotiGroupGSortA, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
+
+        mNotiGroupGSortB = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+                .setContentTitle("B")
+                .setGroup("G")
+                .setSortKey("B")
+                .setWhen(1200)
+                .build();
+        mRecordGroupGSortB = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, null, 0, 0, mNotiGroupGSortB, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
+
+        mNotiNoGroup = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+                .setContentTitle("C")
+                .setWhen(1201)
+                .build();
+        mRecordNoGroup = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, null, 0, 0, mNotiNoGroup, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
+
+        mNotiNoGroup2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+                .setContentTitle("D")
+                .setWhen(1202)
+                .build();
+        mRecordNoGroup2 = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, null, 0, 0, mNotiNoGroup2, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
+
+        mNotiNoGroupSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+                .setContentTitle("E")
+                .setWhen(1201)
+                .setSortKey("A")
+                .build();
+        mRecordNoGroupSortA = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 1, null, 0, 0, mNotiNoGroupSortA, user,
+                null, System.currentTimeMillis()), getDefaultChannel());
+
+        mAudioAttributes = new AudioAttributes.Builder()
+                .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+                .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+                .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
+                .build();
     }
 
     private NotificationChannel getDefaultChannel() {
@@ -229,6 +239,10 @@
         assertEquals(expected.getName(), actual.getName());
     }
 
+    private NotificationChannel getChannel() {
+        return new NotificationChannel("id", "name", IMPORTANCE_LOW);
+    }
+
     @Test
     public void testFindAfterRankingWithASplitGroup() throws Exception {
         ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
@@ -644,14 +658,119 @@
     }
 
     @Test
+    public void testClearLockedFields() throws Exception {
+        final NotificationChannel channel = getChannel();
+        mHelper.clearLockedFields(channel);
+        assertEquals(0, channel.getUserLockedFields());
+
+        channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY
+                | NotificationChannel.USER_LOCKED_IMPORTANCE);
+        mHelper.clearLockedFields(channel);
+        assertEquals(0, channel.getUserLockedFields());
+    }
+
+    @Test
+    public void testLockFields_soundAndVibration() throws Exception {
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+
+        final NotificationChannel update1 = getChannel();
+        update1.setSound(new Uri.Builder().scheme("test").build(),
+                new AudioAttributes.Builder().build());
+        update1.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); // should be ignored
+        mHelper.updateNotificationChannel(PKG, UID, update1);
+        assertEquals(NotificationChannel.USER_LOCKED_SOUND,
+                mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+                        .getUserLockedFields());
+
+        NotificationChannel update2 = getChannel();
+        update2.enableVibration(true);
+        mHelper.updateNotificationChannel(PKG, UID, update2);
+        assertEquals(NotificationChannel.USER_LOCKED_SOUND
+                        | NotificationChannel.USER_LOCKED_VIBRATION,
+                mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+                        .getUserLockedFields());
+    }
+
+    @Test
+    public void testLockFields_vibrationAndLights() throws Exception {
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+
+        final NotificationChannel update1 = getChannel();
+        update1.setVibrationPattern(new long[]{7945, 46 ,246});
+        mHelper.updateNotificationChannel(PKG, UID, update1);
+        assertEquals(NotificationChannel.USER_LOCKED_VIBRATION,
+                mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update2 = getChannel();
+        update2.enableLights(true);
+        mHelper.updateNotificationChannel(PKG, UID, update2);
+        assertEquals(NotificationChannel.USER_LOCKED_VIBRATION
+                        | NotificationChannel.USER_LOCKED_LIGHTS,
+                mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+                        .getUserLockedFields());
+    }
+
+    @Test
+    public void testLockFields_lightsAndImportance() throws Exception {
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+
+        final NotificationChannel update1 = getChannel();
+        update1.setLightColor(Color.GREEN);
+        mHelper.updateNotificationChannel(PKG, UID, update1);
+        assertEquals(NotificationChannel.USER_LOCKED_LIGHTS,
+                mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update2 = getChannel();
+        update2.setImportance(IMPORTANCE_DEFAULT);
+        mHelper.updateNotificationChannel(PKG, UID, update2);
+        assertEquals(NotificationChannel.USER_LOCKED_LIGHTS
+                        | NotificationChannel.USER_LOCKED_IMPORTANCE,
+                mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+                        .getUserLockedFields());
+    }
+
+    @Test
+    public void testLockFields_visibilityAndDndAndBadge() throws Exception {
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        assertEquals(0,
+                mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update1 = getChannel();
+        update1.setBypassDnd(true);
+        mHelper.updateNotificationChannel(PKG, UID, update1);
+        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY,
+                mHelper.getNotificationChannel(PKG, UID, update1.getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update2 = getChannel();
+        update2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+        mHelper.updateNotificationChannel(PKG, UID, update2);
+        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+                        | NotificationChannel.USER_LOCKED_VISIBILITY,
+                mHelper.getNotificationChannel(PKG, UID, update2.getId(), false)
+                        .getUserLockedFields());
+
+        final NotificationChannel update3 = getChannel();
+        update3.setShowBadge(false);
+        mHelper.updateNotificationChannel(PKG, UID, update3);
+        assertEquals(NotificationChannel.USER_LOCKED_PRIORITY
+                        | NotificationChannel.USER_LOCKED_VISIBILITY
+                        | NotificationChannel.USER_LOCKED_SHOW_BADGE,
+                mHelper.getNotificationChannel(PKG, UID, update3.getId(), false)
+                        .getUserLockedFields());
+    }
+
+    @Test
     public void testDeleteNonExistentChannel() throws Exception {
         mHelper.deleteNotificationChannelGroup(PKG, UID, "does not exist");
     }
 
     @Test
     public void testGetDeletedChannel() throws Exception {
-        NotificationChannel channel =
-                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        NotificationChannel channel = getChannel();
         channel.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
         channel.enableLights(true);
         channel.setBypassDnd(true);
@@ -1093,4 +1212,36 @@
                     object.getInt("channelCount"));
         }
     }
+
+    @Test
+    public void testBadgingOverrideTrue() throws Exception {
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 1,
+                USER.getIdentifier());
+        mHelper.updateBadgingEnabled(); // would be called by settings observer
+        assertTrue(mHelper.badgingEnabled(USER));
+    }
+
+    @Test
+    public void testBadgingOverrideFalse() throws Exception {
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 0,
+                USER.getIdentifier());
+        mHelper.updateBadgingEnabled(); // would be called by settings observer
+        assertFalse(mHelper.badgingEnabled(USER));
+    }
+
+    @Test
+    public void testBadgingOverrideUserIsolation() throws Exception {
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 0,
+                USER.getIdentifier());
+        Secure.putIntForUser(getContext().getContentResolver(),
+                Secure.NOTIFICATION_BADGING, 1,
+                USER2.getIdentifier());
+        mHelper.updateBadgingEnabled(); // would be called by settings observer
+        assertFalse(mHelper.badgingEnabled(USER));
+        assertTrue(mHelper.badgingEnabled(USER2));
+    }
+
 }
diff --git a/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
index 07f3162..e354267 100644
--- a/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
@@ -26,7 +26,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class RateEstimatorTest {
+public class RateEstimatorTest extends NotificationTestCase {
     private long mTestStartTime;
     private RateEstimator mEstimator;
 
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
index bc25860..07b21fb 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -27,11 +27,9 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Slog;
@@ -51,7 +49,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class SnoozeHelperTest {
+public class SnoozeHelperTest extends NotificationTestCase {
     private static final String TEST_CHANNEL_ID = "test_channel_id";
 
     @Mock SnoozeHelper.Callback mCallback;
@@ -60,10 +58,6 @@
 
     private SnoozeHelper mSnoozeHelper;
 
-    private Context getContext() {
-        return InstrumentationRegistry.getTargetContext();
-    }
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index d09b858..4ac0c65 100644
--- a/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -32,7 +32,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-public class ValidateNotificationPeopleTest {
+public class ValidateNotificationPeopleTest extends NotificationTestCase {
 
     @Test
     public void testNoExtra() throws Exception {
diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java
index 13056cf..fe97bca1 100644
--- a/tests/testables/src/android/testing/TestableSettingsProvider.java
+++ b/tests/testables/src/android/testing/TestableSettingsProvider.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.test.mock.MockContentProvider;
 import android.util.Log;
@@ -48,9 +49,10 @@
     }
 
     void clearValuesAndCheck(Context context) {
-        mValues.put(key("global", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
-        mValues.put(key("secure", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
-        mValues.put(key("system", MY_UNIQUE_KEY), MY_UNIQUE_KEY);
+        int userId = UserHandle.myUserId();
+        mValues.put(key("global", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
+        mValues.put(key("secure", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
+        mValues.put(key("system", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
 
         // Verify that if any test is using TestableContext, they all have the correct settings
         // provider.
@@ -66,11 +68,12 @@
 
     public Bundle call(String method, String arg, Bundle extras) {
         // Methods are "GET_system", "GET_global", "PUT_secure", etc.
+        final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, 0);
         final String[] commands = method.split("_", 2);
         final String op = commands[0];
         final String table = commands[1];
 
-            String k = key(table, arg);
+            String k = key(table, arg, userId);
             String value;
             Bundle out = new Bundle();
             switch (op) {
@@ -103,8 +106,13 @@
             return out;
     }
 
-    private static String key(String table, String key) {
-        return table + "_" + key;
+    private static String key(String table, String key, int userId) {
+        if ("global".equals(table)) {
+            return table + "_" + key;
+        } else {
+            return table + "_" + userId + "_" + key;
+        }
+
     }
 
     /**
diff --git a/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java b/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
index 1f71867..0e2cc57 100644
--- a/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
+++ b/tests/testables/tests/src/android/testing/TestableSettingsProviderTest.java
@@ -66,6 +66,16 @@
     }
 
     @Test
+    public void testSeparateUsers() {
+        Secure.putStringForUser(mContentResolver, NONEXISTENT_SETTING, "something", 0);
+        Secure.putStringForUser(mContentResolver, NONEXISTENT_SETTING, "else", 1);
+        assertEquals("something",
+                Secure.getStringForUser(mContentResolver, NONEXISTENT_SETTING, 0));
+        assertEquals("else",
+                Secure.getStringForUser(mContentResolver, NONEXISTENT_SETTING, 1));
+    }
+
+    @Test
     public void testPassThrough() {
         // Grab the value of a setting that is not overridden.
         assertTrue(Secure.getInt(mContentResolver, Secure.USER_SETUP_COMPLETE, 0) != 0);
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 15648bd..ba73180 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -762,6 +762,15 @@
         return 1;
     }
 
+    // Now add any dependencies passed in.
+    for (size_t i = 0; i < bundle->getPackageIncludes().size(); i++) {
+      const String8& assetPath = bundle->getPackageIncludes()[i];
+      if (!assets.addAssetPath(assetPath, NULL)) {
+        fprintf(stderr, "ERROR: included asset path %s could not be loaded\n", assetPath.string());
+        return 1;
+      }
+    }
+
     // Make a dummy config for retrieving resources...  we need to supply
     // non-default values for some configs so that we can retrieve resources
     // in the app that don't have a default.  The most important of these is
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 01930d0..e45d142 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -27,7 +27,7 @@
 static const char* sMajorVersion = "2";
 
 // Update minor version whenever a feature or flag is added.
-static const char* sMinorVersion = "14";
+static const char* sMinorVersion = "15";
 
 int PrintVersion() {
   std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 5413b33..c192d69 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -114,6 +114,7 @@
   std::string output_path;
   Maybe<std::string> res_dir;
   bool pseudolocalize = false;
+  bool no_png_crunch = false;
   bool legacy_mode = false;
   bool verbose = false;
 };
@@ -663,6 +664,7 @@
                           "Generate resources for pseudo-locales "
                           "(en-XA and ar-XB)",
                           &options.pseudolocalize)
+          .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)
           .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
                           &options.legacy_mode)
           .OptionalSwitch("-v", "Enables verbose logging", &verbose);
@@ -738,7 +740,8 @@
             if (!CompileXml(&context, options, path_data, archive_writer.get(), output_filename)) {
               error = true;
             }
-          } else if (path_data.extension == "png" || path_data.extension == "9.png") {
+          } else if (!options.no_png_crunch &&
+                     (path_data.extension == "png" || path_data.extension == "9.png")) {
             if (!CompilePng(&context, options, path_data, archive_writer.get(), output_filename)) {
               error = true;
             }
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 0291720..e92565f 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,5 +1,13 @@
 # Android Asset Packaging Tool 2.0 (AAPT2) release notes
 
+## Version 2.15
+### `aapt2 compile ...`
+- Add `--no-crunch` option to avoid processing PNGs during the compile phase. Note that this
+  shouldn't be used as a performance optimization, as once the PNG is processed, its result is
+  cached for incremental linking. This should only be used if the developer has specially
+  pre-processed the PNG and wants it byte-for-byte identical to the input.
+  NOTE: 9-patches will not be processed correctly with this flag set.
+
 ## Version 2.14
 ### `aapt2 link ...`
 - If an app is building with a minSdkVersion < 26 and a --package-id XX where XX > 7F, aapt2
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf
deleted file mode 100644
index 2852302..0000000
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont.ttf
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf
deleted file mode 100644
index b7bf5b4..0000000
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/font/testfont2.ttf
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 2b5e0f9..7b565b1 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -389,7 +389,8 @@
         assertTrue(sRenderMessages.isEmpty());
     }
 
-    @Test
+    //@Test
+    // Temporarily disabled (b/37725933)
     public void testFonts() throws ClassNotFoundException {
         // TODO: styles seem to be broken in TextView
         renderAndVerify("fonts_test.xml", "font_test.png");
diff --git a/vr/Android.mk b/vr/Android.mk
new file mode 100644
index 0000000..5b65d3f
--- /dev/null
+++ b/vr/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2017 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.
+LOCAL_PATH := $(call my-dir)
+
+# Library to perform dlopen on the actual shared library.
+include $(CLEAR_VARS)
+LOCAL_MODULE := libdvr_loader
+LOCAL_MODULE_OWNER := google
+LOCAL_SRC_FILES := dvr_library_loader.cpp
+include $(BUILD_SHARED_LIBRARY)
+
+# Java platform library for vr stuff.
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.google.vr.platform
+LOCAL_MODULE_OWNER := google
+LOCAL_REQUIRED_MODULES := libdvr_loader libdvr
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := com.google.vr.platform.xml
+LOCAL_SRC_FILES := com.google.vr.platform.xml
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_OWNER := google
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
+include $(BUILD_PREBUILT)
diff --git a/vr/com.google.vr.platform.xml b/vr/com.google.vr.platform.xml
new file mode 100644
index 0000000..952b476
--- /dev/null
+++ b/vr/com.google.vr.platform.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<permissions>
+    <library name="com.google.vr.platform"
+             file="/system/framework/com.google.vr.platform.jar" />
+</permissions>
diff --git a/vr/dvr_library_loader.cpp b/vr/dvr_library_loader.cpp
new file mode 100644
index 0000000..0b4298a
--- /dev/null
+++ b/vr/dvr_library_loader.cpp
@@ -0,0 +1,25 @@
+#include <dlfcn.h>
+#include <jni.h>
+
+#include <string>
+
+extern "C" {
+
+JNIEXPORT jlong JNICALL
+Java_com_google_vr_platform_Dvr_nativeLoadLibrary(
+    JNIEnv* env, jclass, jstring java_library) {
+  if (!java_library)
+    return 0;
+
+  // Convert the Java String object to a C++ null-terminated string.
+  const char* data = env->GetStringUTFChars(java_library, NULL);
+  size_t size = env->GetStringUTFLength(java_library);
+  std::string library(data, size);
+  env->ReleaseStringUTFChars(java_library, data);
+
+  // Return the handle to the requested library.
+  return reinterpret_cast<jlong>(
+      dlopen(library.c_str(), RTLD_NOW | RTLD_LOCAL));
+}
+
+}  // extern "C"
diff --git a/vr/java/com/google/vr/platform/DeviceInfo.java b/vr/java/com/google/vr/platform/DeviceInfo.java
new file mode 100644
index 0000000..f6da66b
--- /dev/null
+++ b/vr/java/com/google/vr/platform/DeviceInfo.java
@@ -0,0 +1,19 @@
+package com.google.vr.platform;
+
+import android.os.SystemProperties;
+
+/**
+ * Class to get information about the vr device.
+ * @hide
+ */
+public class DeviceInfo {
+
+    private static final String VR_MODE_BOOT = "ro.boot.vr";
+
+    /**
+     * Returns true if this device boots directly in VR mode.
+     */
+    public static boolean getVrBoot() {
+        return SystemProperties.getBoolean(VR_MODE_BOOT, false);
+    }
+}
diff --git a/vr/java/com/google/vr/platform/Dvr.java b/vr/java/com/google/vr/platform/Dvr.java
new file mode 100644
index 0000000..b07d634
--- /dev/null
+++ b/vr/java/com/google/vr/platform/Dvr.java
@@ -0,0 +1,22 @@
+package com.google.vr.platform;
+
+/**
+ * Class to load the dvr api.
+ * @hide
+ */
+public class Dvr {
+    /**
+     * Opens a shared library containing the dvr api and returns the handle to it.
+     *
+     * @return A Long object describing the handle returned by dlopen.
+     */
+    public static Long loadLibrary() {
+        // Load a thin JNI library that runs dlopen on request.
+        System.loadLibrary("dvr_loader");
+
+        // Performs dlopen on the library and returns the handle.
+        return nativeLoadLibrary("libdvr.so");
+    }
+
+    private static native long nativeLoadLibrary(String library);
+}