Merge "Use feature instead of hidden config for multi-display" into oc-dev
diff --git a/api/current.txt b/api/current.txt
index c7b1e27..cbe4195 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22858,6 +22858,7 @@
     field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
     field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
+    field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
     field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
     field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
     field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
@@ -22866,6 +22867,7 @@
     field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
     field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
+    field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
@@ -37039,11 +37041,11 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
     field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
-    field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3
-    field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; // 0x5
+    field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
+    field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
     field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0
     field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1
-    field public static final int SAVE_DATA_TYPE_USERNAME = 4; // 0x4
+    field public static final int SAVE_DATA_TYPE_USERNAME = 8; // 0x8
   }
 
   public static final class SaveInfo.Builder {
diff --git a/api/system-current.txt b/api/system-current.txt
index 334d024..34f05da 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24683,6 +24683,7 @@
     field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
     field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
+    field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
     field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
     field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
     field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
@@ -24691,6 +24692,7 @@
     field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
     field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
+    field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
@@ -40135,11 +40137,11 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
     field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
-    field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3
-    field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; // 0x5
+    field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
+    field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
     field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0
     field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1
-    field public static final int SAVE_DATA_TYPE_USERNAME = 4; // 0x4
+    field public static final int SAVE_DATA_TYPE_USERNAME = 8; // 0x8
   }
 
   public static final class SaveInfo.Builder {
diff --git a/api/test-current.txt b/api/test-current.txt
index 8ecb50a..ac49a9b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -22971,6 +22971,7 @@
     field public static final int MEDIA_ERROR_TIMED_OUT = -110; // 0xffffff92
     field public static final int MEDIA_ERROR_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_ERROR_UNSUPPORTED = -1010; // 0xfffffc0e
+    field public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804; // 0x324
     field public static final int MEDIA_INFO_BAD_INTERLEAVING = 800; // 0x320
     field public static final int MEDIA_INFO_BUFFERING_END = 702; // 0x2be
     field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
@@ -22979,6 +22980,7 @@
     field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
     field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
+    field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
@@ -37197,11 +37199,11 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
     field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
-    field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3; // 0x3
-    field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5; // 0x5
+    field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
+    field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
     field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0
     field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1
-    field public static final int SAVE_DATA_TYPE_USERNAME = 4; // 0x4
+    field public static final int SAVE_DATA_TYPE_USERNAME = 8; // 0x8
   }
 
   public static final class SaveInfo.Builder {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index d620a81..3191eec 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -44,6 +44,8 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
 import java.util.Objects;
 import java.util.WeakHashMap;
 import java.util.function.Predicate;
@@ -114,7 +116,7 @@
      * A cache of DisplayId, Resources to Display. These display adjustments associated with these
      * {@link Display}s will change as the resources change.
      */
-    private final ArrayMap<Pair<Integer, Resources>, WeakReference<Display>> mResourceDisplays =
+    private final ArrayMap<Pair<Integer, ResourcesKey>, WeakReference<Display>> mResourceDisplays =
         new ArrayMap<>();
 
     public static ResourcesManager getInstance() {
@@ -137,10 +139,7 @@
             for (int i = 0; i < mResourceImpls.size();) {
                 final ResourcesKey key = mResourceImpls.keyAt(i);
                 if (key.isPathReferenced(path)) {
-                    final ResourcesImpl res = mResourceImpls.removeAt(i).get();
-                    if (res != null) {
-                        res.flushLayoutCache();
-                    }
+                    cleanupResourceImpl(key);
                     count++;
                 } else {
                     i++;
@@ -251,8 +250,14 @@
      * @param resources The {@link Resources} backing the display adjustments.
      */
     public Display getAdjustedDisplay(final int displayId, Resources resources) {
-        final Pair<Integer, Resources> key = Pair.create(displayId, resources);
         synchronized (this) {
+            // Note that the ResourcesKey might be {@code null} in the case that the
+            // {@link Resources} is actually from {@link Resources#getSystem}. In this case, it is
+            // not managed by {@link ResourcesManager}, but we still want to cache the display
+            // object.
+            final Pair<Integer, ResourcesKey> key = Pair.create(displayId,
+                    findKeyForResourceImplLocked(resources.getImpl()));
+
             WeakReference<Display> wd = mResourceDisplays.get(key);
             if (wd != null) {
                 final Display display = wd.get();
@@ -273,6 +278,32 @@
         }
     }
 
+    private void cleanupResourceImpl(ResourcesKey removedKey) {
+        // Remove any resource to display mapping based on this key.
+        final Iterator<Map.Entry<Pair<Integer, ResourcesKey>, WeakReference<Display>>> iter =
+                mResourceDisplays.entrySet().iterator();
+        while (iter.hasNext()) {
+            final Map.Entry<Pair<Integer, ResourcesKey>, WeakReference<Display>> entry =
+                    iter.next();
+            final ResourcesKey key = entry.getKey().second;
+
+            // Do not touch system resource displays (indicated by a {@code null} key) or
+            // non-matching keys.
+            if (key == null || !key.equals(removedKey)) {
+                continue;
+            }
+
+            iter.remove();
+        }
+
+        // Remove resource key to resource impl mapping and flush cache
+        final ResourcesImpl res = mResourceImpls.remove(removedKey).get();
+
+        if (res != null) {
+            res.flushLayoutCache();
+        }
+    }
+
     /**
      * Creates an AssetManager from the paths within the ResourcesKey.
      *
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 93204d1..23e54ba 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -284,8 +284,8 @@
 
     private int mVerifyState;
 
-    /** Whether or not the intent filter is visible to ephemeral apps. */
-    private boolean mVisibleToEphemeral;
+    /** Whether or not the intent filter is visible to instant apps. */
+    private boolean mVisibleToInstantApp;
     // These functions are the start of more optimized code for managing
     // the string sets...  not yet implemented.
 
@@ -452,6 +452,7 @@
         }
         mHasPartialTypes = o.mHasPartialTypes;
         mVerifyState = o.mVerifyState;
+        mVisibleToInstantApp = o.mVisibleToInstantApp;
     }
 
     /**
@@ -654,12 +655,12 @@
     }
 
     /** @hide */
-    public void setVisibleToEphemeral(boolean visibleToEmphemeral) {
-        mVisibleToEphemeral = visibleToEmphemeral;
+    public void setVisibleToInstantApp(boolean visibleToInstantApp) {
+        mVisibleToInstantApp = visibleToInstantApp;
     }
     /** @hide */
     public boolean isVisibleToInstantApp() {
-        return mVisibleToEphemeral;
+        return mVisibleToInstantApp;
     }
 
     /**
@@ -1858,6 +1859,7 @@
         dest.writeInt(mPriority);
         dest.writeInt(mHasPartialTypes ? 1 : 0);
         dest.writeInt(getAutoVerify() ? 1 : 0);
+        dest.writeInt(isVisibleToInstantApp() ? 1 : 0);
     }
 
     /**
@@ -1926,6 +1928,7 @@
         mPriority = source.readInt();
         mHasPartialTypes = source.readInt() > 0;
         setAutoVerify(source.readInt() > 0);
+        setVisibleToInstantApp(source.readInt() > 0);
     }
 
     private final boolean findMimeType(String type) {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 940447c..d44d0dc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4258,7 +4258,7 @@
                     a.intents.add(intent);
                 }
                 // adjust activity flags when we implicitly expose it via a browsable filter
-                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
+                intent.setVisibleToInstantApp(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.isVisibleToInstantApp()) {
                     a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
                 }
@@ -4291,7 +4291,7 @@
                     owner.preferredActivityFilters.add(intent);
                 }
                 // adjust activity flags when we implicitly expose it via a browsable filter
-                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
+                intent.setVisibleToInstantApp(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.isVisibleToInstantApp()) {
                     a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
                 }
@@ -4307,12 +4307,12 @@
                     owner.visibleToInstantApps = true;
                     // cycle through any filters already seen
                     for (int i = a.intents.size() - 1; i >= 0; --i) {
-                        a.intents.get(i).setVisibleToEphemeral(true /*visibleToEmphemeral*/);
+                        a.intents.get(i).setVisibleToInstantApp(true /*visibleToInstantApp*/);
                     }
                     if (owner.preferredActivityFilters != null) {
                         for (int i = owner.preferredActivityFilters.size() - 1; i >= 0; --i) {
                             owner.preferredActivityFilters.get(i)
-                                    .setVisibleToEphemeral(true /*visibleToEmphemeral*/);
+                                    .setVisibleToInstantApp(true /*visibleToInstantApp*/);
                         }
                     }
                 }
@@ -4618,7 +4618,7 @@
                             + mArchiveSourcePath + " "
                             + parser.getPositionDescription());
                 } else {
-                    intent.setVisibleToEphemeral(
+                    intent.setVisibleToInstantApp(
                             visibleToEphemeral || isWebBrowsableIntent(intent));
                     a.intents.add(intent);
                 }
@@ -4821,7 +4821,7 @@
                 }
                 outInfo.intents.add(intent);
                 // adjust provider flags when we implicitly expose it via a browsable filter
-                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
+                intent.setVisibleToInstantApp(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.isVisibleToInstantApp()) {
                     outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL;
                 }
@@ -4838,7 +4838,7 @@
                     owner.visibleToInstantApps = true;
                     // cycle through any filters already seen
                     for (int i = outInfo.intents.size() - 1; i >= 0; --i) {
-                        outInfo.intents.get(i).setVisibleToEphemeral(true /*visibleToEmphemeral*/);
+                        outInfo.intents.get(i).setVisibleToInstantApp(true /*visibleToInstantApp*/);
                     }
                 }
 
@@ -5126,7 +5126,7 @@
                     return null;
                 }
                 // adjust activity flags when we implicitly expose it via a browsable filter
-                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
+                intent.setVisibleToInstantApp(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.isVisibleToInstantApp()) {
                     s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL;
                 }
@@ -5143,7 +5143,7 @@
                     owner.visibleToInstantApps = true;
                     // cycle through any filters already seen
                     for (int i = s.intents.size() - 1; i >= 0; --i) {
-                        s.intents.get(i).setVisibleToEphemeral(true /*visibleToEmphemeral*/);
+                        s.intents.get(i).setVisibleToInstantApp(true /*visibleToInstantApp*/);
                     }
                 }
             } else {
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index f75b7af..4ad0f08 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.DebugUtils;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
@@ -111,52 +112,41 @@
      * Type used on when the service can save the contents of an activity, but cannot describe what
      * the content is for.
      */
-    public static final int SAVE_DATA_TYPE_GENERIC = 0;
+    public static final int SAVE_DATA_TYPE_GENERIC = 0x0;
 
     /**
      * Type used when the {@link FillResponse} represents user credentials that have a password.
      */
-    public static final int SAVE_DATA_TYPE_PASSWORD = 1;
+    public static final int SAVE_DATA_TYPE_PASSWORD = 0x01;
 
     /**
      * Type used on when the {@link FillResponse} represents a physical address (such as street,
      * city, state, etc).
      */
-    public static final int SAVE_DATA_TYPE_ADDRESS = 2;
+    public static final int SAVE_DATA_TYPE_ADDRESS = 0x02;
 
     /**
      * Type used when the {@link FillResponse} represents a credit card.
      */
-    public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3;
+    public static final int SAVE_DATA_TYPE_CREDIT_CARD = 0x04;
 
     /**
      * Type used when the {@link FillResponse} represents just an username, without a password.
      */
-    public static final int SAVE_DATA_TYPE_USERNAME = 4;
+    public static final int SAVE_DATA_TYPE_USERNAME = 0x08;
 
     /**
      * Type used when the {@link FillResponse} represents just an email address, without a password.
      */
-    public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 5;
+    public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 0x10;
 
-    private final @SaveDataType int mType;
+    private final int mType;
     private final CharSequence mNegativeActionTitle;
     private final IntentSender mNegativeActionListener;
     private final AutofillId[] mRequiredIds;
     private final AutofillId[] mOptionalIds;
     private final CharSequence mDescription;
 
-    /** @hide */
-    @IntDef({
-        SAVE_DATA_TYPE_GENERIC,
-        SAVE_DATA_TYPE_PASSWORD,
-        SAVE_DATA_TYPE_ADDRESS,
-        SAVE_DATA_TYPE_CREDIT_CARD
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface SaveDataType {
-    }
-
     private SaveInfo(Builder builder) {
         mType = builder.mType;
         mNegativeActionTitle = builder.mNegativeActionTitle;
@@ -201,7 +191,7 @@
      */
     public static final class Builder {
 
-        private final @SaveDataType int mType;
+        private final int mType;
         private CharSequence mNegativeActionTitle;
         private IntentSender mNegativeActionListener;
         // TODO(b/33197203): make mRequiredIds final once addSavableIds() is gone
@@ -213,32 +203,24 @@
         /**
          * Creates a new builder.
          *
-         * @param type the type of information the associated {@link FillResponse} represents. Must
-         * be {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}, {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD},
-         * {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, or {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD};
-         * otherwise it will assume {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}.
+         * @param type the type of information the associated {@link FillResponse} represents, can
+         * be any combination of {@link SaveInfo#SAVE_DATA_TYPE_GENERIC},
+         * {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD},
+         * {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD},
+         * {@link SaveInfo#SAVE_DATA_TYPE_USERNAME}, or
+         * {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}.
          * @param requiredIds ids of all required views that will trigger a save request.
          *
          * <p>See {@link SaveInfo} for more info.
          *
          * @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty.
          */
-        public Builder(@SaveDataType int type, @NonNull AutofillId[] requiredIds) {
+        public Builder(int type, @NonNull AutofillId[] requiredIds) {
             if (false) {// TODO(b/33197203): re-move when clients use it
             Preconditions.checkArgument(requiredIds != null && requiredIds.length > 0,
                     "must have at least one required id: " + Arrays.toString(requiredIds));
             }
-            switch (type) {
-                case SAVE_DATA_TYPE_PASSWORD:
-                case SAVE_DATA_TYPE_ADDRESS:
-                case SAVE_DATA_TYPE_CREDIT_CARD:
-                case SAVE_DATA_TYPE_USERNAME:
-                case SAVE_DATA_TYPE_EMAIL_ADDRESS:
-                    mType = type;
-                    break;
-                default:
-                    mType = SAVE_DATA_TYPE_GENERIC;
-            }
+            mType = type;
             mRequiredIds = requiredIds;
         }
 
@@ -248,7 +230,7 @@
          * // TODO(b/33197203): make sure is removed when clients migrated
          */
         @Deprecated
-        public Builder(@SaveDataType int type) {
+        public Builder(int type) {
             this(type, null);
         }
 
@@ -355,7 +337,8 @@
     public String toString() {
         if (!DEBUG) return super.toString();
 
-        return new StringBuilder("SaveInfo: [type=").append(mType)
+        return new StringBuilder("SaveInfo: [type=")
+                .append(DebugUtils.flagsToString(SaveInfo.class, "SAVE_DATA_TYPE_", mType))
                 .append(", requiredIds=").append(Arrays.toString(mRequiredIds))
                 .append(", optionalIds=").append(Arrays.toString(mOptionalIds))
                 .append(", description=").append(mDescription)
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 50f17e0..255574f 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3073,16 +3073,19 @@
 
         if (mActions != null && !mActions.isEmpty()) {
             final int actionCount = mActions.size();
-            parcel.writeInt(actionCount);
 
+            int nonLegacyActionCount = 0;
             int defaultLegacyStandardActions = 0;
             for (int i = 0; i < actionCount; i++) {
                 AccessibilityAction action = mActions.get(i);
                 if (isDefaultLegacyStandardAction(action)) {
                     defaultLegacyStandardActions |= action.getId();
+                } else {
+                    nonLegacyActionCount++;
                 }
             }
             parcel.writeInt(defaultLegacyStandardActions);
+            parcel.writeInt(nonLegacyActionCount);
 
             for (int i = 0; i < actionCount; i++) {
                 AccessibilityAction action = mActions.get(i);
@@ -3093,6 +3096,7 @@
             }
         } else {
             parcel.writeInt(0);
+            parcel.writeInt(0);
         }
 
         parcel.writeInt(mMaxTextLength);
@@ -3270,16 +3274,13 @@
         mBoundsInScreen.left = parcel.readInt();
         mBoundsInScreen.right = parcel.readInt();
 
-        final int actionCount = parcel.readInt();
-        if (actionCount > 0) {
-            final int legacyStandardActions = parcel.readInt();
-            addLegacyStandardActions(legacyStandardActions);
-            final int nonLegacyActionCount = actionCount - Integer.bitCount(legacyStandardActions);
-            for (int i = 0; i < nonLegacyActionCount; i++) {
-                final AccessibilityAction action = new AccessibilityAction(
-                        parcel.readInt(), parcel.readCharSequence());
-                addActionUnchecked(action);
-            }
+        final int legacyStandardActions = parcel.readInt();
+        addLegacyStandardActions(legacyStandardActions);
+        final int nonLegacyActionCount = parcel.readInt();
+        for (int i = 0; i < nonLegacyActionCount; i++) {
+            final AccessibilityAction action = new AccessibilityAction(
+                    parcel.readInt(), parcel.readCharSequence());
+            addActionUnchecked(action);
         }
 
         mMaxTextLength = parcel.readInt();
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 67bce8c..44fa99d 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -26,7 +26,9 @@
 import android.icu.util.ULocale;
 import android.net.LocalServerSocket;
 import android.opengl.EGL14;
+import android.os.Build;
 import android.os.IInstalld;
+import android.os.Environment;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.Seccomp;
@@ -60,6 +62,7 @@
 import libcore.io.IoUtils;
 
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -457,6 +460,23 @@
         final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
         if (systemServerClasspath != null) {
             performSystemServerDexOpt(systemServerClasspath);
+            // Capturing profiles is only supported for debug or eng builds since selinux normally
+            // prevents it.
+            boolean profileSystemServer = SystemProperties.getBoolean(
+                    "dalvik.vm.profilesystemserver", false);
+            if (profileSystemServer && (Build.IS_USERDEBUG || Build.IS_ENG)) {
+                try {
+                    File profileDir = Environment.getDataProfilesDePackageDirectory(
+                            Process.SYSTEM_UID, "system_server");
+                    File profile = new File(profileDir, "primary.prof");
+                    profile.getParentFile().mkdirs();
+                    profile.createNewFile();
+                    String[] codePaths = systemServerClasspath.split(":");
+                    VMRuntime.registerAppInfo(profile.getPath(), codePaths);
+                } catch (Exception e) {
+                    Log.wtf(TAG, "Failed to set up system server profile", e);
+                }
+            }
         }
 
         if (parsedArgs.invokeWith != null) {
diff --git a/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java b/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java
index e40090f..924b3f7 100644
--- a/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java
+++ b/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java
@@ -31,7 +31,7 @@
     private static final long ONE_S_IN_NS = ONE_MS_IN_NS * 1000;
 
     private final Handler mHandler;
-    private final Choreographer mChoreographer = Choreographer.getInstance();
+    private final Choreographer mChoreographer;
 
     /**
      * The offset between vsync-app and vsync-surfaceflinger. See
@@ -39,8 +39,10 @@
      */
     private long mSurfaceFlingerOffsetMs;
 
-    public SurfaceFlingerVsyncChoreographer(Handler handler, Display display) {
+    public SurfaceFlingerVsyncChoreographer(Handler handler, Display display,
+            Choreographer choreographer) {
         mHandler = handler;
+        mChoreographer = choreographer;
         mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs(display);
     }
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ae28797..75de4da 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4594,6 +4594,14 @@
     <!-- Title for the autofill save dialog shown when the the contents of the activity can be saved
          by an autofill service, and the service does knows what the activity represents (for example, credit card info) [CHAR LIMIT=NONE] -->
     <string name="autofill_save_title_with_type">Save <xliff:g id="type" example="Credit Card">%1$s</xliff:g> to <xliff:g id="label" example="MyPass">%2$s</xliff:g>?</string>
+    <!-- Title for the autofill save dialog shown when the the contents of the activity can be saved
+         by an autofill service, and the service does knows what the activity represents, and it represents 2 types of
+         data (for example, password and credit card info) [CHAR LIMIT=NONE] -->
+    <string name="autofill_save_title_with_2types">Save <xliff:g id="type" example="Password">%1$s</xliff:g>, <xliff:g id="type" example="Credit Card">%2$s</xliff:g> to <xliff:g id="label" example="MyPass">%3$s</xliff:g>?</string>
+    <!-- Title for the autofill save dialog shown when the the contents of the activity can be saved
+         by an autofill service, and the service does knows what the activity represents, and it represents 3 types of
+         data (for example, username, password and credit card info) [CHAR LIMIT=NONE] -->
+    <string name="autofill_save_title_with_3types">Save <xliff:g id="type" example="Username">%1$s</xliff:g>, <xliff:g id="type" example="Password">%2$s</xliff:g>, <xliff:g id="type" example="Credit Card">%3$s</xliff:g> to <xliff:g id="label" example="MyPass">%4$s</xliff:g>?</string>
     <!-- Label for the autofill save button [CHAR LIMIT=NONE] -->
     <string name="autofill_save_yes">Save</string>
     <!-- Label for the autofill cancel button [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 89c3943..7cb9ba8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2881,6 +2881,8 @@
   <java-symbol type="string" name="autofill_picker_accessibility_title " />
   <java-symbol type="string" name="autofill_save_title" />
   <java-symbol type="string" name="autofill_save_title_with_type" />
+  <java-symbol type="string" name="autofill_save_title_with_2types" />
+  <java-symbol type="string" name="autofill_save_title_with_3types" />
   <java-symbol type="string" name="autofill_save_yes" />
   <java-symbol type="string" name="autofill_save_no" />
   <java-symbol type="string" name="autofill_save_type_password" />
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e6f3cfb..71a968b 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -3822,6 +3822,18 @@
      */
     public static final int MEDIA_INFO_EXTERNAL_METADATA_UPDATE = 803;
 
+    /** Informs that audio is not playing. Note that playback of the video
+     * is not interrupted.
+     * @see android.media.MediaPlayer.OnInfoListener
+     */
+    public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804;
+
+    /** Informs that video is not playing. Note that playback of the audio
+     * is not interrupted.
+     * @see android.media.MediaPlayer.OnInfoListener
+     */
+    public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805;
+
     /** Failed to handle timed text track properly.
      * @see android.media.MediaPlayer.OnInfoListener
      *
diff --git a/packages/PrintRecommendationService/res/values/strings.xml b/packages/PrintRecommendationService/res/values/strings.xml
index b6c45b7..2bab1b6 100644
--- a/packages/PrintRecommendationService/res/values/strings.xml
+++ b/packages/PrintRecommendationService/res/values/strings.xml
@@ -18,6 +18,7 @@
 -->
 
 <resources>
+    <string name="plugin_vendor_google_cloud_print">Cloud Print</string>
     <string name="plugin_vendor_hp">HP</string>
     <string name="plugin_vendor_lexmark">Lexmark</string>
     <string name="plugin_vendor_brother">Brother</string>
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
index e18ee90..128ed50 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
@@ -22,6 +22,7 @@
 import android.printservice.recommendation.RecommendationService;
 import android.util.Log;
 
+import com.android.printservice.recommendation.plugin.google.CloudPrintPlugin;
 import com.android.printservice.recommendation.plugin.hp.HPRecommendationPlugin;
 import com.android.printservice.recommendation.plugin.mdnsFilter.MDNSFilterPlugin;
 import com.android.printservice.recommendation.plugin.mdnsFilter.VendorConfig;
@@ -65,6 +66,14 @@
         }
 
         try {
+            mPlugins.add(new RemotePrintServicePlugin(new CloudPrintPlugin(this), this,
+                    true));
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Could not initiate "
+                            + getString(R.string.plugin_vendor_google_cloud_print) + " plugin", e);
+        }
+
+        try {
             mPlugins.add(new RemotePrintServicePlugin(new HPRecommendationPlugin(this), this,
                     false));
         } catch (Exception e) {
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java
new file mode 100644
index 0000000..05b0c86
--- /dev/null
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java
@@ -0,0 +1,168 @@
+/*
+ * 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.printservice.recommendation.plugin.google;
+
+import static com.android.printservice.recommendation.util.MDNSUtils.ATTRIBUTE_TY;
+
+import android.annotation.NonNull;
+import android.annotation.StringRes;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.printservice.recommendation.PrintServicePlugin;
+import com.android.printservice.recommendation.R;
+import com.android.printservice.recommendation.util.MDNSFilteredDiscovery;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Plugin detecting <a href="https://developers.google.com/cloud-print/docs/privet">Google Cloud
+ * Print</a> printers.
+ */
+public class CloudPrintPlugin implements PrintServicePlugin {
+    private static final String LOG_TAG = CloudPrintPlugin.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    private static final String ATTRIBUTE_TXTVERS = "txtvers";
+    private static final String ATTRIBUTE_URL = "url";
+    private static final String ATTRIBUTE_TYPE = "type";
+    private static final String ATTRIBUTE_ID = "id";
+    private static final String ATTRIBUTE_CS = "cs";
+
+    private static final String TYPE = "printer";
+
+    private static final String PRIVET_SERVICE = "_privet._tcp";
+
+    /** The required mDNS service types */
+    private static final Set<String> PRINTER_SERVICE_TYPE = new HashSet<String>() {{
+        // Not checking _printer_._sub
+        add(PRIVET_SERVICE);
+    }};
+
+    /** All possible connection states */
+    private static final Set<String> POSSIBLE_CONNECTION_STATES = new HashSet<String>() {{
+        add("online");
+        add("offline");
+        add("connecting");
+        add("not-configured");
+    }};
+
+    private static final byte SUPPORTED_TXTVERS = '1';
+
+    /** The mDNS filtered discovery */
+    private final MDNSFilteredDiscovery mMDNSFilteredDiscovery;
+
+    /**
+     * Create a plugin detecting Google Cloud Print printers.
+     *
+     * @param context The context the plugin runs in
+     */
+    public CloudPrintPlugin(@NonNull Context context) {
+        mMDNSFilteredDiscovery = new MDNSFilteredDiscovery(context, PRINTER_SERVICE_TYPE,
+                nsdServiceInfo -> {
+                    // The attributes are case insensitive. For faster searching create a clone of
+                    // the map with the attribute-keys all in lower case.
+                    ArrayMap<String, byte[]> caseInsensitiveAttributes =
+                            new ArrayMap<>(nsdServiceInfo.getAttributes().size());
+                    for (Map.Entry<String, byte[]> entry : nsdServiceInfo.getAttributes()
+                            .entrySet()) {
+                        caseInsensitiveAttributes.put(entry.getKey().toLowerCase(),
+                                entry.getValue());
+                    }
+
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, nsdServiceInfo.getServiceName() + ":");
+                        Log.i(LOG_TAG, "type:  " + nsdServiceInfo.getServiceType());
+                        Log.i(LOG_TAG, "host:  " + nsdServiceInfo.getHost());
+                        for (Map.Entry<String, byte[]> entry : caseInsensitiveAttributes.entrySet()) {
+                            if (entry.getValue() == null) {
+                                Log.i(LOG_TAG, entry.getKey() + "= null");
+                            } else {
+                                Log.i(LOG_TAG, entry.getKey() + "=" + new String(entry.getValue(),
+                                        StandardCharsets.UTF_8));
+                            }
+                        }
+                    }
+
+                    byte[] txtvers = caseInsensitiveAttributes.get(ATTRIBUTE_TXTVERS);
+                    if (txtvers == null || txtvers.length != 1 || txtvers[0] != SUPPORTED_TXTVERS) {
+                        // The spec requires this to be the first attribute, but at this time we
+                        // lost the order of the attributes
+                        return false;
+                    }
+
+                    if (caseInsensitiveAttributes.get(ATTRIBUTE_TY) == null) {
+                        return false;
+                    }
+
+                    byte[] url = caseInsensitiveAttributes.get(ATTRIBUTE_URL);
+                    if (url == null || url.length == 0) {
+                        return false;
+                    }
+
+                    byte[] type = caseInsensitiveAttributes.get(ATTRIBUTE_TYPE);
+                    if (type == null || !TYPE.equals(
+                            new String(type, StandardCharsets.UTF_8).toLowerCase())) {
+                        return false;
+                    }
+
+                    if (caseInsensitiveAttributes.get(ATTRIBUTE_ID) == null) {
+                        return false;
+                    }
+
+                    byte[] cs = caseInsensitiveAttributes.get(ATTRIBUTE_CS);
+                    if (cs == null || !POSSIBLE_CONNECTION_STATES.contains(
+                            new String(cs, StandardCharsets.UTF_8).toLowerCase())) {
+                        return false;
+                    }
+
+                    InetAddress address = nsdServiceInfo.getHost();
+                    if (!(address instanceof Inet4Address)) {
+                        // Not checking for link local address
+                        return false;
+                    }
+
+                    return true;
+                });
+    }
+
+    @Override
+    @NonNull public CharSequence getPackageName() {
+        return "com.google.android.apps.cloudprint";
+    }
+
+    @Override
+    public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
+        mMDNSFilteredDiscovery.start(callback);
+    }
+
+    @Override
+    @StringRes public int getName() {
+        return R.string.plugin_vendor_google_cloud_print;
+    }
+
+    @Override
+    public void stop() throws Exception {
+        mMDNSFilteredDiscovery.stop();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 0ee3e19..c48ecdb 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -33,6 +33,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
+import android.view.Choreographer;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.GestureDetector;
@@ -312,7 +313,8 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         EventBus.getDefault().register(this);
-        mSfChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, getDisplay());
+        mSfChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, getDisplay(),
+                Choreographer.getInstance());
     }
 
     @Override
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 9092bdb..c3cbf18 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -398,8 +398,7 @@
             return true;
         }
 
-        // TODO(b/33197203 , b/35707731): must iterate over all responses
-        final FillResponse response = mResponses.get(0);
+        final FillResponse response = mResponses.get(mResponses.size() - 1);
 
         final SaveInfo saveInfo = response.getSaveInfo();
         if (DEBUG) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index d35cc95..f94d456 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -24,7 +24,7 @@
 import android.content.IntentSender;
 import android.os.Handler;
 import android.service.autofill.SaveInfo;
-import android.text.format.DateUtils;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.view.Gravity;
 import android.view.Window;
@@ -105,31 +105,44 @@
         final View view = inflater.inflate(R.layout.autofill_save, null);
 
         final TextView titleView = (TextView) view.findViewById(R.id.autofill_save_title);
-        final String type;
 
-        switch(info.getType()) {
-            case SaveInfo.SAVE_DATA_TYPE_PASSWORD:
-                type = context.getString(R.string.autofill_save_type_password);
-                break;
-            case SaveInfo.SAVE_DATA_TYPE_ADDRESS:
-                type = context.getString(R.string.autofill_save_type_address);
-                break;
-            case SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD:
-                type = context.getString(R.string.autofill_save_type_credit_card);
-                break;
-            case SaveInfo.SAVE_DATA_TYPE_USERNAME:
-                type = context.getString(R.string.autofill_save_type_username);
-                break;
-            case SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS:
-                type = context.getString(R.string.autofill_save_type_email_address);
-                break;
-            default:
-                type = null;
+        final ArraySet<String> types = new ArraySet<>(3);
+        final int type = info.getType();
+
+        if ((type & SaveInfo.SAVE_DATA_TYPE_PASSWORD) != 0) {
+            types.add(context.getString(R.string.autofill_save_type_password));
+        }
+        if ((type & SaveInfo.SAVE_DATA_TYPE_ADDRESS) != 0) {
+            types.add(context.getString(R.string.autofill_save_type_address));
+        }
+        if ((type & SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD) != 0) {
+            types.add(context.getString(R.string.autofill_save_type_credit_card));
+        }
+        if ((type & SaveInfo.SAVE_DATA_TYPE_USERNAME) != 0) {
+            types.add(context.getString(R.string.autofill_save_type_username));
+        }
+        if ((type & SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS) != 0) {
+            types.add(context.getString(R.string.autofill_save_type_email_address));
         }
 
-        final String title = (type == null)
-                ? context.getString(R.string.autofill_save_title, providerLabel)
-                : context.getString(R.string.autofill_save_title_with_type, type, providerLabel);
+        final String title;
+        switch (types.size()) {
+            case 1:
+                title = context.getString(R.string.autofill_save_title_with_type,
+                        types.valueAt(0), providerLabel);
+                break;
+            case 2:
+                title = context.getString(R.string.autofill_save_title_with_2types,
+                        types.valueAt(0), types.valueAt(1), providerLabel);
+                break;
+            case 3:
+                title = context.getString(R.string.autofill_save_title_with_3types,
+                        types.valueAt(0), types.valueAt(1), types.valueAt(2), providerLabel);
+                break;
+            default:
+                // Use generic if more than 3 or invalid type (size 0).
+                title = context.getString(R.string.autofill_save_title, providerLabel);
+        }
 
         titleView.setText(title);
         final CharSequence subTitle = info.getDescription();
@@ -139,6 +152,10 @@
             subTitleView.setVisibility(View.VISIBLE);
         }
 
+        if (DEBUG) {
+            Slog.d(TAG, "Title: " + title + " SubTitle: " + subTitle);
+        }
+
         final TextView noButton = view.findViewById(R.id.autofill_save_no);
         if (info.getNegativeActionTitle() != null) {
             noButton.setText(info.getNegativeActionTitle());
diff --git a/services/core/java/com/android/server/AnimationThread.java b/services/core/java/com/android/server/AnimationThread.java
new file mode 100644
index 0000000..08392b0
--- /dev/null
+++ b/services/core/java/com/android/server/AnimationThread.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
+
+import android.os.Handler;
+import android.os.Trace;
+
+/**
+ * Thread for handling all window animations, or anything that's directly impacting animations like
+ * starting windows or traversals.
+ */
+public final class AnimationThread extends ServiceThread {
+    private static AnimationThread sInstance;
+    private static Handler sHandler;
+
+    private AnimationThread() {
+        super("android.anim", THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
+    }
+
+    private static void ensureThreadLocked() {
+        if (sInstance == null) {
+            sInstance = new AnimationThread();
+            sInstance.start();
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
+            sHandler = new Handler(sInstance.getLooper());
+        }
+    }
+
+    public static AnimationThread get() {
+        synchronized (AnimationThread.class) {
+            ensureThreadLocked();
+            return sInstance;
+        }
+    }
+
+    public static Handler getHandler() {
+        synchronized (AnimationThread.class) {
+            ensureThreadLocked();
+            return sHandler;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/DisplayThread.java b/services/core/java/com/android/server/DisplayThread.java
index 9ef0259..cad2a61 100644
--- a/services/core/java/com/android/server/DisplayThread.java
+++ b/services/core/java/com/android/server/DisplayThread.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.Process;
 import android.os.Trace;
 
 /**
@@ -30,7 +31,9 @@
     private static Handler sHandler;
 
     private DisplayThread() {
-        super("android.display", android.os.Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
+        // DisplayThread runs important stuff, but these are not as important as things running in
+        // AnimationThread. Thus, set the priority to one lower.
+        super("android.display", Process.THREAD_PRIORITY_DISPLAY + 1, false /*allowIo*/);
     }
 
     private static void ensureThreadLocked() {
diff --git a/services/core/java/com/android/server/ThreadPriorityBooster.java b/services/core/java/com/android/server/ThreadPriorityBooster.java
index 17965d0..31726ad 100644
--- a/services/core/java/com/android/server/ThreadPriorityBooster.java
+++ b/services/core/java/com/android/server/ThreadPriorityBooster.java
@@ -41,8 +41,8 @@
         final int tid = Process.myTid();
         final int prevPriority = Process.getThreadPriority(tid);
         PriorityState state = mThreadState.get();
+        state.prevPriority = prevPriority;
         if (state.regionCounter == 0 && prevPriority > mBoostToPriority) {
-            state.prevPriority = prevPriority;
             Process.setThreadPriority(tid, mBoostToPriority);
         }
         state.regionCounter++;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cc6ca2d..35654d7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2386,16 +2386,19 @@
                 idleUids();
             } break;
             case VR_MODE_CHANGE_MSG: {
-                if (mVrController.onVrModeChanged((ActivityRecord) msg.obj)) {
-                    synchronized (ActivityManagerService.this) {
-                        if (mVrController.shouldDisableNonVrUiLocked()) {
-                            // If we are in a VR mode where Picture-in-Picture mode is unsupported,
-                            // then remove the pinned stack.
-                            final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
-                                    PINNED_STACK_ID);
-                            if (pinnedStack != null) {
-                                mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
-                            }
+                if (!mVrController.onVrModeChanged((ActivityRecord) msg.obj)) {
+                    return;
+                }
+                synchronized (ActivityManagerService.this) {
+                    final boolean disableNonVrUi = mVrController.shouldDisableNonVrUiLocked();
+                    mWindowManager.disableNonVrUi(disableNonVrUi);
+                    if (disableNonVrUi) {
+                        // If we are in a VR mode where Picture-in-Picture mode is unsupported,
+                        // then remove the pinned stack.
+                        final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
+                                PINNED_STACK_ID);
+                        if (pinnedStack != null) {
+                            mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4ee1a207..51011b5 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -312,6 +312,11 @@
      */
     boolean mConfigWillChange;
 
+    /**
+     * When set, will force the stack to report as invisible.
+     */
+    boolean mForceHidden = false;
+
     // Whether or not this stack covers the entire screen; by default stacks are fullscreen
     boolean mFullscreen = true;
     // Current bounds of the stack or null if fullscreen.
@@ -1561,7 +1566,8 @@
 
     /** Returns true if the stack is currently considered visible. */
     boolean isVisible() {
-        return mWindowContainerController != null && mWindowContainerController.isVisible();
+        return mWindowContainerController != null && mWindowContainerController.isVisible()
+                && !mForceHidden;
     }
 
     /**
@@ -1571,7 +1577,7 @@
      * @param starting The currently starting activity or null if there is none.
      */
     int shouldBeVisible(ActivityRecord starting) {
-        if (!isAttached()) {
+        if (!isAttached() || mForceHidden) {
             return STACK_INVISIBLE;
         }
 
@@ -3220,8 +3226,25 @@
         r.addResultLocked(null, resultWho, requestCode, resultCode, data);
     }
 
+    /** Returns true if the task is one of the task finishing on-top of the top running task. */
+    boolean isATopFinishingTask(TaskRecord task) {
+        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
+            final TaskRecord current = mTaskHistory.get(i);
+            final ActivityRecord r = current.topRunningActivityLocked();
+            if (r != null) {
+                // We got a top running activity, so there isn't a top finishing task...
+                return false;
+            }
+            if (current == task) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void adjustFocusedActivityStackLocked(ActivityRecord r, String reason) {
-        if (!mStackSupervisor.isFocusedStack(this) || mResumedActivity != r) {
+        if (!mStackSupervisor.isFocusedStack(this) ||
+                ((mResumedActivity != r) && (mResumedActivity != null))) {
             return;
         }
 
@@ -3236,8 +3259,8 @@
                 final TaskRecord task = r.getTask();
                 final boolean isAssistantOrOverAssistant = task.getStack().isAssistantStack() ||
                         task.isOverAssistantStack();
-                if (r.frontOfTask && task == topTask() &&
-                        (task.isOverHomeStack() || isAssistantOrOverAssistant)) {
+                if (r.frontOfTask && isATopFinishingTask(task)
+                        && (task.isOverHomeStack() || isAssistantOrOverAssistant)) {
                     // For non-fullscreen or assistant stack, we want to move the focus to the next
                     // visible stack to prevent the home screen from moving to the top and obscuring
                     // other visible stacks.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0ea834a..c9bb9e5 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -42,6 +42,7 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
 
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
@@ -167,7 +168,6 @@
 
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.ArrayUtils;
@@ -2343,7 +2343,6 @@
         mWindowManager.deferSurfaceLayout();
         try {
             if (fromStackId == DOCKED_STACK_ID) {
-
                 // We are moving all tasks from the docked stack to the fullscreen stack,
                 // which is dismissing the docked stack, so resize all other stacks to
                 // fullscreen here already so we don't end up with resize trashing.
@@ -2362,13 +2361,19 @@
                 // resize when we remove task from it below and it is detached from the
                 // display because it no longer contains any tasks.
                 mAllowDockedStackResize = false;
+            } else if (fromStackId == PINNED_STACK_ID) {
+                if (onTop) {
+                    // Log if we are expanding the PiP to fullscreen
+                    MetricsLogger.action(mService.mContext,
+                            ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
+                }
             }
             ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
             final boolean isFullscreenStackVisible = fullscreenStack != null &&
                     fullscreenStack.shouldBeVisible(null) == STACK_VISIBLE;
             // If we are moving from the pinned stack, then the animation takes care of updating
             // the picture-in-picture mode.
-            final boolean schedulePictureInPictureModeChange = (fromStackId != PINNED_STACK_ID);
+            final boolean schedulePictureInPictureModeChange = (fromStackId == PINNED_STACK_ID);
             final ArrayList<TaskRecord> tasks = stack.getAllTasks();
             final int size = tasks.size();
             if (onTop) {
@@ -2381,8 +2386,6 @@
                         // pinned stack is recreated. See moveActivityToPinnedStackLocked().
                         task.setTaskToReturnTo(isFullscreenStackVisible && onTop ?
                                 APPLICATION_ACTIVITY_TYPE : HOME_ACTIVITY_TYPE);
-                        MetricsLogger.action(mService.mContext,
-                                MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
                     }
                     // Defer resume until all the tasks have been moved to the fullscreen stack
                     task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP,
@@ -2537,27 +2540,24 @@
 
         final ArrayList<TaskRecord> tasks = stack.getAllTasks();
         if (stack.getStackId() == PINNED_STACK_ID) {
-            final ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
-            if (fullscreenStack != null) {
-                final boolean isFullscreenStackVisible =
-                        fullscreenStack.shouldBeVisible(null) == STACK_VISIBLE;
-                for (int i = 0; i < tasks.size(); i++) {
-                    // Insert the task either at the top of the fullscreen stack if it is hidden,
-                    // or to the bottom if it is currently visible
-                    final int insertPosition = isFullscreenStackVisible ? 0
-                            : fullscreenStack.getChildCount();
-                    final TaskRecord task = tasks.get(i);
-                    // Defer resume until we remove all the tasks
-                    task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, insertPosition,
-                            REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME, "removeStack");
-                }
-                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                resumeFocusedStackTopActivityLocked();
-            } else {
-                // If there is no fullscreen stack, then create the stack and move all the tasks
-                // onto the stack
-                moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
-            }
+            /**
+             * Workaround: Force-stop all the activities in the pinned stack before we reparent them
+             * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
+             * that the client receives onStop() before it is reparented.  We do this by detaching
+             * the stack from the display so that it will be considered invisible when
+             * ensureActivitiesVisibleLocked() is called, and all of its activitys will be marked
+             * invisible as well and added to the stopping list.  After which we process the
+             * stopping list by handling the idle.
+             */
+            final PinnedActivityStack pinnedStack = (PinnedActivityStack) stack;
+            pinnedStack.mForceHidden = true;
+            pinnedStack.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            pinnedStack.mForceHidden = false;
+            activityIdleInternalLocked(null, false /* fromTimeout */,
+                    true /* processPausingActivites */, null /* configuration */);
+
+            // Move all the tasks to the bottom of the fullscreen stack
+            moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
         } else {
             for (int i = tasks.size() - 1; i >= 0; i--) {
                 removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
@@ -2935,6 +2935,10 @@
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeFocusedStackTopActivityLocked();
 
+        // TODO(b/36099777): Schedule the PiP mode change here immediately until we can defer all
+        // callbacks until after the bounds animation
+        scheduleUpdatePictureInPictureModeIfNeeded(r.getTask(), destBounds, true /* immediate */);
+
         stack.animateResizePinnedStack(sourceBounds, destBounds, -1 /* animationDuration */);
         mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName);
     }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index a30591b..59f6098 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1457,17 +1457,11 @@
     }
 
     /**
-     * Returns the ID of the display to use for a new activity. If the source activity has
-     * a explicit display ID set, use that to launch the activity. If not and the device is in VR
-     * mode, then return the Vr mode's virtual display ID.
+     * Returns the ID of the display to use for a new activity. If the device is in VR mode,
+     * then return the Vr mode's virtual display ID. If not, if the source activity has
+     * a explicit display ID set, use that to launch the activity.
      */
     private int getSourceDisplayId(ActivityRecord sourceRecord, ActivityRecord startingActivity) {
-        int displayId = sourceRecord != null ? sourceRecord.getDisplayId() : INVALID_DISPLAY;
-        // If the activity has a displayId set explicitly, launch it on the same displayId.
-        if (displayId != INVALID_DISPLAY) {
-            return displayId;
-        }
-
         // Check if the Activity is a VR activity. If so, the activity should be launched in
         // main display.
         if (startingActivity != null && startingActivity.requestedVrComponent != null) {
@@ -1475,7 +1469,7 @@
         }
 
         // Get the virtual display id from ActivityManagerService.
-        displayId = mService.mVrCompatibilityDisplayId;
+        int displayId = mService.mVrCompatibilityDisplayId;
         if (displayId != INVALID_DISPLAY) {
             if (DEBUG_STACK) {
                 Slog.d(TAG, "getSourceDisplayId :" + displayId);
@@ -1483,6 +1477,12 @@
             mUsingVrCompatibilityDisplay = true;
             return displayId;
         }
+
+        displayId = sourceRecord != null ? sourceRecord.getDisplayId() : INVALID_DISPLAY;
+        // If the activity has a displayId set explicitly, launch it on the same displayId.
+        if (displayId != INVALID_DISPLAY) {
+            return displayId;
+        }
         return DEFAULT_DISPLAY;
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d83676b..2082958 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -63,6 +63,7 @@
 import android.view.Surface;
 import android.view.WindowManagerInternal;
 
+import com.android.server.AnimationThread;
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -257,13 +258,13 @@
     }
 
     public void setupSchedulerPolicies() {
-	/*
-	 * android.display is critical to user experience and we should
-	 * make sure it is not in the default foregroup groups, add it to
-	 * top-app to make sure it uses all the cores and scheduling
-	 * settings for top-app when it runs.
-	 */
-	Process.setThreadGroupAndCpuset(DisplayThread.get().getThreadId(), Process.THREAD_GROUP_TOP_APP);
+        // android.display and android.anim is critical to user experience and we should make sure
+        // it is not in the default foregroup groups, add it to top-app to make sure it uses all the
+        // cores and scheduling settings for top-app when it runs.
+        Process.setThreadGroupAndCpuset(DisplayThread.get().getThreadId(),
+                Process.THREAD_GROUP_TOP_APP);
+        Process.setThreadGroupAndCpuset(AnimationThread.get().getThreadId(),
+                Process.THREAD_GROUP_TOP_APP);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 656e01a..85f7056 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -570,12 +570,12 @@
      * @return true if caller can use fingerprint API
      */
     private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
-            int pid) {
+            int pid, int userId) {
         checkPermission(USE_FINGERPRINT);
         if (isKeyguard(opPackageName)) {
             return true; // Keyguard is always allowed
         }
-        if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
+        if (!isCurrentUserOrProfile(userId)) {
             Slog.w(TAG,"Rejecting " + opPackageName + " ; not a current user or profile");
             return false;
         }
@@ -906,7 +906,7 @@
                 @Override
                 public void run() {
                     if (!canUseFingerprint(opPackageName, true /* foregroundOnly */,
-                            callingUid, pid)) {
+                            callingUid, pid, callingUserId)) {
                         if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
                         return;
                     }
@@ -933,10 +933,12 @@
         public void cancelAuthentication(final IBinder token, final String opPackageName) {
             final int uid = Binder.getCallingUid();
             final int pid = Binder.getCallingPid();
+            final int callingUserId = UserHandle.getCallingUserId();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, uid, pid)) {
+                    if (!canUseFingerprint(opPackageName, true /* foregroundOnly */, uid, pid,
+                            callingUserId)) {
                         if (DEBUG) Slog.v(TAG, "cancelAuthentication(): reject " + opPackageName);
                     } else {
                         ClientMonitor client = mCurrentClient;
@@ -996,7 +998,8 @@
         @Override // Binder call
         public boolean isHardwareDetected(long deviceId, String opPackageName) {
             if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
-                    Binder.getCallingUid(), Binder.getCallingPid())) {
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
                 return false;
             }
 
@@ -1027,7 +1030,8 @@
         @Override // Binder call
         public List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
             if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
-                    Binder.getCallingUid(), Binder.getCallingPid())) {
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
                 return Collections.emptyList();
             }
 
@@ -1037,7 +1041,8 @@
         @Override // Binder call
         public boolean hasEnrolledFingerprints(int userId, String opPackageName) {
             if (!canUseFingerprint(opPackageName, false /* foregroundOnly */,
-                    Binder.getCallingUid(), Binder.getCallingPid())) {
+                    Binder.getCallingUid(), Binder.getCallingPid(),
+                    UserHandle.getCallingUserId())) {
                 return false;
             }
 
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index efc92cf..7ed3eac 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -50,7 +50,7 @@
     private String mNotificationTag;
     private final NotificationManager mNotificationManager;
     private final String mPackageName;
-    private boolean mCancelled;
+    private boolean mPosted;
     private IconUtilities mIconUtilities;
 
     AlertWindowNotification(WindowManagerService service, String packageName) {
@@ -61,7 +61,9 @@
         mNotificationTag = CHANNEL_PREFIX + mPackageName;
         mRequestCode = sNextRequestCode++;
         mIconUtilities = new IconUtilities(mService.mContext);
+    }
 
+    void post() {
         // We can't create/post the notification while the window manager lock is held since it will
         // end up calling into activity manager. So, we post a message to do it later.
         mService.mH.post(this::onPostNotification);
@@ -76,16 +78,21 @@
 
     /** Don't call with the window manager lock held! */
     private void onCancelNotification() {
+        if (!mPosted) {
+            // Notification isn't currently posted...
+            return;
+        }
+        mPosted = false;
         mNotificationManager.cancel(mNotificationTag, NOTIFICATION_ID);
-        mCancelled = true;
     }
 
     /** Don't call with the window manager lock held! */
     private void onPostNotification() {
-        if (mCancelled) {
-            // Notification was cancelled, so nothing more to do...
+        if (mPosted) {
+            // Notification already posted...
             return;
         }
+        mPosted = true;
 
         final Context context = mService.mContext;
         final PackageManager pm = context.getPackageManager();
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 4b4be40..a39131c 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -554,7 +554,7 @@
         // want to process the message ASAP, before any other queued
         // messages.
         if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
-        mHandler.postAtFrontOfQueue(mAddStartingWindow);
+        mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
     }
 
     private boolean createSnapshot() {
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 7b8057ca..2811145 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -170,8 +170,8 @@
 
             // If we are animating to a new fullscreen state (either to/from fullscreen), then
             // notify the target of the change with the new frozen task bounds
-            if (mAnimatingToNewFullscreenState) {
-                mTarget.updatePictureInPictureMode(mMoveToFullScreen ? null : mTo);
+            if (mAnimatingToNewFullscreenState && mMoveToFullScreen) {
+                mTarget.updatePictureInPictureMode(null);
             }
 
             // Immediately update the task bounds if they have to become larger, but preserve
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 30e0ded..6a7123c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -81,6 +81,7 @@
     private final Set<WindowSurfaceController> mAlertWindowSurfaces = new HashSet<>();
     final boolean mCanAddInternalSystemWindow;
     private AlertWindowNotification mAlertWindowNotification;
+    private boolean mShowingAlertWindowNotificationAllowed;
     private boolean mClientDead = false;
     private float mLastReportedAnimatorScale;
     private String mPackageName;
@@ -95,6 +96,7 @@
         mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
         mCanAddInternalSystemWindow = service.mContext.checkCallingOrSelfPermission(
                 INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED;
+        mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
         StringBuilder sb = new StringBuilder();
         sb.append("Session{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
@@ -591,6 +593,9 @@
                     cancelAlertWindowNotification();
                 } else if (mAlertWindowNotification == null){
                     mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
+                    if (mShowingAlertWindowNotificationAllowed) {
+                        mAlertWindowNotification.post();
+                    }
                 }
             }
         }
@@ -612,6 +617,17 @@
         }
     }
 
+    void setShowingAlertWindowNotificationAllowed(boolean allowed) {
+        mShowingAlertWindowNotificationAllowed = allowed;
+        if (mAlertWindowNotification != null) {
+            if (allowed) {
+                mAlertWindowNotification.post();
+            } else {
+                mAlertWindowNotification.cancel();
+            }
+        }
+    }
+
     private void killSessionLocked() {
         if (mNumWindow > 0 || !mClientDead) {
             return;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 53c24e1..0c68e2c 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -262,7 +262,8 @@
         mService.mInputManager.registerInputChannel(mServerChannel, null);
 
         mInputEventReceiver = new WindowPositionerEventReceiver(
-                mClientChannel, mService.mH.getLooper(), mService.mChoreographer);
+                mClientChannel, mService.mAnimationHandler.getLooper(),
+                mService.mAnimator.getChoreographer());
 
         mDragApplicationHandle = new InputApplicationHandle(null);
         mDragApplicationHandle.name = TAG;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 57eaa2b..1367a06 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -25,7 +25,6 @@
 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
 
 import android.content.Context;
-import android.os.Handler;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -35,7 +34,7 @@
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
-import com.android.server.DisplayThread;
+import com.android.server.AnimationThread;
 
 import java.io.PrintWriter;
 
@@ -87,20 +86,25 @@
     private final Runnable mAnimationTick;
     private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
 
+    private Choreographer mChoreographer;
+    private boolean mAnimationScheduled;
+
+
     WindowAnimator(final WindowManagerService service) {
         mService = service;
         mContext = service.mContext;
         mPolicy = service.mPolicy;
         mWindowPlacerLocked = service.mWindowPlacerLocked;
-        final Handler handler = DisplayThread.getHandler();
+        AnimationThread.getHandler().runWithScissors(
+                () -> mChoreographer = Choreographer.getInstance(), 0 /* timeout */);
 
         // TODO: Multi-display: If displays have different vsync tick, have a separate tick per
         // display.
-        mSfChoreographer = new SurfaceFlingerVsyncChoreographer(handler,
-                mService.getDefaultDisplayContentLocked().getDisplay());
+        mSfChoreographer = new SurfaceFlingerVsyncChoreographer(AnimationThread.getHandler(),
+                mService.getDefaultDisplayContentLocked().getDisplay(), mChoreographer);
         mAnimationTick = () -> {
             synchronized (mService.mWindowMap) {
-                mService.mAnimationScheduled = false;
+                mAnimationScheduled = false;
                 animateLocked(mCurrentFrameTime);
             }
         };
@@ -366,6 +370,13 @@
         mRemoveReplacedWindows = true;
     }
 
+    void scheduleAnimation() {
+        if (!mAnimationScheduled) {
+            mAnimationScheduled = true;
+            mChoreographer.postFrameCallback(mAnimationFrameCallback);
+        }
+    }
+
     private class DisplayContentsAnimator {
         ScreenRotationAnimation mScreenRotationAnimation = null;
     }
@@ -374,6 +385,14 @@
         return mAnimating;
     }
 
+    boolean isAnimationScheduled() {
+        return mAnimationScheduled;
+    }
+
+    Choreographer getChoreographer() {
+        return mChoreographer;
+    }
+
     void setAnimating(boolean animating) {
         mAnimating = animating;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1be0512..0f4707e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -31,6 +31,7 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 import static android.os.Process.myPid;
+import static android.os.Process.myTid;
 import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.DOCKED_INVALID;
@@ -147,6 +148,7 @@
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
@@ -169,7 +171,6 @@
 import android.util.TimeUtils;
 import android.util.TypedValue;
 import android.view.AppTransitionAnimationSpec;
-import android.view.Choreographer;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Gravity;
@@ -218,6 +219,7 @@
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.WindowManagerPolicyThread;
+import com.android.server.AnimationThread;
 import com.android.server.DisplayThread;
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
@@ -402,6 +404,9 @@
 
     final DisplaySettings mDisplaySettings;
 
+    /** If the system should display notifications for apps displaying an alert window. */
+    boolean mShowAlertWindowNotifications = true;
+
     /**
      * All currently active sessions with clients.
      */
@@ -601,7 +606,12 @@
 
     final H mH = new H();
 
-    final Choreographer mChoreographer = Choreographer.getInstance();
+    /**
+     * Handler for things to run that have direct impact on an animation, i.e. animation tick,
+     * layout, starting window creation, whereas {@link H} runs things that are still important, but
+     * not as critical.
+     */
+    final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper());
 
     WindowState mCurrentFocus = null;
     WindowState mLastFocus = null;
@@ -708,8 +718,6 @@
     // For frozen screen animations.
     private int mExitAnimId, mEnterAnimId;
 
-    boolean mAnimationScheduled;
-
     /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this
      * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */
     int mTransactionSequence;
@@ -4645,7 +4653,6 @@
     final class H extends android.os.Handler {
         public static final int REPORT_FOCUS_CHANGE = 2;
         public static final int REPORT_LOSING_FOCUS = 3;
-        public static final int DO_TRAVERSAL = 4;
         public static final int WINDOW_FREEZE_TIMEOUT = 11;
 
         public static final int APP_TRANSITION_TIMEOUT = 13;
@@ -4775,12 +4782,6 @@
                     }
                 } break;
 
-                case DO_TRAVERSAL: {
-                    synchronized(mWindowMap) {
-                        mWindowPlacerLocked.performSurfacePlacement();
-                    }
-                } break;
-
                 case WINDOW_FREEZE_TIMEOUT: {
                     // TODO(multidisplay): Can non-default displays rotate?
                     synchronized (mWindowMap) {
@@ -4849,7 +4850,7 @@
                     synchronized (mWindowMap) {
                         // Since we're holding both mWindowMap and mAnimator we don't need to
                         // hold mAnimator.mLayoutToAnim.
-                        if (mAnimator.isAnimating() || mAnimationScheduled) {
+                        if (mAnimator.isAnimating() || mAnimator.isAnimationScheduled()) {
                             // If we are animating, don't do the gc now but
                             // delay a bit so we don't interrupt the animation.
                             sendEmptyMessageDelayed(H.FORCE_GC, 2000);
@@ -5744,10 +5745,7 @@
 
     /** Note that Locked in this case is on mLayoutToAnim */
     void scheduleAnimationLocked() {
-        if (!mAnimationScheduled) {
-            mAnimationScheduled = true;
-            mChoreographer.postFrameCallback(mAnimator.mAnimationFrameCallback);
-        }
+        mAnimator.scheduleAnimation();
     }
 
     // TODO: Move to DisplayContent
@@ -7370,4 +7368,21 @@
             }
         }
     }
+
+    /** Called to inform window manager if non-Vr UI shoul be disabled or not. */
+    public void disableNonVrUi(boolean disable) {
+        synchronized (mWindowMap) {
+            // Allow alert window notifications to be shown if non-vr UI is enabled.
+            final boolean showAlertWindowNotifications = !disable;
+            if (showAlertWindowNotifications == mShowAlertWindowNotifications) {
+                return;
+            }
+            mShowAlertWindowNotifications = showAlertWindowNotifications;
+
+            for (int i = mSessions.size() - 1; i >= 0; --i) {
+                final Session s = mSessions.valueAt(i);
+                s.setShowingAlertWindowNotificationAllowed(mShowAlertWindowNotifications);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index ee2d5de..ddd1ca5 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -30,7 +30,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.DO_TRAVERSAL;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
 import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
@@ -97,9 +96,16 @@
     private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
     private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
 
+    private final Runnable mPerformSurfacePlacement;
+
     public WindowSurfacePlacer(WindowManagerService service) {
         mService = service;
         mWallpaperControllerLocked = mService.mRoot.mWallpaperController;
+        mPerformSurfacePlacement = () -> {
+            synchronized (mService.mWindowMap) {
+                performSurfacePlacement();
+            }
+        };
     }
 
     /**
@@ -131,7 +137,7 @@
         do {
             mTraversalScheduled = false;
             performSurfacePlacementLoop();
-            mService.mH.removeMessages(DO_TRAVERSAL);
+            mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
             loopCount--;
         } while (mTraversalScheduled && loopCount > 0);
         mService.mRoot.mWallpaperActionPending = false;
@@ -735,7 +741,7 @@
     void requestTraversal() {
         if (!mTraversalScheduled) {
             mTraversalScheduled = true;
-            mService.mH.sendEmptyMessage(DO_TRAVERSAL);
+            mService.mAnimationHandler.post(mPerformSurfacePlacement);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
index 25004de..3c8bf20 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
@@ -98,11 +98,11 @@
                 createAppWindowController();
         controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false);
-        waitUntilHandlerIdle();
+        waitUntilHandlersIdle();
         final AppWindowToken atoken = controller.getAppWindowToken();
         assertHasStartingWindow(atoken);
         controller.removeStartingWindow();
-        waitUntilHandlerIdle();
+        waitUntilHandlersIdle();
         assertNoStartingWindow(atoken);
     }
 
@@ -114,11 +114,11 @@
                 createAppWindowController();
         controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false);
-        waitUntilHandlerIdle();
+        waitUntilHandlersIdle();
         controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
                 true, true, false);
-        waitUntilHandlerIdle();
+        waitUntilHandlersIdle();
         assertNoStartingWindow(controller1.getAppWindowToken());
         assertHasStartingWindow(controller2.getAppWindowToken());
     }
@@ -138,7 +138,7 @@
         });
         controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(),
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false);
-        waitUntilHandlerIdle();
+        waitUntilHandlersIdle();
         assertNoStartingWindow(controller1.getAppWindowToken());
         assertHasStartingWindow(controller2.getAppWindowToken());
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index eaf4ac4..218af73 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -167,8 +167,9 @@
     /**
      * Waits until the main handler for WM has processed all messages.
      */
-    void waitUntilHandlerIdle() {
+    void waitUntilHandlersIdle() {
         sWm.mH.runWithScissors(() -> { }, 0);
+        sWm.mAnimationHandler.runWithScissors(() -> { }, 0);
     }
 
     private static WindowToken createWindowToken(DisplayContent dc, int stackId, int type) {