Merge "Control privileged permissions for priv-apps"
diff --git a/api/current.txt b/api/current.txt
index 2989145..f43ce6d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3892,6 +3892,7 @@
 
   public class ActivityOptions {
     method public android.graphics.Rect getLaunchBounds();
+    method public int getLaunchDisplayId();
     method public static android.app.ActivityOptions makeBasic();
     method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -3902,6 +3903,7 @@
     method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
     method public void requestUsageTimeReport(android.app.PendingIntent);
     method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
+    method public android.app.ActivityOptions setLaunchDisplayId(int);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
     field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
diff --git a/api/system-current.txt b/api/system-current.txt
index ab5acf5..f813204 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4024,6 +4024,7 @@
 
   public class ActivityOptions {
     method public android.graphics.Rect getLaunchBounds();
+    method public int getLaunchDisplayId();
     method public static android.app.ActivityOptions makeBasic();
     method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -4034,6 +4035,7 @@
     method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
     method public void requestUsageTimeReport(android.app.PendingIntent);
     method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
+    method public android.app.ActivityOptions setLaunchDisplayId(int);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
     field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
diff --git a/api/test-current.txt b/api/test-current.txt
index 76af8c5..da025ff 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3901,6 +3901,7 @@
 
   public class ActivityOptions {
     method public android.graphics.Rect getLaunchBounds();
+    method public int getLaunchDisplayId();
     method public static android.app.ActivityOptions makeBasic();
     method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -3911,6 +3912,7 @@
     method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
     method public void requestUsageTimeReport(android.app.PendingIntent);
     method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
+    method public android.app.ActivityOptions setLaunchDisplayId(int);
     method public void setLaunchStackId(int);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index d9a4690..1e7f4f0 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.view.Display.INVALID_DISPLAY;
 
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -152,6 +153,12 @@
     private static final String KEY_ANIM_SPECS = "android:activity.animSpecs";
 
     /**
+     * The display id the activity should be launched into.
+     * @hide
+     */
+    private static final String KEY_LAUNCH_DISPLAY_ID = "android.activity.launchDisplayId";
+
+    /**
      * The stack id the activity should be launched into.
      * @hide
      */
@@ -240,6 +247,7 @@
     private int mResultCode;
     private int mExitCoordinatorIndex;
     private PendingIntent mUsageTimeReport;
+    private int mLaunchDisplayId = INVALID_DISPLAY;
     private int mLaunchStackId = INVALID_STACK_ID;
     private int mLaunchTaskId = -1;
     private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
@@ -850,6 +858,7 @@
                 mExitCoordinatorIndex = opts.getInt(KEY_EXIT_COORDINATOR_INDEX);
                 break;
         }
+        mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY);
         mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID);
         mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
         mTaskOverlay = opts.getBoolean(KEY_TASK_OVERLAY, false);
@@ -1015,6 +1024,25 @@
         }
     }
 
+    /**
+     * Gets the id of the display where activity should be launched.
+     * @return The id of the display where activity should be launched,
+     *         {@link android.view.Display#INVALID_DISPLAY} if not set.
+     */
+    public int getLaunchDisplayId() {
+        return mLaunchDisplayId;
+    }
+
+    /**
+     * Sets the id of the display where activity should be launched.
+     * @param launchDisplayId The id of the display where the activity should be launched.
+     * @return {@code this} {@link ActivityOptions} instance.
+     */
+    public ActivityOptions setLaunchDisplayId(int launchDisplayId) {
+        mLaunchDisplayId = launchDisplayId;
+        return this;
+    }
+
     /** @hide */
     public int getLaunchStackId() {
         return mLaunchStackId;
@@ -1209,6 +1237,7 @@
                 b.putInt(KEY_EXIT_COORDINATOR_INDEX, mExitCoordinatorIndex);
                 break;
         }
+        b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId);
         b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId);
         b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId);
         b.putBoolean(KEY_TASK_OVERLAY, mTaskOverlay);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 79ace08..1aa13a9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5242,7 +5242,14 @@
         }
         updateDefaultDensity();
 
-        final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24));
+        final String use24HourSetting = mCoreSettings.getString(Settings.System.TIME_12_24);
+        Boolean is24Hr = null;
+        if (use24HourSetting != null) {
+            is24Hr = "24".equals(use24HourSetting) ? Boolean.TRUE : Boolean.FALSE;
+        }
+        // null : use locale default for 12/24 hour formatting,
+        // false : use 12 hour format,
+        // true : use 24 hour format.
         DateFormat.set24HourTimePref(is24Hr);
 
         View.mDebugViewAttributes =
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 6b16e8f..e222fee 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -764,7 +764,7 @@
         }
         if (!mAllowOptimization) {
             // Added fragments are added at the end to comply with prior behavior.
-            mManager.moveToState(mManager.mCurState);
+            mManager.moveToState(mManager.mCurState, true);
         }
     }
 
@@ -810,7 +810,7 @@
             }
         }
         if (!mAllowOptimization) {
-            mManager.moveToState(mManager.mCurState);
+            mManager.moveToState(mManager.mCurState, true);
         }
     }
 
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 5a2c5e7..07ef136 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1445,11 +1445,24 @@
         }
     }
 
-    void moveToState(int newState) {
+    /**
+     * Changes the state of the fragment manager to {@code newState}. If the fragment manager
+     * changes state or {@code always} is {@code true}, any fragments within it have their
+     * states updated as well.
+     *
+     * @param newState The new state for the fragment manager
+     * @param always If {@code true}, all fragments update their state, even
+     *               if {@code newState} matches the current fragment manager's state.
+     */
+    void moveToState(int newState, boolean always) {
         if (mHost == null && newState != Fragment.INITIALIZING) {
             throw new IllegalStateException("No activity");
         }
 
+        if (!always && mCurState == newState) {
+            return;
+        }
+
         mCurState = newState;
 
         if (mActive != null) {
@@ -2024,7 +2037,7 @@
             // need to run something now
             FragmentTransition.startTransitions(this, records, isRecordPop, startIndex,
                     postponeIndex, true);
-            moveToState(mCurState);
+            moveToState(mCurState, true);
         }
 
         for (int recordNum = startIndex; recordNum < endIndex; recordNum++) {
@@ -2117,7 +2130,7 @@
             FragmentTransition.startTransitions(this, records, isRecordPop, 0, 1, true);
         }
         if (moveToState) {
-            moveToState(mCurState);
+            moveToState(mCurState, true);
         } else if (mActive != null) {
             final int numActive = mActive.size();
             for (int i = 0; i < numActive; i++) {
@@ -2691,40 +2704,40 @@
     
     public void dispatchCreate() {
         mStateSaved = false;
-        moveToState(Fragment.CREATED);
+        moveToState(Fragment.CREATED, false);
     }
     
     public void dispatchActivityCreated() {
         mStateSaved = false;
-        moveToState(Fragment.ACTIVITY_CREATED);
+        moveToState(Fragment.ACTIVITY_CREATED, false);
     }
     
     public void dispatchStart() {
         mStateSaved = false;
-        moveToState(Fragment.STARTED);
+        moveToState(Fragment.STARTED, false);
     }
     
     public void dispatchResume() {
         mStateSaved = false;
-        moveToState(Fragment.RESUMED);
+        moveToState(Fragment.RESUMED, false);
     }
     
     public void dispatchPause() {
-        moveToState(Fragment.STARTED);
+        moveToState(Fragment.STARTED, false);
     }
     
     public void dispatchStop() {
-        moveToState(Fragment.STOPPED);
+        moveToState(Fragment.STOPPED, false);
     }
     
     public void dispatchDestroyView() {
-        moveToState(Fragment.CREATED);
+        moveToState(Fragment.CREATED, false);
     }
 
     public void dispatchDestroy() {
         mDestroyed = true;
         execPendingActions();
-        moveToState(Fragment.INITIALIZING);
+        moveToState(Fragment.INITIALIZING, false);
         mHost = null;
         mContainer = null;
         mParent = null;
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index cbd5a6d..d89d2bb 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -357,33 +357,40 @@
     public static final int BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE = 1;
 
     /** @hide */
-    public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
+    public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS =
+            "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
 
     /** @hide */
-    public static final String EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID = "android.app.extra.CHOOSE_PRIVATE_KEY_SENDER_UID";
+    public static final String EXTRA_CHOOSE_PRIVATE_KEY_SENDER_UID =
+            "android.app.extra.CHOOSE_PRIVATE_KEY_SENDER_UID";
 
     /** @hide */
-    public static final String EXTRA_CHOOSE_PRIVATE_KEY_URI = "android.app.extra.CHOOSE_PRIVATE_KEY_URI";
+    public static final String EXTRA_CHOOSE_PRIVATE_KEY_URI =
+            "android.app.extra.CHOOSE_PRIVATE_KEY_URI";
 
     /** @hide */
-    public static final String EXTRA_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.extra.CHOOSE_PRIVATE_KEY_ALIAS";
+    public static final String EXTRA_CHOOSE_PRIVATE_KEY_ALIAS =
+            "android.app.extra.CHOOSE_PRIVATE_KEY_ALIAS";
 
     /** @hide */
-    public static final String EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE = "android.app.extra.CHOOSE_PRIVATE_KEY_RESPONSE";
+    public static final String EXTRA_CHOOSE_PRIVATE_KEY_RESPONSE =
+            "android.app.extra.CHOOSE_PRIVATE_KEY_RESPONSE";
 
     /**
      * Broadcast action: notify device owner that there is a pending system update.
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_NOTIFY_PENDING_SYSTEM_UPDATE = "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE";
+    public static final String ACTION_NOTIFY_PENDING_SYSTEM_UPDATE =
+            "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE";
 
     /**
      * A long type extra for {@link #onSystemUpdatePending} recording the system time as given by
      * {@link System#currentTimeMillis()} when the current pending system update is first available.
      * @hide
      */
-    public static final String EXTRA_SYSTEM_UPDATE_RECEIVED_TIME = "android.app.extra.SYSTEM_UPDATE_RECEIVED_TIME";
+    public static final String EXTRA_SYSTEM_UPDATE_RECEIVED_TIME =
+            "android.app.extra.SYSTEM_UPDATE_RECEIVED_TIME";
 
     /**
      * Name under which a DevicePolicy component publishes information
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 18ce260..a9268d4e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -404,6 +404,7 @@
         // Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
         mBitmapCache = new BitmapCache();
         setBitmapCache(mBitmapCache);
+        recalculateMemoryUsage();
     }
 
     private class SetEmptyView extends Action {
@@ -2117,26 +2118,8 @@
             return mMemoryUsage;
         }
 
-        @SuppressWarnings("deprecation")
         public void addBitmapMemory(Bitmap b) {
-            final Bitmap.Config c = b.getConfig();
-            // If we don't know, be pessimistic and assume 4
-            int bpp = 4;
-            if (c != null) {
-                switch (c) {
-                    case ALPHA_8:
-                        bpp = 1;
-                        break;
-                    case RGB_565:
-                    case ARGB_4444:
-                        bpp = 2;
-                        break;
-                    case ARGB_8888:
-                        bpp = 4;
-                        break;
-                }
-            }
-            increment(b.getWidth() * b.getHeight() * bpp);
+            increment(b.getAllocationByteCount());
         }
 
         int mMemoryUsage;
diff --git a/core/java/com/android/internal/util/ToBooleanFunction.java b/core/java/com/android/internal/util/ToBooleanFunction.java
new file mode 100644
index 0000000..83866c2
--- /dev/null
+++ b/core/java/com/android/internal/util/ToBooleanFunction.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.util;
+
+import java.util.function.Function;
+
+/**
+ * Represents a function that produces an boolean-valued result.  This is the
+ * {@code boolean}-producing primitive specialization for {@link Function}.
+ *
+ * <p>This is a <a href="package-summary.html">functional interface</a>
+ * whose functional method is {@link #apply(Object)}.
+ *
+ * @param <T> the type of the input to the function
+ *
+ * @see Function
+ * @since 1.8
+ */
+@FunctionalInterface
+public interface ToBooleanFunction<T> {
+
+    /**
+     * Applies this function to the given argument.
+     *
+     * @param value the function argument
+     * @return the function result
+     */
+    boolean apply(T value);
+}
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index f909580..1a67cee 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -425,8 +425,8 @@
     status_t err = parcel->writeBuffer(s, sizeof(*s), &parentHandle);
 
     if (err == OK) {
-        err = s->writeEmbeddedToParcel(
-                parcel, parentHandle, 0 /* parentOffset */);
+        err = ::android::hardware::writeEmbeddedToParcel(
+                *s, parcel, parentHandle, 0 /* parentOffset */);
     }
 
     signalExceptionForError(env, err);
@@ -453,7 +453,8 @@
     if (err == OK) {                                                           \
         size_t childHandle;                                                    \
                                                                                \
-        err = vec->writeEmbeddedToParcel(                                      \
+        err = ::android::hardware::writeEmbeddedToParcel(                      \
+                *vec,                                                          \
                 parcel,                                                        \
                 parentHandle,                                                  \
                 0 /* parentOffset */,                                          \
@@ -508,7 +509,8 @@
     if (err == OK) {
         size_t childHandle;
 
-        err = vec->writeEmbeddedToParcel(
+        err = ::android::hardware::writeEmbeddedToParcel(
+                *vec,
                 parcel,
                 parentHandle,
                 0 /* parentOffset */,
@@ -568,7 +570,8 @@
         return NULL;
     }
 
-    status_t err = const_cast<hidl_string *>(s)->readEmbeddedFromParcel(
+    status_t err = ::android::hardware::readEmbeddedFromParcel(
+            const_cast<hidl_string *>(s),
             *parcel, parentHandle, 0 /* parentOffset */);
 
     if (err != OK) {
@@ -597,8 +600,8 @@
                                                                                \
     size_t childHandle;                                                        \
                                                                                \
-    status_t err = const_cast<hidl_vec<Type> *>(vec)                           \
-        ->readEmbeddedFromParcel(                                              \
+    status_t err = ::android::hardware::readEmbeddedFromParcel(                \
+                const_cast<hidl_vec<Type> *>(vec),                             \
                 *parcel,                                                       \
                 parentHandle,                                                  \
                 0 /* parentOffset */,                                          \
@@ -639,8 +642,8 @@
 
     size_t childHandle;
 
-    status_t err = const_cast<hidl_vec<bool> *>(vec)
-        ->readEmbeddedFromParcel(
+    status_t err = ::android::hardware::readEmbeddedFromParcel(
+                const_cast<hidl_vec<bool> *>(vec),
                 *parcel,
                 parentHandle,
                 0 /* parentOffset */,
@@ -701,12 +704,13 @@
     }
 
     size_t childHandle;
-    status_t err = const_cast<string_vec *>(vec)->readEmbeddedFromParcel(
+    status_t err = ::android::hardware::readEmbeddedFromParcel(
+            const_cast<string_vec *>(vec),
             *parcel, parentHandle, 0 /* parentOffset */, &childHandle);
 
     for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) {
-        err = const_cast<hidl_vec<hidl_string> *>(vec)
-            ->readEmbeddedFromParcel(
+        err = android::hardware::readEmbeddedFromParcel(
+                    const_cast<hidl_vec<hidl_string> *>(vec),
                     *parcel,
                     childHandle,
                     i * sizeof(hidl_string),
@@ -760,14 +764,16 @@
 
     if (err == OK) {
         size_t childHandle;
-        err = vec->writeEmbeddedToParcel(
+        err = ::android::hardware::writeEmbeddedToParcel(
+                *vec,
                 parcel,
                 parentHandle,
                 0 /* parentOffset */,
                 &childHandle);
 
         for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) {
-            err = (*vec)[i].writeEmbeddedToParcel(
+            err = ::android::hardware::writeEmbeddedToParcel(
+                    (*vec)[i],
                     parcel,
                     childHandle,
                     i * sizeof(hidl_string));
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 1743731..b9376d8 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -111,7 +111,7 @@
     }
 
     status_t error;
-    sp<GraphicBuffer> buffer(alloc->createGraphicBuffer(width, height, format, usage, &error));
+    sp<GraphicBuffer> buffer(alloc->createGraphicBuffer(width, height, format, 1, usage, &error));
     if (buffer == NULL) {
         if (kDebugGraphicBuffer) {
             ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 79c63ee..e628cf8 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1241,6 +1241,11 @@
      * #getAllocationByteCount()}.</p>
      */
     public final int getByteCount() {
+        if (mRecycled) {
+            Log.w(TAG, "Called getByteCount() on a recycle()'d bitmap! "
+                    + "This is undefined behavior!");
+            return 0;
+        }
         // int result permits bitmaps up to 46,340 x 46,340
         return getRowBytes() * getHeight();
     }
@@ -1260,6 +1265,11 @@
      * @see #reconfigure(int, int, Config)
      */
     public final int getAllocationByteCount() {
+        if (mRecycled) {
+            Log.w(TAG, "Called getAllocationByteCount() on a recycle()'d bitmap! "
+                    + "This is undefined behavior!");
+            return 0;
+        }
         return nativeGetAllocationByteCount(mNativePtr);
     }
 
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index b8f7d9f..2077b0e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -227,7 +227,7 @@
     PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
     status_t error;
     sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(info.width(), info.height(), pixelFormat,
-            GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER
+            1, GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER
             | GraphicBuffer::USAGE_SW_READ_NEVER , &error);
 
     if (!buffer.get()) {
diff --git a/packages/Osu/src/com/android/hotspot2/flow/PlatformAdapter.java b/packages/Osu/src/com/android/hotspot2/flow/PlatformAdapter.java
index 43cc1d6..d95af61 100644
--- a/packages/Osu/src/com/android/hotspot2/flow/PlatformAdapter.java
+++ b/packages/Osu/src/com/android/hotspot2/flow/PlatformAdapter.java
@@ -530,7 +530,8 @@
 
     private int addSP(String xml) throws IOException, SAXException {
         WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
-        return wifiManager.addPasspointManagementObject(xml);
+        // TODO(b/32883320): use the new API for adding Passpoint configuration.
+        return 0;
     }
 
     private int modifySP(HomeSP homeSP, Collection<MOData> mods) throws IOException {
@@ -540,7 +541,8 @@
             defMods.add(new PasspointManagementObjectDefinition(mod.getBaseURI(),
                     mod.getURN(), mod.getMOTree().toXml()));
         }
-        return wifiManager.modifyPasspointManagementObject(homeSP.getFQDN(), defMods);
+        // TODO(b/32883320): use the new API to update Passpoint configuration.
+        return 0;
     }
 
     private void reconnect(Network osuNetwork, int newNwkId) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index ed411be..14a0e82 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -18,6 +18,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
@@ -25,12 +26,15 @@
 import com.android.settingslib.applications.InterestingConfigChanges;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
+import static java.lang.String.CASE_INSENSITIVE_ORDER;
+
 public class CategoryManager {
 
     private static final String TAG = "CategoryManager";
@@ -111,6 +115,7 @@
                 mCategoryByKeyMap.put(category.key, category);
             }
             backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
+            normalizePriority(context, mCategoryByKeyMap);
         }
     }
 
@@ -163,4 +168,57 @@
             }
         }
     }
+
+    /**
+     * Normalize priority values on tiles across injected from all apps to make sure they don't set
+     * the same priority value. However internal tiles' priority remains unchanged.
+     * <p/>
+     * A list of tiles are considered normalized when their priority value increases in a linear
+     * scan.
+     */
+    @VisibleForTesting
+    synchronized void normalizePriority(Context context,
+            Map<String, DashboardCategory> categoryByKeyMap) {
+        for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
+            normalizePriorityForExternalTiles(context, categoryEntry.getValue());
+        }
+    }
+
+    /**
+     * Normalize priority value for tiles within a single {@code DashboardCategory}.
+     *
+     * @see #normalizePriority(Context, Map)
+     */
+    private synchronized void normalizePriorityForExternalTiles(Context context,
+            DashboardCategory dashboardCategory) {
+        final String skipPackageName = context.getPackageName();
+
+        // Sort tiles based on [package, priority within package]
+        Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> {
+            final String package1 = tile1.intent.getComponent().getPackageName();
+            final String package2 = tile2.intent.getComponent().getPackageName();
+            final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
+            // First sort by package name
+            if (packageCompare != 0) {
+                return packageCompare;
+            } else if (TextUtils.equals(package1, skipPackageName)) {
+                return 0;
+            }
+            // Then sort by priority
+            return tile1.priority - tile2.priority;
+        });
+        // Update priority for all items so no package define the same priority value.
+        final int count = dashboardCategory.tiles.size();
+        for (int i = 0; i < count; i++) {
+            final String packageName =
+                    dashboardCategory.tiles.get(i).intent.getComponent().getPackageName();
+            if (TextUtils.equals(packageName, skipPackageName)) {
+                // We skip this tile because it's a intent pointing to our own app. We trust the
+                // priority is set correctly, so don't normalize.
+                continue;
+            }
+            dashboardCategory.tiles.get(i).priority = i;
+
+        }
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
index 380f622..50bb216 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
@@ -16,7 +16,9 @@
 
 package com.android.settingslib.drawer;
 
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.util.Pair;
 
 import com.android.settingslib.TestConfig;
@@ -116,4 +118,114 @@
         // Old category still exists.
         assertThat(mCategoryByKeyMap.get(oldCategory).tiles.size()).isEqualTo(1);
     }
+
+    @Test
+    public void normalizePriority_singlePackage_shouldReorderBasedOnPriority() {
+        // Create some fake tiles that are not sorted.
+        final String testPackage = "com.android.test";
+        final DashboardCategory category = new DashboardCategory();
+        final Tile tile1 = new Tile();
+        tile1.intent =
+                new Intent().setComponent(new ComponentName(testPackage, "class1"));
+        tile1.priority = 100;
+        final Tile tile2 = new Tile();
+        tile2.intent =
+                new Intent().setComponent(new ComponentName(testPackage, "class2"));
+        tile2.priority = 50;
+        final Tile tile3 = new Tile();
+        tile3.intent =
+                new Intent().setComponent(new ComponentName(testPackage, "class3"));
+        tile3.priority = 200;
+        category.tiles.add(tile1);
+        category.tiles.add(tile2);
+        category.tiles.add(tile3);
+        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
+
+        // Normalize their priorities
+        mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(),
+                mCategoryByKeyMap);
+
+        // Verify they are now sorted.
+        assertThat(category.tiles.get(0)).isSameAs(tile2);
+        assertThat(category.tiles.get(1)).isSameAs(tile1);
+        assertThat(category.tiles.get(2)).isSameAs(tile3);
+        // Verify their priority is normalized
+        assertThat(category.tiles.get(0).priority).isEqualTo(0);
+        assertThat(category.tiles.get(1).priority).isEqualTo(1);
+        assertThat(category.tiles.get(2).priority).isEqualTo(2);
+    }
+
+    @Test
+    public void normalizePriority_multiPackage_shouldReorderBasedOnPackageAndPriority() {
+        // Create some fake tiles that are not sorted.
+        final String testPackage1 = "com.android.test1";
+        final String testPackage2 = "com.android.test2";
+        final DashboardCategory category = new DashboardCategory();
+        final Tile tile1 = new Tile();
+        tile1.intent =
+                new Intent().setComponent(new ComponentName(testPackage2, "class1"));
+        tile1.priority = 100;
+        final Tile tile2 = new Tile();
+        tile2.intent =
+                new Intent().setComponent(new ComponentName(testPackage1, "class2"));
+        tile2.priority = 100;
+        final Tile tile3 = new Tile();
+        tile3.intent =
+                new Intent().setComponent(new ComponentName(testPackage1, "class3"));
+        tile3.priority = 50;
+        category.tiles.add(tile1);
+        category.tiles.add(tile2);
+        category.tiles.add(tile3);
+        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
+
+        // Normalize their priorities
+        mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(),
+                mCategoryByKeyMap);
+
+        // Verify they are now sorted.
+        assertThat(category.tiles.get(0)).isSameAs(tile3);
+        assertThat(category.tiles.get(1)).isSameAs(tile2);
+        assertThat(category.tiles.get(2)).isSameAs(tile1);
+        // Verify their priority is normalized
+        assertThat(category.tiles.get(0).priority).isEqualTo(0);
+        assertThat(category.tiles.get(1).priority).isEqualTo(1);
+        assertThat(category.tiles.get(2).priority).isEqualTo(2);
+    }
+
+    @Test
+    public void normalizePriority_internalPackageTiles_shouldSkipTileForInternalPackage() {
+        // Create some fake tiles that are not sorted.
+        final String testPackage =
+                ShadowApplication.getInstance().getApplicationContext().getPackageName();
+        final DashboardCategory category = new DashboardCategory();
+        final Tile tile1 = new Tile();
+        tile1.intent =
+                new Intent().setComponent(new ComponentName(testPackage, "class1"));
+        tile1.priority = 100;
+        final Tile tile2 = new Tile();
+        tile2.intent =
+                new Intent().setComponent(new ComponentName(testPackage, "class2"));
+        tile2.priority = 100;
+        final Tile tile3 = new Tile();
+        tile3.intent =
+                new Intent().setComponent(new ComponentName(testPackage, "class3"));
+        tile3.priority = 50;
+        category.tiles.add(tile1);
+        category.tiles.add(tile2);
+        category.tiles.add(tile3);
+        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
+
+        // Normalize their priorities
+        mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(),
+                mCategoryByKeyMap);
+
+        // Verify the sorting order is not changed
+        assertThat(category.tiles.get(0)).isSameAs(tile1);
+        assertThat(category.tiles.get(1)).isSameAs(tile2);
+        assertThat(category.tiles.get(2)).isSameAs(tile3);
+        // Verify their priorities are not changed.
+        assertThat(category.tiles.get(0).priority).isEqualTo(100);
+        assertThat(category.tiles.get(1).priority).isEqualTo(100);
+        assertThat(category.tiles.get(2).priority).isEqualTo(50);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 8ca277e..42b06b2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -797,7 +797,7 @@
 
         // update slider
         final boolean enableSlider = !zenMuted;
-        final int vlevel = row.ss.muted && (isRingVibrate || !isRingStream && !zenMuted) ? 0
+        final int vlevel = row.ss.muted && (!isRingStream && !zenMuted) ? 0
                 : row.ss.level;
         updateVolumeRowSliderH(row, enableSlider, vlevel);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 87f6138..12a00e9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -552,8 +552,9 @@
             setToMidnight(nextAlarm);
 
             if (weekRange.compareTo(nextAlarm) >= 0) {
-                return ZenModeConfig.toNextAlarmCondition(mContext, now,
-                        nextAlarmMs, ActivityManager.getCurrentUser());
+                return ZenModeConfig.toTimeCondition(mContext, nextAlarmMs,
+                        Math.round((nextAlarmMs - now) / (float) MINUTES_MS),
+                        ActivityManager.getCurrentUser(), true);
             }
         }
         return null;
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 5cf74c7..9b4b186 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -22,6 +22,27 @@
 // Wrapper for System UI log events
 message MetricsEvent {
 
+  // Types of events
+  enum Type {
+    // Unknown
+    TYPE_UNKNOWN = 0;
+
+    // The view became visible to the user.
+    TYPE_OPEN = 1;
+
+    // The view became hidden.
+    TYPE_CLOSE = 2;
+
+    // The view switched to detail mode (most relevant for quick settings tiles)
+    TYPE_DETAIL = 3;
+
+    // The view or control was activated.
+    TYPE_ACTION = 4;
+
+    // The view or control was dismissed.
+    TYPE_DISMISS = 5;
+  }
+
   // Known visual elements: views or controls.
   enum View {
     // Unknown view
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index bf60e47..6404604 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -1593,14 +1593,6 @@
 
         // Make sure the package runs under the caller uid.
         mSecurityPolicy.enforceCallFromPackage(callingPackage);
-
-        final int bitmapMemoryUsage = (views != null) ? views.estimateMemoryUsage() : 0;
-        if (bitmapMemoryUsage > mMaxWidgetBitmapMemory) {
-            throw new IllegalArgumentException("RemoteViews for widget update exceeds"
-                    + " maximum bitmap memory usage (used: " + bitmapMemoryUsage
-                    + ", max: " + mMaxWidgetBitmapMemory + ")");
-        }
-
         synchronized (mLock) {
             ensureGroupStateLoadedLocked(userId);
 
@@ -1812,6 +1804,15 @@
                 // For a full update we replace the RemoteViews completely.
                 widget.views = views;
             }
+            int memoryUsage;
+            if ((UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) &&
+                    (widget.views != null) &&
+                    ((memoryUsage = widget.views.estimateMemoryUsage()) > mMaxWidgetBitmapMemory)) {
+                widget.views = null;
+                throw new IllegalArgumentException("RemoteViews for widget update exceeds"
+                        + " maximum bitmap memory usage (used: " + memoryUsage
+                        + ", max: " + mMaxWidgetBitmapMemory + ")");
+            }
             scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 52ad72d..14b843a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -80,6 +80,7 @@
 import static android.app.ActivityManager.RESIZE_MODE_USER;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.view.Display.INVALID_DISPLAY;
 
 final class ActivityManagerShellCommand extends ShellCommand {
     public static final String NO_CLASS_ERROR_CODE = "Error type 3";
@@ -114,6 +115,7 @@
     private String mProfileFile;
     private int mSamplingInterval;
     private boolean mAutoStop;
+    private int mDisplayId;
     private int mStackId;
 
     final boolean mDumping;
@@ -249,6 +251,7 @@
         mSamplingInterval = 0;
         mAutoStop = false;
         mUserId = defUser;
+        mDisplayId = INVALID_DISPLAY;
         mStackId = INVALID_STACK_ID;
 
         return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
@@ -278,6 +281,8 @@
                     mUserId = UserHandle.parseUserArg(getNextArgRequired());
                 } else if (opt.equals("--receiver-permission")) {
                     mReceiverPermission = getNextArgRequired();
+                } else if (opt.equals("--display")) {
+                    mDisplayId = Integer.parseInt(getNextArgRequired());
                 } else if (opt.equals("--stack")) {
                     mStackId = Integer.parseInt(getNextArgRequired());
                 } else {
@@ -354,6 +359,10 @@
             int res;
             final long startTime = SystemClock.uptimeMillis();
             ActivityOptions options = null;
+            if (mDisplayId != INVALID_DISPLAY) {
+                options = ActivityOptions.makeBasic();
+                options.setLaunchDisplayId(mDisplayId);
+            }
             if (mStackId != INVALID_STACK_ID) {
                 options = ActivityOptions.makeBasic();
                 options.setLaunchStackId(mStackId);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 282ec50..539ac16 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -38,6 +38,8 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_PRIVATE;
+import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
@@ -90,6 +92,7 @@
 import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -1483,16 +1486,35 @@
             Slog.w(TAG, message);
             return false;
         }
-        if (options != null && options.getLaunchTaskId() != -1) {
-            final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,
-                    callingPid, callingUid);
-            if (startInTaskPerm != PERMISSION_GRANTED) {
-                final String msg = "Permission Denial: starting " + intent.toString()
-                        + " from " + callerApp + " (pid=" + callingPid
-                        + ", uid=" + callingUid + ") with launchTaskId="
-                        + options.getLaunchTaskId();
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
+        if (options != null) {
+            if (options.getLaunchTaskId() != INVALID_STACK_ID) {
+                final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,
+                        callingPid, callingUid);
+                if (startInTaskPerm != PERMISSION_GRANTED) {
+                    final String msg = "Permission Denial: starting " + intent.toString()
+                            + " from " + callerApp + " (pid=" + callingPid
+                            + ", uid=" + callingUid + ") with launchTaskId="
+                            + options.getLaunchTaskId();
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+                }
+            }
+            // Check if someone tries to launch an activity on a private display with a different
+            // owner.
+            final int launchDisplayId = options.getLaunchDisplayId();
+            if (launchDisplayId != INVALID_DISPLAY) {
+                final ActivityDisplay activityDisplay = mActivityDisplays.get(launchDisplayId);
+                if (activityDisplay != null
+                        && (activityDisplay.mDisplay.getFlags() & FLAG_PRIVATE) != 0) {
+                    if (activityDisplay.mDisplay.getOwnerUid() != callingUid) {
+                        final String msg = "Permission Denial: starting " + intent.toString()
+                                + " from " + callerApp + " (pid=" + callingPid
+                                + ", uid=" + callingUid + ") with launchDisplayId="
+                                + launchDisplayId;
+                        Slog.w(TAG, msg);
+                        throw new SecurityException(msg);
+                    }
+                }
             }
         }
 
@@ -1937,7 +1959,7 @@
     }
 
     ActivityStack getStack(int stackId, boolean createStaticStackIfNeeded, boolean createOnTop) {
-        ActivityContainer activityContainer = mActivityContainers.get(stackId);
+        final ActivityContainer activityContainer = mActivityContainers.get(stackId);
         if (activityContainer != null) {
             return activityContainer.mStack;
         }
@@ -1948,6 +1970,40 @@
         return createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
     }
 
+    /**
+     * Get a topmost stack on the display, that is a valid launch stack for specified activity.
+     * If there is no such stack, new dynamic stack can be created.
+     * @param displayId Target display.
+     * @param r Activity that should be launched there.
+     * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
+     */
+    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r) {
+        final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException(
+                    "Display with displayId=" + displayId + " not found.");
+        }
+
+        // Return the topmost valid stack on the display.
+        for (int i = activityDisplay.mStacks.size() - 1; i >= 0; --i) {
+            final ActivityStack stack = activityDisplay.mStacks.get(i);
+            if (mService.mActivityStarter.isValidLaunchStackId(stack.mStackId, r)) {
+                return stack;
+            }
+        }
+
+        // If there is no valid stack on the external display - check if new dynamic stack will do.
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            final int newDynamicStackId = getNextStackId();
+            if (mService.mActivityStarter.isValidLaunchStackId(newDynamicStackId, r)) {
+                return createStackOnDisplay(newDynamicStackId, displayId, true /*onTop*/);
+            }
+        }
+
+        Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
+        return null;
+    }
+
     ArrayList<ActivityStack> getStacks() {
         ArrayList<ActivityStack> allStacks = new ArrayList<>();
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -2326,11 +2382,15 @@
      * Restores a recent task to a stack
      * @param task The recent task to be restored.
      * @param stackId The stack to restore the task to (default launch stack will be used
-     *                if stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}).
+     *                if stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}
+     *                or is not a static stack).
      * @return true if the task has been restored successfully.
      */
     private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
-        if (stackId == INVALID_STACK_ID) {
+        if (!StackId.isStaticStack(stackId)) {
+            // If stack is not static (or stack id is invalid) - use the default one.
+            // This means that tasks that were on external displays will be restored on the
+            // primary display.
             stackId = task.getLaunchStackId();
         } else if (stackId == DOCKED_STACK_ID && !task.canGoInDockedStack()) {
             // Preferred stack is the docked stack, but the task can't go in the docked stack.
@@ -3504,7 +3564,10 @@
             if (activityDisplay != null) {
                 ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
                 for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-                    stacks.get(stackNdx).mActivityContainer.removeLocked();
+                    final ActivityStack stack = stacks.get(stackNdx);
+                    // TODO: Implement proper stack removal and ability to choose the behavior -
+                    // remove stack completely or move it to other display.
+                    moveStackToDisplayLocked(stack.mStackId, DEFAULT_DISPLAY);
                 }
                 mActivityDisplays.remove(displayId);
             }
@@ -4155,7 +4218,10 @@
             }
         }
 
-        /** Remove the stack completely. */
+        /**
+         * Remove the stack completely. Must be called only when there are no tasks left in it,
+         * as this method does not finish running activities.
+         */
         void removeLocked() {
             if (DEBUG_STACK) Slog.d(TAG_STACK, "removeLocked: " + this + " from display="
                     + mActivityDisplay + " Callers=" + Debug.getCallers(2));
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 691d6b9..709c3d0 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -31,6 +31,7 @@
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.isStaticStack;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -50,6 +51,8 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
+import static android.view.Display.INVALID_DISPLAY;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
@@ -254,11 +257,7 @@
 
         if (err == ActivityManager.START_SUCCESS) {
             Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
-                    + "} from uid " + callingUid
-                    + " on display " + (container == null ? (mSupervisor.mFocusedStack == null ?
-                    Display.DEFAULT_DISPLAY : mSupervisor.mFocusedStack.mDisplayId) :
-                    (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
-                            container.mActivityDisplay.mDisplayId)));
+                    + "} from uid " + callingUid);
         }
 
         ActivityRecord sourceRecord = null;
@@ -1952,12 +1951,14 @@
 
         // The fullscreen stack can contain any task regardless of if the task is resizeable
         // or not. So, we let the task go in the fullscreen task if it is the focus stack.
+        // Same also applies to dynamic stacks, as they behave similar to fullscreen stack.
         // If the freeform or docked stack has focus, and the activity to be launched is resizeable,
         // we can also put it in the focused stack.
         final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
         final boolean canUseFocusedStack = focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
                 || (focusedStackId == DOCKED_STACK_ID && r.canGoInDockedStack())
-                || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeableOrForced());
+                || (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeableOrForced())
+                || !isStaticStack(focusedStackId);
         if (canUseFocusedStack && (!newTask
                 || mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
             if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
@@ -1965,7 +1966,7 @@
             return mSupervisor.mFocusedStack;
         }
 
-        // We first try to put the task in the first dynamic stack.
+        // We first try to put the task in the first dynamic stack on home display.
         final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
         for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             stack = homeDisplayStacks.get(stackNdx);
@@ -1994,16 +1995,29 @@
             return mReuseTask.getStack();
         }
 
+        final int launchDisplayId =
+                (aOptions != null) ? aOptions.getLaunchDisplayId() : INVALID_DISPLAY;
+
         final int launchStackId =
                 (aOptions != null) ? aOptions.getLaunchStackId() : INVALID_STACK_ID;
 
+        if (launchStackId != INVALID_STACK_ID && launchDisplayId != INVALID_DISPLAY) {
+            throw new IllegalArgumentException(
+                    "Stack and display id can't be set at the same time.");
+        }
+
         if (isValidLaunchStackId(launchStackId, r)) {
             return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP);
-        } else if (launchStackId == DOCKED_STACK_ID) {
+        }
+        if (launchStackId == DOCKED_STACK_ID) {
             // The preferred launch stack is the docked stack, but it isn't a valid launch stack
             // for this activity, so we put the activity in the fullscreen stack.
             return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
         }
+        if (launchDisplayId != INVALID_DISPLAY) {
+            // Stack id has higher priority than display id.
+            return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r);
+        }
 
         if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0) {
             return null;
@@ -2047,9 +2061,8 @@
         }
     }
 
-    private boolean isValidLaunchStackId(int stackId, ActivityRecord r) {
-        if (stackId == INVALID_STACK_ID || stackId == HOME_STACK_ID
-                || !StackId.isStaticStack(stackId)) {
+    boolean isValidLaunchStackId(int stackId, ActivityRecord r) {
+        if (stackId == INVALID_STACK_ID || stackId == HOME_STACK_ID) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 6d97796..0a7454f 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -36,6 +36,7 @@
 import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.Arrays;
 
 /**
@@ -259,5 +260,18 @@
                 Binder.restoreCallingIdentity(callingId);
             }
         }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+
+                pw.println("Permission Denial: can't dump webviewupdate service from pid="
+                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+
+            WebViewUpdateService.this.mImpl.dumpState(pw);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 453e745..1a77c68 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -30,6 +30,7 @@
 import android.webkit.WebViewProviderInfo;
 import android.webkit.WebViewProviderResponse;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -673,6 +674,27 @@
             mMinimumVersionCode = minimumVersionCode;
             return mMinimumVersionCode;
         }
+
+        public void dumpState(PrintWriter pw) {
+            synchronized (mLock) {
+                if (mCurrentWebViewPackage == null) {
+                    pw.println("  Current WebView package is null");
+                } else {
+                    pw.println(String.format("  Current WebView package (name, version): (%s, %s)",
+                            mCurrentWebViewPackage.packageName,
+                            mCurrentWebViewPackage.versionName));
+                }
+                pw.println(String.format("  Minimum WebView version code: %d",
+                      mMinimumVersionCode));
+                pw.println(String.format("  Number of relros started: %d",
+                        mNumRelroCreationsStarted));
+                pw.println(String.format("  Number of relros finished: %d",
+                            mNumRelroCreationsFinished));
+                pw.println(String.format("  WebView package dirty: %b", mWebViewPackageDirty));
+                pw.println(String.format("  Any WebView package installed: %b",
+                        mAnyWebViewInstalled));
+            }
+        }
     }
 
     private static boolean providerHasValidSignature(WebViewProviderInfo provider,
@@ -741,4 +763,14 @@
             mSystemInterface.setMultiProcessEnabledFromContext(mContext);
         }
     }
+
+    /**
+     * Dump the state of this Service.
+     */
+    void dumpState(PrintWriter pw) {
+        pw.println("Current WebView Update Service state");
+        pw.println(String.format("  Fallback logic enabled: %b",
+                mSystemInterface.isFallbackLogicEnabled()));
+        mWebViewUpdater.dumpState(pw);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 05e6f96..0844d48 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -47,6 +47,7 @@
 import static com.android.server.wm.WindowManagerService.logWithStack;
 
 import android.os.Debug;
+import com.android.internal.util.ToBooleanFunction;
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.wm.WindowManagerService.H;
 
@@ -66,7 +67,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.function.Consumer;
+import java.util.function.Function;
 
 class AppTokenList extends ArrayList<AppWindowToken> {
 }
@@ -1270,19 +1271,20 @@
     }
 
     @Override
-    void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
+    boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
         // For legacy reasons we process the TaskStack.mExitingAppTokens first in DisplayContent
         // before the non-exiting app tokens. So, we skip the exiting app tokens here.
         // TODO: Investigate if we need to continue to do this or if we can just process them
         // in-order.
         if (mIsExiting && !waitingForReplacement()) {
-            return;
+            return false;
         }
-        forAllWindowsUnchecked(callback, traverseTopToBottom);
+        return forAllWindowsUnchecked(callback, traverseTopToBottom);
     }
 
-    void forAllWindowsUnchecked(Consumer<WindowState> callback, boolean traverseTopToBottom) {
-        super.forAllWindows(callback, traverseTopToBottom);
+    boolean forAllWindowsUnchecked(ToBooleanFunction<WindowState> callback,
+            boolean traverseTopToBottom) {
+        return super.forAllWindows(callback, traverseTopToBottom);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ff39853..e73acde 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -127,6 +127,7 @@
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.view.IInputMethodClient;
 import com.android.server.input.InputWindowHandle;
 
@@ -141,6 +142,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Utility class for keeping track of the WindowStates and other pertinent contents of a
@@ -725,7 +727,8 @@
             win.getTouchableRegion(mTmpRegion);
             mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION);
         }
-        if (getDockedStackLocked() != null) {
+        // TODO(multi-display): Support docked stacks on secondary displays.
+        if (mDisplayId == DEFAULT_DISPLAY && getDockedStackLocked() != null) {
             mDividerControllerLocked.getTouchRegion(mTmpRect);
             mTmpRegion.set(mTmpRect);
             mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
@@ -1407,9 +1410,7 @@
     }
 
     void adjustWallpaperWindows() {
-        if (mWallpaperController.adjustWallpaperWindows(mWindows)) {
-            assignWindowLayers(true /*setLayoutNeeded*/);
-        }
+        mWallpaperController.adjustWallpaperWindows(this);
     }
 
     /**
@@ -3235,17 +3236,27 @@
         }
 
         @Override
-        void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
+        boolean forAllWindows(ToBooleanFunction<WindowState> callback,
+                boolean traverseTopToBottom) {
             if (traverseTopToBottom) {
-                super.forAllWindows(callback, traverseTopToBottom);
-                forAllExitingAppTokenWindows(callback, traverseTopToBottom);
+                if (super.forAllWindows(callback, traverseTopToBottom)) {
+                    return true;
+                }
+                if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
+                    return true;
+                }
             } else {
-                forAllExitingAppTokenWindows(callback, traverseTopToBottom);
-                super.forAllWindows(callback, traverseTopToBottom);
+                if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
+                    return true;
+                }
+                if (super.forAllWindows(callback, traverseTopToBottom)) {
+                    return true;
+                }
             }
+            return false;
         }
 
-        private void forAllExitingAppTokenWindows(Consumer<WindowState> callback,
+        private boolean forAllExitingAppTokenWindows(ToBooleanFunction<WindowState> callback,
                 boolean traverseTopToBottom) {
             // For legacy reasons we process the TaskStack.mExitingAppTokens first here before the
             // app tokens.
@@ -3255,7 +3266,10 @@
                 for (int i = mChildren.size() - 1; i >= 0; --i) {
                     final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
                     for (int j = appTokens.size() - 1; j >= 0; --j) {
-                        appTokens.get(j).forAllWindowsUnchecked(callback, traverseTopToBottom);
+                        if (appTokens.get(j).forAllWindowsUnchecked(callback,
+                                traverseTopToBottom)) {
+                            return true;
+                        }
                     }
                 }
             } else {
@@ -3264,10 +3278,14 @@
                     final AppTokenList appTokens = mChildren.get(i).mExitingAppTokens;
                     final int appTokensCount = appTokens.size();
                     for (int j = 0; j < appTokensCount; j++) {
-                        appTokens.get(j).forAllWindowsUnchecked(callback, traverseTopToBottom);
+                        if (appTokens.get(j).forAllWindowsUnchecked(callback,
+                                traverseTopToBottom)) {
+                            return true;
+                        }
                     }
                 }
             }
+            return false;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 4d195e8..40b737d 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -271,8 +271,9 @@
             Slog.d(TAG_WM, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
         }
 
-        mDisplayContent.forAllWindows((w) -> sendDragStartedLw(w, touchX, touchY, mDataDescription),
-                false /* traverseTopToBottom */ );
+        mDisplayContent.forAllWindows(w -> {
+            sendDragStartedLw(w, touchX, touchY, mDataDescription);
+        }, false /* traverseTopToBottom */ );
     }
 
     /* helper - send a ACTION_DRAG_STARTED event, if the
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index d3e8e8e..8dbf2b3 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -17,9 +17,9 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 
@@ -30,8 +30,6 @@
 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.WALLPAPER_DRAW_PENDING_TIMEOUT;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
 
 import android.os.Bundle;
 import android.os.Debug;
@@ -61,11 +59,8 @@
     // with the wallpaper.
     private WindowState mWallpaperTarget = null;
     // If non-null, we are in the middle of animating from one wallpaper target
-    // to another, and this is the lower one in Z-order.
-    private WindowState mLowerWallpaperTarget = null;
-    // If non-null, we are in the middle of animating from one wallpaper target
-    // to another, and this is the higher one in Z-order.
-    private WindowState mUpperWallpaperTarget = null;
+    // to another, and this is the previous wallpaper target.
+    private WindowState mPrevWallpaperTarget = null;
 
     private int mWallpaperAnimLayerAdjustment;
 
@@ -78,7 +73,7 @@
 
     // This is set when we are waiting for a wallpaper to tell us it is done
     // changing its scroll position.
-    WindowState mWaitingOnWallpaper;
+    private WindowState mWaitingOnWallpaper;
 
     // The last time we had a timeout when waiting for a wallpaper.
     private long mLastWallpaperTimeoutTime;
@@ -110,14 +105,6 @@
         return mWallpaperTarget;
     }
 
-    WindowState getLowerWallpaperTarget() {
-        return mLowerWallpaperTarget;
-    }
-
-    WindowState getUpperWallpaperTarget() {
-        return mUpperWallpaperTarget;
-    }
-
     boolean isWallpaperTarget(WindowState win) {
         return win == mWallpaperTarget;
     }
@@ -145,13 +132,11 @@
                 + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
                 + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
                 ? wallpaperTarget.mAppToken.mAppAnimator.animation : null)
-                + " upper=" + mUpperWallpaperTarget
-                + " lower=" + mLowerWallpaperTarget);
+                + " prev=" + mPrevWallpaperTarget);
         return (wallpaperTarget != null
                 && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
                 && wallpaperTarget.mAppToken.mAppAnimator.animation != null)))
-                || mUpperWallpaperTarget != null
-                || mLowerWallpaperTarget != null;
+                || mPrevWallpaperTarget != null;
     }
 
     boolean isWallpaperTargetAnimating() {
@@ -177,7 +162,7 @@
 
     void hideWallpapers(final WindowState winGoingAway) {
         if (mWallpaperTarget != null
-                && (mWallpaperTarget != winGoingAway || mLowerWallpaperTarget != null)) {
+                && (mWallpaperTarget != winGoingAway || mPrevWallpaperTarget != null)) {
             return;
         }
         if (mService.mAppTransition.isRunning()) {
@@ -192,8 +177,8 @@
             final WallpaperWindowToken token = mWallpaperTokens.get(i);
             token.hideWallpaperToken(wasDeferred, "hideWallpapers");
             if (DEBUG_WALLPAPER_LIGHT && !token.hidden) Slog.d(TAG, "Hiding wallpaper " + token
-                    + " from " + winGoingAway + " target=" + mWallpaperTarget + " lower="
-                    + mLowerWallpaperTarget + "\n" + Debug.getCallers(5, "  "));
+                    + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
+                    + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, "  "));
         }
     }
 
@@ -299,9 +284,7 @@
 
     Bundle sendWindowWallpaperCommand(
             WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
-        if (window == mWallpaperTarget
-                || window == mLowerWallpaperTarget
-                || window == mUpperWallpaperTarget) {
+        if (window == mWallpaperTarget || window == mPrevWallpaperTarget) {
             boolean doWait = sync;
             for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
                 final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
@@ -388,51 +371,52 @@
         return mWallpaperAnimLayerAdjustment;
     }
 
-    private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) {
+    private void findWallpaperTarget(DisplayContent dc , FindWallpaperTargetResult result) {
         final WindowAnimator winAnimator = mService.mAnimator;
         result.reset();
-        WindowState w = null;
-        int windowDetachedI = -1;
-        boolean resetTopWallpaper = false;
-        boolean inFreeformSpace = false;
-        boolean replacing = false;
-        boolean keyguardGoingAwayWithWallpaper = false;
-        boolean needsShowWhenLockedWallpaper = false;
+        if (mService.isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID)) {
+            // In freeform mode we set the wallpaper as its own target, so we don't need an
+            // additional window to make it visible.
+            result.setUseTopWallpaperAsTarget(true);
+        }
 
-        for (int i = windows.size() - 1; i >= 0; i--) {
-            w = windows.get(i);
+        dc.forAllWindows(w -> {
             if ((w.mAttrs.type == TYPE_WALLPAPER)) {
-                if (result.topWallpaper == null || resetTopWallpaper) {
-                    result.setTopWallpaper(w, i);
-                    resetTopWallpaper = false;
+                if (result.topWallpaper == null || result.resetTopWallpaper) {
+                    result.setTopWallpaper(w);
+                    result.resetTopWallpaper = false;
                 }
-                continue;
+                return false;
             }
-            resetTopWallpaper = true;
+
+            result.resetTopWallpaper = true;
             if (w != winAnimator.mWindowDetachedWallpaper && w.mAppToken != null) {
                 // If this window's app token is hidden and not animating,
                 // it is of no interest to us.
                 if (w.mAppToken.hidden && w.mAppToken.mAppAnimator.animation == null) {
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
                             "Skipping hidden and not animating token: " + w);
-                    continue;
+                    return false;
                 }
             }
-            if (DEBUG_WALLPAPER) Slog.v(TAG, "Win #" + i + " " + w + ": isOnScreen="
-                    + w.isOnScreen() + " mDrawState=" + w.mWinAnimator.mDrawState);
+            if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen()
+                    + " mDrawState=" + w.mWinAnimator.mDrawState);
 
-            if (!inFreeformSpace) {
-                TaskStack stack = w.getStack();
-                inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+            if (w.mWillReplaceWindow && mWallpaperTarget == null
+                    && !result.useTopWallpaperAsTarget) {
+                // When we are replacing a window and there was wallpaper before replacement, we
+                // want to keep the window until the new windows fully appear and can determine the
+                // visibility, to avoid flickering.
+                result.setUseTopWallpaperAsTarget(true);
             }
 
-            replacing |= w.mWillReplaceWindow;
-            keyguardGoingAwayWithWallpaper |= (w.mAppToken != null
+            final boolean keyguardGoingAwayWithWallpaper = (w.mAppToken != null
                     && AppTransition.isKeyguardGoingAwayTransit(
                             w.mAppToken.mAppAnimator.getTransit())
                     && (w.mAppToken.mAppAnimator.getTransitFlags()
                             & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
 
+            boolean needsShowWhenLockedWallpaper = false;
             if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
                     && mService.mPolicy.isKeyguardLocked()
                     && mService.mPolicy.isKeyguardOccluded()) {
@@ -442,248 +426,147 @@
                         || (w.mAppToken != null && !w.mAppToken.fillsParent());
             }
 
+            if (keyguardGoingAwayWithWallpaper || needsShowWhenLockedWallpaper) {
+                // Keep the wallpaper during Keyguard exit but also when it's needed for a
+                // non-fullscreen show when locked activity.
+                result.setUseTopWallpaperAsTarget(true);
+            }
+
             final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
             if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
-                if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w);
-                result.setWallpaperTarget(w, i);
+                if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
+                result.setWallpaperTarget(w);
                 if (w == mWallpaperTarget && w.mWinAnimator.isAnimationSet()) {
                     // The current wallpaper target is animating, so we'll look behind it for
                     // another possible target and figure out what is going on later.
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
                             "Win " + w + ": token animating, looking behind.");
-                    continue;
                 }
-                break;
+                // Found a target! End search.
+                return true;
             } else if (w == winAnimator.mWindowDetachedWallpaper) {
-                windowDetachedI = i;
+                if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
+                        "Found animating detached wallpaper target win: " + w);
+                result.setUseTopWallpaperAsTarget(true);
             }
-        }
+            return false;
+        }, true /* traverseTopToBottom */);
 
-        if (result.wallpaperTarget != null) {
-            return;
-        }
-
-        if (windowDetachedI >= 0) {
-            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
-                    "Found animating detached wallpaper activity: #" + windowDetachedI + "=" + w);
-            result.setWallpaperTarget(w, windowDetachedI);
-        } else if (inFreeformSpace || (replacing && mWallpaperTarget != null)) {
-            // In freeform mode we set the wallpaper as its own target, so we don't need an
-            // additional window to make it visible. When we are replacing a window and there was
-            // wallpaper before replacement, we want to keep the window until the new windows fully
-            // appear and can determine the visibility, to avoid flickering.
-            result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex);
-
-        } else if (keyguardGoingAwayWithWallpaper || needsShowWhenLockedWallpaper) {
-            // Keep the wallpaper during Keyguard exit but also when it's needed for a
-            // non-fullscreen show when locked activity.
-            result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex);
+        if (result.wallpaperTarget == null && result.useTopWallpaperAsTarget) {
+            result.setWallpaperTarget(result.topWallpaper);
         }
     }
 
     private boolean isFullscreen(WindowManager.LayoutParams attrs) {
         return attrs.x == 0 && attrs.y == 0
-                && attrs.width == WindowManager.LayoutParams.MATCH_PARENT
-                && attrs.height == WindowManager.LayoutParams.MATCH_PARENT;
+                && attrs.width == MATCH_PARENT && attrs.height == MATCH_PARENT;
     }
 
     /** Updates the target wallpaper if needed and returns true if an update happened. */
-    private boolean updateWallpaperWindowsTarget(
-            WindowList windows, FindWallpaperTargetResult result) {
+    private void updateWallpaperWindowsTarget(DisplayContent dc,
+            FindWallpaperTargetResult result) {
 
         WindowState wallpaperTarget = result.wallpaperTarget;
-        int wallpaperTargetIndex = result.wallpaperTargetIndex;
 
         if (mWallpaperTarget == wallpaperTarget
-                || (mLowerWallpaperTarget != null && mLowerWallpaperTarget == wallpaperTarget)) {
+                || (mPrevWallpaperTarget != null && mPrevWallpaperTarget == wallpaperTarget)) {
 
-            if (mLowerWallpaperTarget != null) {
-                // Is it time to stop animating?
-                if (!mLowerWallpaperTarget.isAnimatingLw()
-                        || !mUpperWallpaperTarget.isAnimatingLw()) {
-                    if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
-                            "No longer animating wallpaper targets!");
-                    mLowerWallpaperTarget = null;
-                    mUpperWallpaperTarget = null;
-                    mWallpaperTarget = wallpaperTarget;
-                    return true;
-                }
+            if (mPrevWallpaperTarget == null) {
+                return;
             }
 
-            return false;
+            // Is it time to stop animating?
+            if (!mPrevWallpaperTarget.isAnimatingLw()) {
+                if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "No longer animating wallpaper targets!");
+                mPrevWallpaperTarget = null;
+                mWallpaperTarget = wallpaperTarget;
+            }
+            return;
         }
 
         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
-                "New wallpaper target: " + wallpaperTarget + " oldTarget: " + mWallpaperTarget);
+                "New wallpaper target: " + wallpaperTarget + " prevTarget: " + mWallpaperTarget);
 
-        mLowerWallpaperTarget = null;
-        mUpperWallpaperTarget = null;
+        mPrevWallpaperTarget = null;
 
-        WindowState oldW = mWallpaperTarget;
+        final WindowState prevWallpaperTarget = mWallpaperTarget;
         mWallpaperTarget = wallpaperTarget;
 
-        if (wallpaperTarget == null || oldW == null) {
-            return true;
+        if (wallpaperTarget == null || prevWallpaperTarget == null) {
+            return;
         }
 
         // Now what is happening...  if the current and new targets are animating,
         // then we are in our super special mode!
-        boolean oldAnim = oldW.isAnimatingLw();
+        boolean oldAnim = prevWallpaperTarget.isAnimatingLw();
         boolean foundAnim = wallpaperTarget.isAnimatingLw();
         if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
                 "New animation: " + foundAnim + " old animation: " + oldAnim);
 
         if (!foundAnim || !oldAnim) {
-            return true;
+            return;
         }
 
-        int oldI = windows.indexOf(oldW);
-        if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
-                "New i: " + wallpaperTargetIndex + " old i: " + oldI);
-
-        if (oldI < 0) {
-            return true;
+        if (dc.getWindow(w -> w == prevWallpaperTarget) == null) {
+            return;
         }
 
         final boolean newTargetHidden = wallpaperTarget.mAppToken != null
                 && wallpaperTarget.mAppToken.hiddenRequested;
-        final boolean oldTargetHidden = oldW.mAppToken != null
-                && oldW.mAppToken.hiddenRequested;
+        final boolean oldTargetHidden = prevWallpaperTarget.mAppToken != null
+                && prevWallpaperTarget.mAppToken.hiddenRequested;
 
-        if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old#" + oldI + "="
-                + oldW + " hidden=" + oldTargetHidden + " new#" + wallpaperTargetIndex + "="
-                + wallpaperTarget + " hidden=" + newTargetHidden);
+        if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old: "
+                + prevWallpaperTarget + " hidden=" + oldTargetHidden + " new: " + wallpaperTarget
+                + " hidden=" + newTargetHidden);
 
-        // Set the upper and lower wallpaper targets correctly,
-        // and make sure that we are positioning the wallpaper below the lower.
-        if (wallpaperTargetIndex > oldI) {
-            // The new target is on top of the old one.
-            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Found target above old target.");
-            mUpperWallpaperTarget = wallpaperTarget;
-            mLowerWallpaperTarget = oldW;
-
-            wallpaperTarget = oldW;
-            wallpaperTargetIndex = oldI;
-        } else {
-            // The new target is below the old one.
-            if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Found target below old target.");
-            mUpperWallpaperTarget = oldW;
-            mLowerWallpaperTarget = wallpaperTarget;
-        }
+        mPrevWallpaperTarget = prevWallpaperTarget;
 
         if (newTargetHidden && !oldTargetHidden) {
             if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Old wallpaper still the target.");
             // Use the old target if new target is hidden but old target
             // is not. If they're both hidden, still use the new target.
-            mWallpaperTarget = oldW;
+            mWallpaperTarget = prevWallpaperTarget;
         } else if (newTargetHidden == oldTargetHidden
                 && !mService.mOpeningApps.contains(wallpaperTarget.mAppToken)
-                && (mService.mOpeningApps.contains(oldW.mAppToken)
-                || mService.mClosingApps.contains(oldW.mAppToken))) {
+                && (mService.mOpeningApps.contains(prevWallpaperTarget.mAppToken)
+                || mService.mClosingApps.contains(prevWallpaperTarget.mAppToken))) {
             // If they're both hidden (or both not hidden), prefer the one that's currently in
             // opening or closing app list, this allows transition selection logic to better
             // determine the wallpaper status of opening/closing apps.
-            mWallpaperTarget = oldW;
+            mWallpaperTarget = prevWallpaperTarget;
         }
 
-        result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex);
-        return true;
+        result.setWallpaperTarget(wallpaperTarget);
     }
 
-    private boolean updateWallpaperWindowsTargetByLayer(WindowList windows,
-            FindWallpaperTargetResult result) {
-
-        WindowState wallpaperTarget = result.wallpaperTarget;
-        int wallpaperTargetIndex = result.wallpaperTargetIndex;
-        boolean visible = wallpaperTarget != null;
-
-        if (visible) {
-            // The window is visible to the compositor...but is it visible to the user?
-            // That is what the wallpaper cares about.
-            visible = isWallpaperVisible(wallpaperTarget);
-            if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
-
-            // If the wallpaper target is animating, we may need to copy its layer adjustment.
-            // Only do this if we are not transferring between two wallpaper targets.
-            mWallpaperAnimLayerAdjustment =
-                    (mLowerWallpaperTarget == null && wallpaperTarget.mAppToken != null)
-                            ? wallpaperTarget.mAppToken.mAppAnimator.animLayerAdjustment : 0;
-
-            final int maxLayer = (mService.mPolicy.getMaxWallpaperLayer() * TYPE_LAYER_MULTIPLIER)
-                    + TYPE_LAYER_OFFSET;
-
-            // Now w is the window we are supposed to be behind...  but we
-            // need to be sure to also be behind any of its attached windows,
-            // AND any starting window associated with it, AND below the
-            // maximum layer the policy allows for wallpapers.
-            while (wallpaperTargetIndex > 0) {
-                final WindowState wb = windows.get(wallpaperTargetIndex - 1);
-                final WindowState wbParentWindow = wb.getParentWindow();
-                final WindowState wallpaperParentWindow = wallpaperTarget.getParentWindow();
-                if (wb.mBaseLayer < maxLayer
-                        && wbParentWindow != wallpaperTarget
-                        && (wallpaperParentWindow == null || wbParentWindow != wallpaperParentWindow)
-                        && (wb.mAttrs.type != TYPE_APPLICATION_STARTING
-                                || wallpaperTarget.mToken == null
-                                || wb.mToken != wallpaperTarget.mToken)) {
-                    // This window is not related to the previous one in any
-                    // interesting way, so stop here.
-                    break;
-                }
-                wallpaperTarget = wb;
-                wallpaperTargetIndex--;
-            }
-        } else {
-            if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target");
-        }
-
-        result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex);
-        return visible;
-    }
-
-    private boolean updateWallpaperWindowsPlacement(WindowList windows,
-            WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible) {
-
-        // TODO(multidisplay): Wallpapers on main screen only.
-        final DisplayInfo displayInfo = mService.getDefaultDisplayContentLocked().getDisplayInfo();
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
-
-        // Start stepping backwards from here, ensuring that our wallpaper windows are correctly placed.
-        boolean changed = false;
+    private void updateWallpaperTokens(boolean visible) {
         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
-            changed |= token.updateWallpaperWindowsPlacement(windows, wallpaperTarget,
-                    wallpaperTargetIndex, visible, dw, dh, mWallpaperAnimLayerAdjustment);
+            token.updateWallpaperWindows(visible, mWallpaperAnimLayerAdjustment);
         }
-
-        return changed;
     }
 
-    boolean adjustWallpaperWindows(WindowList windows) {
+    void adjustWallpaperWindows(DisplayContent dc) {
         mService.mRoot.mWallpaperMayChange = false;
 
         // First find top-most window that has asked to be on top of the wallpaper;
         // all wallpapers go behind it.
-        findWallpaperTarget(windows, mFindResults);
-        final boolean targetChanged = updateWallpaperWindowsTarget(windows, mFindResults);
-        final boolean visible = updateWallpaperWindowsTargetByLayer(windows, mFindResults);
-        WindowState wallpaperTarget = mFindResults.wallpaperTarget;
-        int wallpaperTargetIndex = mFindResults.wallpaperTargetIndex;
+        findWallpaperTarget(dc, mFindResults);
+        updateWallpaperWindowsTarget(dc, mFindResults);
 
-        if (wallpaperTarget == null && mFindResults.topWallpaper != null) {
-            // There is no wallpaper target, so it goes at the bottom.
-            // We will assume it is the same place as last time, if known.
-            wallpaperTarget = mFindResults.topWallpaper;
-            wallpaperTargetIndex = mFindResults.topWallpaperIndex + 1;
-        } else {
-            // Okay i is the position immediately above the wallpaper.
-            // Look at what is below it for later.
-            wallpaperTarget = wallpaperTargetIndex > 0
-                    ? windows.get(wallpaperTargetIndex - 1) : null;
-        }
+        // The window is visible to the compositor...but is it visible to the user?
+        // That is what the wallpaper cares about.
+        final boolean visible = mWallpaperTarget != null && isWallpaperVisible(mWallpaperTarget);
+        if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible);
 
         if (visible) {
+            // If the wallpaper target is animating, we may need to copy its layer adjustment.
+            // Only do this if we are not transferring between two wallpaper targets.
+            mWallpaperAnimLayerAdjustment =
+                    (mPrevWallpaperTarget == null && mWallpaperTarget.mAppToken != null)
+                            ? mWallpaperTarget.mAppToken.mAppAnimator.animLayerAdjustment : 0;
+
             if (mWallpaperTarget.mWallpaperX >= 0) {
                 mLastWallpaperX = mWallpaperTarget.mWallpaperX;
                 mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep;
@@ -700,14 +583,10 @@
             }
         }
 
-        final boolean changed = updateWallpaperWindowsPlacement(
-                windows, wallpaperTarget, wallpaperTargetIndex, visible);
+        updateWallpaperTokens(visible);
 
-        if (targetChanged && DEBUG_WALLPAPER_LIGHT)  Slog.d(TAG, "New wallpaper: target="
-                + mWallpaperTarget + " lower=" + mLowerWallpaperTarget + " upper="
-                + mUpperWallpaperTarget);
-
-        return changed;
+        if (DEBUG_WALLPAPER_LIGHT)  Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
+                + " prev=" + mPrevWallpaperTarget);
     }
 
     boolean processWallpaperDrawPendingTimeout() {
@@ -773,7 +652,7 @@
         }
 
         if (adjust) {
-            dc.adjustWallpaperWindows();
+            adjustWallpaperWindows(dc);
         }
     }
 
@@ -787,9 +666,8 @@
 
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mWallpaperTarget="); pw.println(mWallpaperTarget);
-        if (mLowerWallpaperTarget != null || mUpperWallpaperTarget != null) {
-            pw.print(prefix); pw.print("mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget);
-            pw.print(prefix); pw.print("mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget);
+        if (mPrevWallpaperTarget != null) {
+            pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
         }
         pw.print(prefix); pw.print("mLastWallpaperX="); pw.print(mLastWallpaperX);
         pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
@@ -825,26 +703,28 @@
 
     /** Helper class for storing the results of a wallpaper target find operation. */
     final private static class FindWallpaperTargetResult {
-        int topWallpaperIndex = 0;
         WindowState topWallpaper = null;
-        int wallpaperTargetIndex = 0;
+        boolean useTopWallpaperAsTarget = false;
         WindowState wallpaperTarget = null;
+        boolean resetTopWallpaper = false;
 
-        void setTopWallpaper(WindowState win, int index) {
+        void setTopWallpaper(WindowState win) {
             topWallpaper = win;
-            topWallpaperIndex = index;
         }
 
-        void setWallpaperTarget(WindowState win, int index) {
+        void setWallpaperTarget(WindowState win) {
             wallpaperTarget = win;
-            wallpaperTargetIndex = index;
+        }
+
+        void setUseTopWallpaperAsTarget(boolean topWallpaperAsTarget) {
+            useTopWallpaperAsTarget = topWallpaperAsTarget;
         }
 
         void reset() {
-            topWallpaperIndex = 0;
             topWallpaper = null;
-            wallpaperTargetIndex = 0;
             wallpaperTarget = null;
+            useTopWallpaperAsTarget = false;
+            resetTopWallpaper = false;
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 3a76cd4..8ea1b3b 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -119,11 +119,8 @@
         }
     }
 
-    boolean updateWallpaperWindowsPlacement(WindowList windowList,
-            WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible, int dw, int dh,
-            int wallpaperAnimLayerAdj) {
+    void updateWallpaperWindows(boolean visible, int animLayerAdj) {
 
-        boolean changed = false;
         if (hidden == visible) {
             if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
                     "Wallpaper token " + token + " hidden=" + !visible);
@@ -132,6 +129,9 @@
             mDisplayContent.setLayoutNeeded();
         }
 
+        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+        final int dw = displayInfo.logicalWidth;
+        final int dh = displayInfo.logicalHeight;
         final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
             final WindowState wallpaper = mChildren.get(wallpaperNdx);
@@ -142,66 +142,11 @@
 
             // First, make sure the client has the current visibility state.
             wallpaper.dispatchWallpaperVisibility(visible);
-            wallpaper.adjustAnimLayer(wallpaperAnimLayerAdj);
+            wallpaper.adjustAnimLayer(animLayerAdj);
 
             if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win "
                     + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer);
-
-            // First, if this window is at the current index, then all is well.
-            if (wallpaper == wallpaperTarget) {
-                wallpaperTargetIndex--;
-                wallpaperTarget = wallpaperTargetIndex > 0
-                        ? windowList.get(wallpaperTargetIndex - 1) : null;
-                continue;
-            }
-
-            // The window didn't match...  the current wallpaper window,
-            // wherever it is, is in the wrong place, so make sure it is not in the list.
-            int oldIndex = windowList.indexOf(wallpaper);
-            if (oldIndex >= 0) {
-                if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG,
-                        "Wallpaper removing at " + oldIndex + ": " + wallpaper);
-                mDisplayContent.removeFromWindowList(wallpaper);
-                if (oldIndex < wallpaperTargetIndex) {
-                    wallpaperTargetIndex--;
-                }
-            }
-
-            // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost
-            // layer. For keyguard over wallpaper put the wallpaper under the lowest window that
-            // is currently on screen, i.e. not hidden by policy.
-            int insertionIndex = 0;
-            if (visible && wallpaperTarget != null) {
-                final int privateFlags = wallpaperTarget.mAttrs.privateFlags;
-                if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
-                    insertionIndex = Math.min(windowList.indexOf(wallpaperTarget),
-                            findLowestWindowOnScreen(windowList));
-                }
-            }
-            if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT
-                    || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
-                    "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + insertionIndex);
-
-            mDisplayContent.addToWindowList(wallpaper, insertionIndex);
-            changed = true;
         }
-
-        return changed;
-    }
-
-    /**
-     * @return The index in {@param windows} of the lowest window that is currently on screen and
-     *         not hidden by the policy.
-     */
-    private int findLowestWindowOnScreen(WindowList windowList) {
-        final int size = windowList.size();
-        for (int index = 0; index < size; index++) {
-            final WindowState win = windowList.get(index);
-            if (win.isOnScreen()) {
-                return index;
-            }
-        }
-        return Integer.MAX_VALUE;
     }
 
     boolean hasVisibleNotDrawnWallpaper() {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 150160c..a6a907c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -19,10 +19,12 @@
 import android.annotation.CallSuper;
 import android.content.res.Configuration;
 import android.view.animation.Animation;
+import com.android.internal.util.ToBooleanFunction;
 
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
@@ -496,17 +498,38 @@
         return addIndex;
     }
 
-    void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
+    /**
+     * For all windows at or below this container call the callback.
+     * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
+     *                   stops the search if {@link ToBooleanFunction#apply} returns true.
+     * @param   traverseTopToBottom If true traverses the hierarchy from top-to-bottom in terms of
+     *                              z-order, else from bottom-to-top.
+     * @return  True if the search ended before we reached the end of the hierarchy due to
+     *          {@link Function#apply} returning true.
+     */
+    boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
         if (traverseTopToBottom) {
             for (int i = mChildren.size() - 1; i >= 0; --i) {
-                mChildren.get(i).forAllWindows(callback, traverseTopToBottom);
+                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
+                    return true;
+                }
             }
         } else {
             final int count = mChildren.size();
             for (int i = 0; i < count; i++) {
-                mChildren.get(i).forAllWindows(callback, traverseTopToBottom);
+                if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
+                    return true;
+                }
             }
         }
+        return false;
+    }
+
+    void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
+        forAllWindows(w -> {
+            callback.accept(w);
+            return false;
+        }, traverseTopToBottom);
     }
 
     WindowState getWindow(Predicate<WindowState> callback) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 518e55b..9648282 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4905,9 +4905,9 @@
             }
 
             if (rotateSeamlessly) {
-                dc.forAllWindows((w) ->
-                        w.mWinAnimator.seamlesslyRotateWindow(oldRotation, mRotation),
-                        true /* traverseTopToBottom */);
+                dc.forAllWindows(w -> {
+                        w.mWinAnimator.seamlesslyRotateWindow(oldRotation, mRotation);
+                }, true /* traverseTopToBottom */);
             }
 
             mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
@@ -4920,7 +4920,7 @@
             }
         }
 
-        dc.forAllWindows((w) -> {
+        dc.forAllWindows(w -> {
             // Discard surface after orientation change, these can't be reused.
             if (w.mAppToken != null) {
                 w.mAppToken.destroySavedSurfaces();
@@ -5183,7 +5183,9 @@
 
         final WindowList windows = new WindowList();
         synchronized (mWindowMap) {
-            mRoot.forAllWindows(windows::add, false /* traverseTopToBottom */);
+            mRoot.forAllWindows(w -> {
+                windows.add(w);
+            }, false /* traverseTopToBottom */);
         }
 
         BufferedWriter out = null;
@@ -8260,7 +8262,7 @@
                     mRoot.dumpChildrenNames(output, " ");
                     pw.println(output.toString());
                     pw.println(" ");
-                    mRoot.forAllWindows(pw::println, true /* traverseTopToBottom */);
+                    mRoot.forAllWindows(w -> {pw.println(w);}, true /* traverseTopToBottom */);
                 }
                 return;
             } else {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5e65aec..c0b3f27 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -53,6 +53,7 @@
 import android.view.WindowManager;
 import android.view.WindowManagerPolicy;
 
+import com.android.internal.util.ToBooleanFunction;
 import com.android.server.input.InputWindowHandle;
 
 import java.io.PrintWriter;
@@ -60,6 +61,7 @@
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 import static android.app.ActivityManager.StackId;
@@ -3833,21 +3835,20 @@
     }
 
     @Override
-    void forAllWindows(Consumer<WindowState> callback, boolean traverseTopToBottom) {
+    boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
         if (mChildren.isEmpty()) {
             // The window has no children so we just return it.
-            callback.accept(this);
-            return;
+            return callback.apply(this);
         }
 
         if (traverseTopToBottom) {
-            forAllWindowTopToBottom(callback);
+            return forAllWindowTopToBottom(callback);
         } else {
-            forAllWindowBottomToTop(callback);
+            return forAllWindowBottomToTop(callback);
         }
     }
 
-    private void forAllWindowBottomToTop(Consumer<WindowState> callback) {
+    private boolean forAllWindowBottomToTop(ToBooleanFunction<WindowState> callback) {
         // We want to consumer the negative sublayer children first because they need to appear
         // below the parent, then this window (the parent), and then the positive sublayer children
         // because they need to appear above the parent.
@@ -3856,7 +3857,9 @@
         WindowState child = mChildren.get(i);
 
         while (i < count && child.mSubLayer < 0) {
-            callback.accept(child);
+            if (callback.apply(child)) {
+                return true;
+            }
             i++;
             if (i >= count) {
                 break;
@@ -3864,19 +3867,25 @@
             child = mChildren.get(i);
         }
 
-        callback.accept(this);
+        if (callback.apply(this)) {
+            return true;
+        }
 
         while (i < count) {
-            callback.accept(child);
+            if (callback.apply(child)) {
+                return true;
+            }
             i++;
             if (i >= count) {
                 break;
             }
             child = mChildren.get(i);
         }
+
+        return false;
     }
 
-    private void forAllWindowTopToBottom(Consumer<WindowState> callback) {
+    private boolean forAllWindowTopToBottom(ToBooleanFunction<WindowState> callback) {
         // We want to consumer the positive sublayer children first because they need to appear
         // above the parent, then this window (the parent), and then the negative sublayer children
         // because they need to appear above the parent.
@@ -3884,7 +3893,9 @@
         WindowState child = mChildren.get(i);
 
         while (i >= 0 && child.mSubLayer >= 0) {
-            callback.accept(child);
+            if (callback.apply(child)) {
+                return true;
+            }
             --i;
             if (i < 0) {
                 break;
@@ -3892,16 +3903,22 @@
             child = mChildren.get(i);
         }
 
-        callback.accept(this);
+        if (callback.apply(this)) {
+            return true;
+        }
 
         while (i >= 0) {
-            callback.accept(child);
+            if (callback.apply(child)) {
+                return true;
+            }
             --i;
             if (i < 0) {
                 break;
             }
             child = mChildren.get(i);
         }
+
+        return false;
     }
 
     WindowState getWindow(Predicate<WindowState> callback) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index f2682ba..7e1880f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -266,21 +266,9 @@
         mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(displayContent,
                 mService.mOpeningApps);
 
-        final WindowState lowerWallpaperTarget =
-                mWallpaperControllerLocked.getLowerWallpaperTarget();
-        final WindowState upperWallpaperTarget =
-                mWallpaperControllerLocked.getUpperWallpaperTarget();
-
+        final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
         boolean openingAppHasWallpaper = false;
         boolean closingAppHasWallpaper = false;
-        final AppWindowToken lowerWallpaperAppToken;
-        final AppWindowToken upperWallpaperAppToken;
-        if (lowerWallpaperTarget == null) {
-            lowerWallpaperAppToken = upperWallpaperAppToken = null;
-        } else {
-            lowerWallpaperAppToken = lowerWallpaperTarget.mAppToken;
-            upperWallpaperAppToken = upperWallpaperTarget.mAppToken;
-        }
 
         // Do a first pass through the tokens for two things:
         // (1) Determine if both the closing and opening app token sets are wallpaper targets, in
@@ -294,12 +282,12 @@
             final AppWindowToken wtoken;
             if (i < closingAppsCount) {
                 wtoken = mService.mClosingApps.valueAt(i);
-                if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
+                if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
                     closingAppHasWallpaper = true;
                 }
             } else {
                 wtoken = mService.mOpeningApps.valueAt(i - closingAppsCount);
-                if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) {
+                if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
                     openingAppHasWallpaper = true;
                 }
             }
@@ -307,14 +295,14 @@
             voiceInteraction |= wtoken.voiceInteraction;
 
             if (wtoken.fillsParent()) {
-                WindowState ws = wtoken.findMainWindow();
+                final WindowState ws = wtoken.findMainWindow();
                 if (ws != null) {
                     animLp = ws.mAttrs;
                     bestAnimLayer = ws.mLayer;
                     fullscreenAnim = true;
                 }
             } else if (!fullscreenAnim) {
-                WindowState ws = wtoken.findMainWindow();
+                final WindowState ws = wtoken.findMainWindow();
                 if (ws != null) {
                     if (ws.mLayer > bestAnimLayer) {
                         animLp = ws.mAttrs;
@@ -325,7 +313,7 @@
         }
 
         transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
-                closingAppHasWallpaper, lowerWallpaperTarget, upperWallpaperTarget);
+                closingAppHasWallpaper);
 
         // If all closing windows are obscured, then there is no need to do an animation. This is
         // the case, for example, when this transition is being done behind the lock screen.
@@ -578,8 +566,7 @@
     }
 
     private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
-            boolean closingAppHasWallpaper, WindowState lowerWallpaperTarget,
-            WindowState upperWallpaperTarget) {
+            boolean closingAppHasWallpaper) {
         // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper
         final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
         final WindowState oldWallpaper = mWallpaperControllerLocked.isWallpaperTargetAnimating()
@@ -590,8 +577,6 @@
         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                 "New wallpaper target=" + wallpaperTarget
                         + ", oldWallpaper=" + oldWallpaper
-                        + ", lower target=" + lowerWallpaperTarget
-                        + ", upper target=" + upperWallpaperTarget
                         + ", openingApps=" + openingApps
                         + ", closingApps=" + closingApps);
         mService.mAnimateWallpaperWithTarget = false;
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 0533efc..225dc5d2 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -98,7 +98,7 @@
         final ArrayList<WindowState> windows = new ArrayList();
 
         // Test forward traversal.
-        dc.forAllWindows(windows::add, false /* traverseTopToBottom */);
+        dc.forAllWindows(w -> {windows.add(w);}, false /* traverseTopToBottom */);
 
         assertEquals(wallpaperWindow, windows.get(0));
         assertEquals(exitingAppWindow, windows.get(1));
@@ -112,7 +112,7 @@
 
         // Test backward traversal.
         windows.clear();
-        dc.forAllWindows(windows::add, true /* traverseTopToBottom */);
+        dc.forAllWindows(w -> {windows.add(w);}, true /* traverseTopToBottom */);
 
         assertEquals(wallpaperWindow, windows.get(8));
         assertEquals(exitingAppWindow, windows.get(7));
diff --git a/telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl b/telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl
index 5b71149..2ae424f 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl
+++ b/telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl
@@ -89,4 +89,11 @@
      */
     void listCapInfoReceived(in PresRlmiInfo rlmiInfo,
                              in PresResInfo [] resInfo);
+
+    /**
+     * Callback function to be invoked to inform the client when Unpublish message
+     * is sent to network.
+     */
+    void unpublishMessageSent();
+
 }
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
index c363055..dd79998 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
index 7b58539..f274dbf 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ