Merge "[WIFI][SysUI] Update QS iconography for Wi-Fi" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 7f6a7c1..4a1f96e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -472,7 +472,7 @@
         "framework-sdkextensions-stubs-systemapi",
         "framework-statsd-stubs-module_libs_api",
         "framework-permission-stubs-systemapi",
-        "framework-wifi-stubs",
+        "framework-wifi-stubs-systemapi",
         "framework-tethering-stubs",
     ],
     installable: true,
@@ -496,7 +496,6 @@
         "//frameworks/base/apex/appsearch/framework",
         "//frameworks/base/apex/blobstore/framework",
         "//frameworks/base/apex/jobscheduler/framework",
-        "//frameworks/base/apex/statsd/service",
         "//frameworks/base/packages/Tethering/tests/unit",
     ],
 }
@@ -522,7 +521,7 @@
         "framework-permission-stubs-systemapi",
         "framework-sdkextensions-stubs-systemapi",
         "framework-statsd-stubs-module_libs_api",
-        "framework-wifi-stubs",
+        "framework-wifi-stubs-systemapi",
         "framework-tethering-stubs",
         // TODO (b/147688669) should be framework-telephony-stubs
         "framework-telephony",
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
index d7428cf..761e930 100644
--- a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
@@ -31,6 +31,7 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -58,6 +59,12 @@
         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
     }
 
+    @Before
+    public void setup() {
+        PackageManager.disableApplicationInfoCache();
+        PackageManager.disablePackageInfoCache();
+    }
+
     @Test
     @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
     public void testCheckPermissionExists() {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index a4ab31d..7a1b4f2 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1844,7 +1844,7 @@
                     break;
 
                 case MSG_REPORT_SYNC_SCHEDULED:
-                    final boolean exempted = msg.arg1 > 0 ? true : false;
+                    final boolean exempted = msg.arg2 > 0 ? true : false;
                     if (exempted) {
                         reportExemptedSyncScheduled((String) msg.obj, msg.arg1);
                     } else {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 11d3a68..566f4cd 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -1114,20 +1114,22 @@
     static {
         // Using a LinkedHashMap to keep the insertion order when iterating over the keys.
         LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>();
-        extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new);
-        extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new);
-        extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new);
-        extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new);
-        extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new);
-        extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new);
-        extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new);
+        // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering,
+        // which in turn aims to minimize the chances of incorrect extractor selections.
         extractorFactoriesByName.put("exo.MatroskaParser", MatroskaExtractor::new);
-        extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new);
+        extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new);
         extractorFactoriesByName.put("exo.Mp4Parser", Mp4Extractor::new);
+        extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new);
+        extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new);
+        extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new);
+        extractorFactoriesByName.put("exo.TsParser", TsExtractor::new);
+        extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new);
         extractorFactoriesByName.put("exo.OggParser", OggExtractor::new);
         extractorFactoriesByName.put("exo.PsParser", PsExtractor::new);
-        extractorFactoriesByName.put("exo.TsParser", TsExtractor::new);
         extractorFactoriesByName.put("exo.WavParser", WavExtractor::new);
+        extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new);
+        extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new);
+        extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new);
         EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName);
 
         HashMap<String, Class> expectedTypeByParameterName = new HashMap<>();
diff --git a/api/current.txt b/api/current.txt
index c8a7956..a2f2c06 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5943,7 +5943,7 @@
     method public boolean isImportantConversation();
     method public void setAllowBubbles(boolean);
     method public void setBypassDnd(boolean);
-    method public void setConversationId(@Nullable String, @Nullable String);
+    method public void setConversationId(@NonNull String, @NonNull String);
     method public void setDescription(String);
     method public void setGroup(String);
     method public void setImportance(int);
@@ -5956,7 +5956,6 @@
     method public boolean shouldShowLights();
     method public boolean shouldVibrate();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
     field @NonNull public static final android.os.Parcelable.Creator<android.app.NotificationChannel> CREATOR;
     field public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
   }
@@ -8190,6 +8189,7 @@
     field public static final String OPTION_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
     field public static final String OPTION_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
     field public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
+    field public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
   }
 
   public class AppWidgetProvider extends android.content.BroadcastReceiver {
@@ -29357,9 +29357,9 @@
   public abstract class TvInputService extends android.app.Service {
     ctor public TvInputService();
     method public final android.os.IBinder onBind(android.content.Intent);
-    method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(String);
+    method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String);
     method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String);
-    method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(String);
+    method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(@NonNull String);
     method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String);
     field public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; // 0x64
     field public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; // 0x190
@@ -48147,6 +48147,7 @@
     method public String getMmsUserAgent();
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNai();
     method public String getNetworkCountryIso();
+    method @NonNull public String getNetworkCountryIso(int);
     method public String getNetworkOperator();
     method public String getNetworkOperatorName();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
diff --git a/api/system-current.txt b/api/system-current.txt
index 85ad66f..3b95f32 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7199,9 +7199,9 @@
   }
 
   public final class SoftApCapability implements android.os.Parcelable {
+    method public boolean areFeaturesSupported(long);
     method public int describeContents();
     method public int getMaxSupportedClients();
-    method public boolean isFeatureSupported(long);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR;
     field public static final long SOFTAP_FEATURE_ACS_OFFLOAD = 1L; // 0x1L
@@ -11738,7 +11738,6 @@
     method public int getMaxNumberOfSimultaneouslyActiveSims();
     method public static long getMaxNumberVerificationTimeoutMillis();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup();
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
     method public int getSimApplicationState();
diff --git a/api/test-current.txt b/api/test-current.txt
index 2d15c0e..0f8694f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -118,7 +118,6 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePinnedStack(int, android.graphics.Rect, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
@@ -413,6 +412,12 @@
     field public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri";
   }
 
+  public class DreamManager {
+    method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void setActiveDream(@NonNull android.content.ComponentName);
+    method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void startDream(@NonNull android.content.ComponentName);
+    method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void stopDream();
+  }
+
   public final class NotificationChannel implements android.os.Parcelable {
     method public int getOriginalImportance();
     method public boolean isBlockableSystem();
@@ -792,6 +797,7 @@
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
     field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
+    field public static final String DREAM_SERVICE = "dream";
     field public static final String ETHERNET_SERVICE = "ethernet";
     field public static final String NETWORK_STACK_SERVICE = "network_stack";
     field public static final String PERMISSION_SERVICE = "permission";
@@ -975,6 +981,14 @@
     field @Nullable public final String backgroundPermission;
   }
 
+  public final class ProviderInfoList implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public static android.content.pm.ProviderInfoList fromList(@NonNull java.util.List<android.content.pm.ProviderInfo>);
+    method @NonNull public java.util.List<android.content.pm.ProviderInfo> getList();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR;
+  }
+
   public final class ShortcutInfo implements android.os.Parcelable {
     method public boolean isVisibleToPublisher();
   }
@@ -2463,7 +2477,9 @@
   }
 
   public final class Parcel {
+    method public boolean allowSquashing();
     method public int readExceptionCode();
+    method public void restoreAllowSquashing(boolean);
   }
 
   public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -3753,7 +3769,6 @@
     method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
     method public int getEmergencyNumberDbVersion();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
-    method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int);
     method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
     method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 95de6c5..1e200c5 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -146,6 +146,8 @@
     struct uhid_event ev = {};
     ev.type = UHID_CREATE2;
     strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
+    std::string uniq = android::base::StringPrintf("Id: %d", id);
+    strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq.c_str(), sizeof(ev.u.create2.uniq));
     memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0]));
     ev.u.create2.rd_size = size;
     ev.u.create2.bus = bus;
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 2229e1c..d79123b 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -20,9 +20,9 @@
 
 #include <dirent.h>
 #include <errno.h>
-
 #include <mutex>
 #include <set>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
@@ -42,6 +42,7 @@
 #include "frameworks/base/core/proto/android/os/backtrace.proto.h"
 #include "frameworks/base/core/proto/android/os/data.proto.h"
 #include "frameworks/base/core/proto/android/util/log.proto.h"
+#include "frameworks/base/core/proto/android/util/textdump.proto.h"
 #include "incidentd_util.h"
 
 namespace android {
@@ -135,7 +136,7 @@
     status_t ihStatus = wait_child(pid);
     if (ihStatus != NO_ERROR) {
         ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-ihStatus));
-        return ihStatus;
+        return OK; // Not a fatal error.
     }
 
     return writer->writeSection(buffer);
@@ -234,7 +235,7 @@
     Fpipe pipe;
 
     // Lock protects these fields
-    mutex lock;
+    std::mutex lock;
     bool workerDone;
     status_t workerError;
 
@@ -261,83 +262,47 @@
     }
 }
 
-static void* worker_thread_func(void* cookie) {
-    // Don't crash the service if we write to a closed pipe (which can happen if
-    // dumping times out).
-    signal(SIGPIPE, sigpipe_handler);
-
-    WorkerThreadData* data = (WorkerThreadData*)cookie;
-    status_t err = data->section->BlockingCall(data->pipe.writeFd());
-
-    {
-        unique_lock<mutex> lock(data->lock);
-        data->workerDone = true;
-        data->workerError = err;
-    }
-
-    data->pipe.writeFd().reset();
-    data->decStrong(data->section);
-    // data might be gone now. don't use it after this point in this thread.
-    return NULL;
-}
-
 status_t WorkerThreadSection::Execute(ReportWriter* writer) const {
     status_t err = NO_ERROR;
-    pthread_t thread;
-    pthread_attr_t attr;
     bool workerDone = false;
     FdBuffer buffer;
 
-    // Data shared between this thread and the worker thread.
-    sp<WorkerThreadData> data = new WorkerThreadData(this);
-
-    // Create the pipe
-    if (!data->pipe.init()) {
+    // Create shared data and pipe
+    WorkerThreadData data(this);
+    if (!data.pipe.init()) {
         return -errno;
     }
 
-    // Create the thread
-    err = pthread_attr_init(&attr);
-    if (err != 0) {
-        return -err;
-    }
-    // TODO: Do we need to tweak thread priority?
-    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-    if (err != 0) {
-        pthread_attr_destroy(&attr);
-        return -err;
-    }
-
-    // The worker thread needs a reference and we can't let the count go to zero
-    // if that thread is slow to start.
-    data->incStrong(this);
-
-    err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
-    pthread_attr_destroy(&attr);
-    if (err != 0) {
-        data->decStrong(this);
-        return -err;
-    }
+    std::thread([&]() {
+        // Don't crash the service if writing to a closed pipe (may happen if dumping times out)
+        signal(SIGPIPE, sigpipe_handler);
+        status_t err = data.section->BlockingCall(data.pipe.writeFd());
+        {
+            std::unique_lock<std::mutex> lock(data.lock);
+            data.workerDone = true;
+            data.workerError = err;
+            // unique_fd is not thread safe. If we don't lock it, reset() may pause half way while
+            // the other thread executes to the end, calling ~Fpipe, which is a race condition.
+            data.pipe.writeFd().reset();
+        }
+    }).detach();
 
     // Loop reading until either the timeout or the worker side is done (i.e. eof).
-    err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
+    err = buffer.read(data.pipe.readFd().get(), this->timeoutMs);
     if (err != NO_ERROR) {
         ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
     }
 
-    // Done with the read fd. The worker thread closes the write one so
-    // we never race and get here first.
-    data->pipe.readFd().reset();
-
     // If the worker side is finished, then return its error (which may overwrite
     // our possible error -- but it's more interesting anyway). If not, then we timed out.
     {
-        unique_lock<mutex> lock(data->lock);
-        if (data->workerError != NO_ERROR) {
-            err = data->workerError;
+        std::unique_lock<std::mutex> lock(data.lock);
+        data.pipe.close();
+        if (data.workerError != NO_ERROR) {
+            err = data.workerError;
             ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
         }
-        workerDone = data->workerDone;
+        workerDone = data.workerDone;
     }
 
     writer->setSectionStats(buffer);
@@ -473,6 +438,77 @@
 }
 
 // ================================================================================
+TextDumpsysSection::TextDumpsysSection(int id, const char* service, ...)
+    : WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS), mService(service) {
+    name = "dumpsys ";
+    name += service;
+
+    va_list args;
+    va_start(args, service);
+    while (true) {
+        const char* arg = va_arg(args, const char*);
+        if (arg == NULL) {
+            break;
+        }
+        mArgs.add(String16(arg));
+        name += " ";
+        name += arg;
+    }
+    va_end(args);
+}
+
+TextDumpsysSection::~TextDumpsysSection() {}
+
+status_t TextDumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const {
+    // checkService won't wait for the service to show up like getService will.
+    sp<IBinder> service = defaultServiceManager()->checkService(mService);
+    if (service == NULL) {
+        ALOGW("TextDumpsysSection: Can't lookup service: %s", String8(mService).string());
+        return NAME_NOT_FOUND;
+    }
+
+    // Create pipe
+    Fpipe dumpPipe;
+    if (!dumpPipe.init()) {
+        ALOGW("[%s] failed to setup pipe", this->name.string());
+        return -errno;
+    }
+
+    // Run dumping thread
+    const uint64_t start = Nanotime();
+    std::thread worker([&]() {
+        // Don't crash the service if writing to a closed pipe (may happen if dumping times out)
+        signal(SIGPIPE, sigpipe_handler);
+        status_t err = service->dump(dumpPipe.writeFd().get(), mArgs);
+        if (err != OK) {
+            ALOGW("[%s] dump thread failed. Error: %s", this->name.string(), strerror(-err));
+        }
+        dumpPipe.writeFd().reset();
+    });
+
+    // Collect dump content
+    std::string content;
+    bool success = ReadFdToString(dumpPipe.readFd(), &content);
+    worker.join(); // Wait for worker to finish
+    dumpPipe.readFd().reset();
+    if (!success) {
+        ALOGW("[%s] failed to read data from pipe", this->name.string());
+        return -1;
+    }
+
+    ProtoOutputStream proto;
+    proto.write(util::TextDumpProto::COMMAND, std::string(name.string()));
+    proto.write(util::TextDumpProto::CONTENT, content);
+    proto.write(util::TextDumpProto::DUMP_DURATION_NS, int64_t(Nanotime() - start));
+
+    if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
+        ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+        return EPIPE;
+    }
+    return OK;
+}
+
+// ================================================================================
 // initialization only once in Section.cpp.
 map<log_id_t, log_time> LogSection::gLastLogsRetrieved;
 
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 0bb9da9..6162b3a 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -112,7 +112,8 @@
 };
 
 /**
- * Section that calls dumpsys on a system service.
+ * Section that calls protobuf dumpsys on a system service, usually
+ * "dumpsys [service_name] --proto".
  */
 class DumpsysSection : public WorkerThreadSection {
 public:
@@ -127,6 +128,21 @@
 };
 
 /**
+ * Section that calls text dumpsys on a system service, usually "dumpsys [service_name]".
+ */
+class TextDumpsysSection : public WorkerThreadSection {
+public:
+    TextDumpsysSection(int id, const char* service, ...);
+    virtual ~TextDumpsysSection();
+
+    virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
+
+private:
+    String16 mService;
+    Vector<String16> mArgs;
+};
+
+/**
  * Section that calls dumpsys on a system service.
  */
 class SystemPropertyDumpsysSection : public WorkerThreadSection {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1b0e51e..f36b855 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -393,11 +393,13 @@
         WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
         AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"];
         SnapshotMergeReported snapshot_merge_reported = 255;
+        ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended =
+            256  [(module) = "framework"];
         SdkExtensionStatus sdk_extension_status = 354;
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10075
+    // Next: 10076
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -479,6 +481,7 @@
         PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences =
                 10073 [(module) = "framework"];
         GnssStats gnss_stats = 10074 [(module) = "framework"];
+        AppFeaturesOps app_features_ops = 10075 [(module) = "framework"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3284,12 +3287,12 @@
     ];
 }
 
-/*
+/**
  * Logs foreground service starts and stops.
  * Note that this is not when a service starts or stops, but when it is
  * considered foreground.
  * Logged from
- *     //frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ *     frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
  */
 message ForegroundServiceStateChanged {
     optional int32 uid = 1 [(is_uid) = true];
@@ -3301,6 +3304,49 @@
         EXIT = 2;
     }
     optional State state = 3;
+
+    // Whether the fgs is allowed while-in-use permissions, i.e. is considered 'in-use' to the user.
+    // (If the fgs was started while the app wasn't TOP it usually will be denied these permissions)
+    optional bool allow_while_in_use_permission = 4;
+}
+
+/**
+ * Logs the number of times a uid accesses a sensitive AppOp during a foreground service session.
+ * A foreground service session is any continuous period during which the uid holds at least one
+ * foreground service; the atom will be pushed when the uid no longer holds any foreground services.
+ * Accesses initiated while the uid is in the TOP state are ignored.
+ * Sessions with no attempted accesses are not logged.
+ * Logged from
+ *     frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
+ */
+message ForegroundServiceAppOpSessionEnded {
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // The operation's name.
+    // To the extent possible, preserve the mapping from AppOpsManager.OP_ constants.
+    // Only these named ops are actually logged.
+    enum AppOpName {
+        OP_NONE = -1; // Also represents UNKNOWN.
+        OP_COARSE_LOCATION = 0;
+        OP_FINE_LOCATION = 1;
+        OP_CAMERA = 26;
+        OP_RECORD_AUDIO = 27;
+    }
+    optional AppOpName app_op_name = 2 [default = OP_NONE];
+
+    // The uid's permission mode for accessing the AppOp during this fgs session.
+    enum Mode {
+        MODE_UNKNOWN = 0;
+        MODE_ALLOWED = 1; // Always allowed
+        MODE_IGNORED = 2; // Denied
+        MODE_FOREGROUND = 3; // Allow-while-in-use (or allowed-one-time)
+    }
+    optional Mode app_op_mode = 3;
+
+    // Number of times this AppOp was requested and allowed.
+    optional int32 count_ops_accepted = 4;
+    // Number of times this AppOp was requested but denied.
+    optional int32 count_ops_rejected = 5;
 }
 
 /**
@@ -7548,6 +7594,51 @@
 }
 
 /**
+ * Historical app ops data per package and features.
+ */
+message AppFeaturesOps {
+    // Uid of the package requesting the op
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // Name of the package performing the op
+    optional string package_name = 2;
+
+    // feature id; provided by developer when accessing related API, limited at 50 chars by API.
+    // Features must be provided through manifest using <feature> tag available in R and above.
+    optional string feature_id = 3;
+
+    // operation id; maps to the OPSTR_* constants in AppOpsManager.java
+    optional string op = 4;
+
+    // The number of times the op was granted while the app was in the
+    // foreground (only for trusted requests)
+    optional int64 trusted_foreground_granted_count = 5;
+
+    // The number of times the op was granted while the app was in the
+    // background (only for trusted requests)
+    optional int64 trusted_background_granted_count = 6;
+
+    // The number of times the op was rejected while the app was in the
+    // foreground (only for trusted requests)
+    optional int64 trusted_foreground_rejected_count = 7;
+
+    // The number of times the op was rejected while the app was in the
+    // background (only for trusted requests)
+    optional int64 trusted_background_rejected_count = 8;
+
+    // For long-running operations, total duration of the operation
+    // while the app was in the foreground (only for trusted requests)
+    optional int64 trusted_foreground_duration_millis = 9;
+
+    // For long-running operations, total duration of the operation
+    // while the app was in the background (only for trusted requests)
+    optional int64 trusted_background_duration_millis = 10;
+
+    // Whether AppOps is guarded by Runtime permission
+    optional bool is_runtime_permission = 11;
+}
+
+/**
  * Location Manager API Usage information(e.g. API under usage,
  * API call's parameters).
  * Logged from:
@@ -8143,7 +8234,7 @@
  */
 message CameraActionEvent {
     // Camera session duration
-    optional int64 duration = 1;
+    optional int64 duration_millis = 1;
 
     // Camera API level used
     optional int32 api_level = 2;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 82fdb90..b51bbdf 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2012,15 +2012,16 @@
         /** See {@link android.view.Surface.Rotation} */
         @Surface.Rotation
         private int mRotation;
+        /** The size of the snapshot before scaling */
+        private final Point mTaskSize;
         private final Rect mContentInsets;
-        // Whether this snapshot is a down-sampled version of the full resolution, used mainly for
-        // low-ram devices
+        // Whether this snapshot is a down-sampled version of the high resolution snapshot, used
+        // mainly for loading snapshots quickly from disk when user is flinging fast
         private final boolean mIsLowResolution;
         // Whether or not the snapshot is a real snapshot or an app-theme generated snapshot due to
         // the task having a secure window or having previews disabled
         private final boolean mIsRealSnapshot;
         private final int mWindowingMode;
-        private final float mScale;
         private final int mSystemUiVisibility;
         private final boolean mIsTranslucent;
         // Must be one of the named color spaces, otherwise, always use SRGB color space.
@@ -2028,9 +2029,9 @@
 
         public TaskSnapshot(long id,
                 @NonNull ComponentName topActivityComponent, GraphicBuffer snapshot,
-                @NonNull ColorSpace colorSpace, int orientation, int rotation, Rect contentInsets,
-                boolean isLowResolution, float scale, boolean isRealSnapshot, int windowingMode,
-                int systemUiVisibility, boolean isTranslucent) {
+                @NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize,
+                Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot,
+                int windowingMode, int systemUiVisibility, boolean isTranslucent) {
             mId = id;
             mTopActivityComponent = topActivityComponent;
             mSnapshot = snapshot;
@@ -2038,9 +2039,9 @@
                     ? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace;
             mOrientation = orientation;
             mRotation = rotation;
+            mTaskSize = new Point(taskSize);
             mContentInsets = new Rect(contentInsets);
             mIsLowResolution = isLowResolution;
-            mScale = scale;
             mIsRealSnapshot = isRealSnapshot;
             mWindowingMode = windowingMode;
             mSystemUiVisibility = systemUiVisibility;
@@ -2057,9 +2058,9 @@
                     : ColorSpace.get(ColorSpace.Named.SRGB);
             mOrientation = source.readInt();
             mRotation = source.readInt();
+            mTaskSize = source.readParcelable(null /* classLoader */);
             mContentInsets = source.readParcelable(null /* classLoader */);
             mIsLowResolution = source.readBoolean();
-            mScale = source.readFloat();
             mIsRealSnapshot = source.readBoolean();
             mWindowingMode = source.readInt();
             mSystemUiVisibility = source.readInt();
@@ -2111,6 +2112,14 @@
         }
 
         /**
+         * @return The size of the task at the point this snapshot was taken.
+         */
+        @UnsupportedAppUsage
+        public Point getTaskSize() {
+            return mTaskSize;
+        }
+
+        /**
          * @return The system/content insets on the snapshot. These can be clipped off in order to
          *         remove any areas behind system bars in the snapshot.
          */
@@ -2159,14 +2168,6 @@
             return mSystemUiVisibility;
         }
 
-        /**
-         * @return The scale this snapshot was taken in.
-         */
-        @UnsupportedAppUsage
-        public float getScale() {
-            return mScale;
-        }
-
         @Override
         public int describeContents() {
             return 0;
@@ -2180,9 +2181,9 @@
             dest.writeInt(mColorSpace.getId());
             dest.writeInt(mOrientation);
             dest.writeInt(mRotation);
+            dest.writeParcelable(mTaskSize, 0);
             dest.writeParcelable(mContentInsets, 0);
             dest.writeBoolean(mIsLowResolution);
-            dest.writeFloat(mScale);
             dest.writeBoolean(mIsRealSnapshot);
             dest.writeInt(mWindowingMode);
             dest.writeInt(mSystemUiVisibility);
@@ -2200,9 +2201,11 @@
                     + " mColorSpace=" + mColorSpace.toString()
                     + " mOrientation=" + mOrientation
                     + " mRotation=" + mRotation
+                    + " mTaskSize=" + mTaskSize.toString()
                     + " mContentInsets=" + mContentInsets.toShortString()
-                    + " mIsLowResolution=" + mIsLowResolution + " mScale=" + mScale
-                    + " mIsRealSnapshot=" + mIsRealSnapshot + " mWindowingMode=" + mWindowingMode
+                    + " mIsLowResolution=" + mIsLowResolution
+                    + " mIsRealSnapshot=" + mIsRealSnapshot
+                    + " mWindowingMode=" + mWindowingMode
                     + " mSystemUiVisibility=" + mSystemUiVisibility
                     + " mIsTranslucent=" + mIsTranslucent;
         }
@@ -2224,9 +2227,8 @@
             private ColorSpace mColorSpace;
             private int mOrientation;
             private int mRotation;
+            private Point mTaskSize;
             private Rect mContentInsets;
-            private boolean mIsLowResolution;
-            private float mScaleFraction;
             private boolean mIsRealSnapshot;
             private int mWindowingMode;
             private int mSystemUiVisibility;
@@ -2263,28 +2265,19 @@
                 return this;
             }
 
+            /**
+             * Sets the original size of the task
+             */
+            public Builder setTaskSize(Point size) {
+                mTaskSize = size;
+                return this;
+            }
+
             public Builder setContentInsets(Rect contentInsets) {
                 mContentInsets = contentInsets;
                 return this;
             }
 
-            /**
-             * Set to true if this is a low-resolution snapshot stored in *_reduced.jpg.
-             */
-            public Builder setIsLowResolution(boolean isLowResolution) {
-                mIsLowResolution = isLowResolution;
-                return this;
-            }
-
-            public float getScaleFraction() {
-                return mScaleFraction;
-            }
-
-            public Builder setScaleFraction(float scaleFraction) {
-                mScaleFraction = scaleFraction;
-                return this;
-            }
-
             public Builder setIsRealSnapshot(boolean realSnapshot) {
                 mIsRealSnapshot = realSnapshot;
                 return this;
@@ -2322,9 +2315,12 @@
                         mColorSpace,
                         mOrientation,
                         mRotation,
+                        mTaskSize,
                         mContentInsets,
-                        mIsLowResolution,
-                        mScaleFraction,
+                        // When building a TaskSnapshot with the Builder class, isLowResolution
+                        // is always false. Low-res snapshots are only created when loading from
+                        // disk.
+                        false /* isLowResolution */,
                         mIsRealSnapshot,
                         mWindowingMode,
                         mSystemUiVisibility,
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 9ba56cf..d48b35b 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -362,25 +362,6 @@
     }
 
     /**
-     * Resize the input stack id to the given bounds with animate setting.
-     * @param stackId Id of the stack to resize.
-     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
-     * @param animate Whether we should play an animation for resizing stack.
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void resizePinnedStack(int stackId, Rect bounds, boolean animate) {
-        try {
-            if (animate) {
-                getService().animateResizePinnedStack(stackId, bounds, -1 /* animationDuration */);
-            } else {
-                getService().resizePinnedStack(bounds, null /* tempPinnedTaskBounds */);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Resize task to given bounds.
      * @param taskId Id of task to resize.
      * @param bounds Bounds to resize task.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 92dd91a..0ed5aec5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -68,6 +68,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
+import android.content.pm.ProviderInfoList;
 import android.content.pm.ServiceInfo;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
@@ -1011,8 +1012,9 @@
             sendMessage(H.STOP_SERVICE, token);
         }
 
+        @Override
         public final void bindApplication(String processName, ApplicationInfo appInfo,
-                List<ProviderInfo> providers, ComponentName instrumentationName,
+                ProviderInfoList providerList, ComponentName instrumentationName,
                 ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                 IInstrumentationWatcher instrumentationWatcher,
                 IUiAutomationConnection instrumentationUiConnection, int debugMode,
@@ -1052,7 +1054,7 @@
             AppBindData data = new AppBindData();
             data.processName = processName;
             data.appInfo = appInfo;
-            data.providers = providers;
+            data.providers = providerList.getList();
             data.instrumentationName = instrumentationName;
             data.instrumentationArgs = instrumentationArgs;
             data.instrumentationWatcher = instrumentationWatcher;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8f02f15..a53fc35 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -6910,11 +6910,7 @@
      * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
      */
     public int unsafeCheckOpRaw(@NonNull String op, int uid, @NonNull String packageName) {
-        try {
-            return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return unsafeCheckOpRawNoThrow(op, uid, packageName);
     }
 
     /**
@@ -6923,8 +6919,17 @@
      * {@link #MODE_FOREGROUND}.
      */
     public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
+        return unsafeCheckOpRawNoThrow(strOpToOp(op), uid, packageName);
+    }
+
+    /**
+     * Returns the <em>raw</em> mode associated with the op.
+     * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}.
+     * @hide
+     */
+    public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
         try {
-            return mService.checkOperationRaw(strOpToOp(op), uid, packageName);
+            return mService.checkOperationRaw(op, uid, packageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
new file mode 100644
index 0000000..fe13b8f
--- /dev/null
+++ b/core/java/android/app/DreamManager.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.dreams.DreamService;
+import android.service.dreams.IDreamManager;
+
+/**
+ * @hide
+ */
+@SystemService(Context.DREAM_SERVICE)
+@TestApi
+public class DreamManager {
+    private final IDreamManager mService;
+    private final Context mContext;
+
+    /**
+     * @hide
+     */
+    public DreamManager(Context context) throws ServiceManager.ServiceNotFoundException {
+        mService = IDreamManager.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(DreamService.DREAM_SERVICE));
+        mContext = context;
+    }
+
+    /**
+     * Starts dream service with name "name".
+     *
+     * <p>This is only used for testing the dream service APIs.
+     *
+     * @hide
+     */
+    @TestApi
+    @UserHandleAware
+    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+    public void startDream(@NonNull ComponentName name) {
+        try {
+            mService.testDream(mContext.getUserId(), name);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Stops the dream service on the device if one is started.
+     *
+     * <p> This is only used for testing the dream service APIs.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+    public void stopDream() {
+        try {
+            mService.awaken();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets the active dream on the device to be "dreamComponent".
+     *
+     * <p>This is only used for testing the dream service APIs.
+     *
+     * @hide
+     */
+    @TestApi
+    @UserHandleAware
+    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+    public void setActiveDream(@NonNull ComponentName dreamComponent) {
+        ComponentName[] dreams = {dreamComponent};
+        try {
+            mService.setDreamComponentsForUser(mContext.getUserId(), dreams);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 5b61402..266a06a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -98,6 +98,14 @@
             in ProfilerInfo profilerInfo, in Bundle options, int userId);
     boolean startNextMatchingActivity(in IBinder callingActivity,
             in Intent intent, in Bundle options);
+
+    /**
+    *  The DreamActivity has to be started in a special way that does not involve the PackageParser.
+    *  The DreamActivity is a framework component inserted in the dream application process. Hence,
+    *  it is not declared in the application's manifest and cannot be parsed. startDreamActivity
+    *  creates the activity and starts it without reaching out to the PackageParser.
+    */
+    boolean startDreamActivity(in Intent intent);
     int startActivityIntentSender(in IApplicationThread caller,
             in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent,
             in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
@@ -236,33 +244,9 @@
      */
     boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
     void moveTaskToStack(int taskId, int stackId, boolean toTop);
-    /**
-     * Resizes the input pinned stack to the given bounds with animation.
-     *
-     * @param stackId Id of the pinned stack to resize.
-     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     * @throws RemoteException
-     */
-    void animateResizePinnedStack(int stackId, in Rect bounds, int animationDuration);
     boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
             boolean animate, in Rect initialBounds, boolean showRecents);
     /**
-     * Use the offset to adjust the stack boundary with animation.
-     *
-     * @param stackId Id of the stack to adjust.
-     * @param compareBounds Offset is only applied if the current pinned stack bounds is equal to
-     *                      the compareBounds.
-     * @param xOffset The horizontal offset.
-     * @param yOffset The vertical offset.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     * @throws RemoteException
-     */
-    void offsetPinnedStackBounds(int stackId, in Rect compareBounds, int xOffset, int yOffset,
-            int animationDuration);
-    /**
      * Removes stacks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
@@ -371,23 +355,10 @@
     void startLocalVoiceInteraction(in IBinder token, in Bundle options);
     void stopLocalVoiceInteraction(in IBinder token);
     boolean supportsLocalVoiceInteraction();
-    void notifyPinnedStackAnimationStarted();
-    void notifyPinnedStackAnimationEnded();
 
     // Get device configuration
     ConfigurationInfo getDeviceConfigurationInfo();
 
-    /**
-     * Resizes the pinned stack.
-     *
-     * @param pinnedBounds The bounds for the pinned stack.
-     * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which
-     *                             might be different from the stack bounds to allow more
-     *                             flexibility while resizing, or {@code null} if they should be the
-     *                             same as the stack bounds.
-     */
-    void resizePinnedStack(in Rect pinnedBounds, in Rect tempPinnedTaskBounds);
-
     void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
             in CharSequence message);
 
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index c33c515..1f6e4ca 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -30,6 +30,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
+import android.content.pm.ProviderInfoList;
 import android.content.pm.ServiceInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -66,7 +67,7 @@
     @UnsupportedAppUsage
     void scheduleStopService(IBinder token);
     void bindApplication(in String packageName, in ApplicationInfo info,
-            in List<ProviderInfo> providers, in ComponentName testName,
+            in ProviderInfoList providerList, in ComponentName testName,
             in ProfilerInfo profilerInfo, in Bundle testArguments,
             IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,
             int debugMode, boolean enableBinderTracking, boolean trackAllocation,
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 37bdda0..28b28da 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -46,16 +46,6 @@
     void onPinnedActivityRestartAttempt(boolean clearedTask);
 
     /**
-     * Called whenever the pinned stack is starting animating a resize.
-     */
-    void onPinnedStackAnimationStarted();
-
-    /**
-     * Called whenever the pinned stack is done animating a resize.
-     */
-    void onPinnedStackAnimationEnded();
-
-    /**
      * Called when we launched an activity that we forced to be resizable.
      *
      * @param packageName Package name of the top activity in the task.
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 7212be8..3f2ec44 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,7 @@
  */
 package android.app;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -63,6 +64,7 @@
      * string takes two arguments, in this order: the
      * {@link #getId()} of the original notification channel, and the
      * {@link ShortcutInfo#getId() id} of the conversation.
+     * @hide
      */
     public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
 
@@ -554,8 +556,8 @@
     }
 
     /**
-     * Sets this channel as being person-centric. Different settings and functionality may be
-     * exposed for people-centric channels.
+     * Sets this channel as being converastion-centric. Different settings and functionality may be
+     * exposed for conversation-centric channels.
      *
      * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of
      *                        this type would be posted to in absence of a specific conversation id.
@@ -564,8 +566,8 @@
      * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this
      *                       channel's conversation.
      */
-    public void setConversationId(@Nullable String parentChannelId,
-            @Nullable String conversationId) {
+    public void setConversationId(@NonNull String parentChannelId,
+            @NonNull String conversationId) {
         mParentId = parentChannelId;
         mConversationId = conversationId;
     }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 88edb05..cbbdf63 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1573,6 +1573,15 @@
                 PRIORITY_CATEGORY_CONVERSATIONS,
         };
 
+        /** @hide */
+        @IntDef(prefix = { "PRIORITY_SENDERS_" }, value = {
+                PRIORITY_SENDERS_ANY,
+                PRIORITY_SENDERS_CONTACTS,
+                PRIORITY_SENDERS_STARRED,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface PrioritySenders {}
+
         /** Any sender is prioritized. */
         public static final int PRIORITY_SENDERS_ANY = 0;
         /** Saved contacts are prioritized. */
@@ -1816,8 +1825,9 @@
          * @param suppressedVisualEffects which visual interruptions should be suppressed from
          *                                notifications that are filtered by DND.
          */
-        public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
-                int suppressedVisualEffects, int priorityConversationSenders) {
+        public Policy(int priorityCategories, @PrioritySenders int priorityCallSenders,
+                @PrioritySenders int priorityMessageSenders,
+                int suppressedVisualEffects, @ConversationSenders int priorityConversationSenders) {
             this(priorityCategories, priorityCallSenders, priorityMessageSenders,
                     suppressedVisualEffects, STATE_UNSET, priorityConversationSenders);
         }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 4369680..d04630c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -186,7 +186,6 @@
 import android.telephony.TelephonyRegistryManager;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Slog;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.WindowManager;
@@ -223,8 +222,6 @@
 public final class SystemServiceRegistry {
     private static final String TAG = "SystemServiceRegistry";
 
-    private static final boolean ENABLE_SERVICE_NOT_FOUND_WTF = true;
-
     // Service registry information.
     // This information is never changed once static initialization has completed.
     private static final Map<Class<?>, String> SYSTEM_SERVICE_NAMES =
@@ -1334,6 +1331,13 @@
                         IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE);
                         return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b));
                     }});
+        registerService(Context.DREAM_SERVICE, DreamManager.class,
+                new CachedServiceFetcher<DreamManager>() {
+                    @Override
+                    public DreamManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        return new DreamManager(ctx);
+                    }});
 
         sInitializing = true;
         try {
@@ -1370,29 +1374,8 @@
      * @hide
      */
     public static Object getSystemService(ContextImpl ctx, String name) {
-        if (name == null) {
-            return null;
-        }
-        final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
-        if (ENABLE_SERVICE_NOT_FOUND_WTF && fetcher == null) {
-            // This should be a caller bug.
-            Slog.wtf(TAG, "Unknown manager requested: " + name);
-            return null;
-        }
-
-        final Object ret = fetcher.getService(ctx);
-        if (ENABLE_SERVICE_NOT_FOUND_WTF && ret == null) {
-            // Some services do return null in certain situations, so don't do WTF for them.
-            switch (name) {
-                case Context.CONTENT_CAPTURE_MANAGER_SERVICE:
-                case Context.APP_PREDICTION_SERVICE:
-                case Context.INCREMENTAL_SERVICE:
-                    return null;
-            }
-            Slog.wtf(TAG, "Manager wrapper not available: " + name);
-            return null;
-        }
-        return ret;
+        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
+        return fetcher != null ? fetcher.getService(ctx) : null;
     }
 
     /**
@@ -1400,15 +1383,7 @@
      * @hide
      */
     public static String getSystemServiceName(Class<?> serviceClass) {
-        if (serviceClass == null) {
-            return null;
-        }
-        final String serviceName = SYSTEM_SERVICE_NAMES.get(serviceClass);
-        if (ENABLE_SERVICE_NOT_FOUND_WTF && serviceName == null) {
-            // This should be a caller bug.
-            Slog.wtf(TAG, "Unknown manager requested: " + serviceClass.getCanonicalName());
-        }
-        return serviceName;
+        return SYSTEM_SERVICE_NAMES.get(serviceClass);
     }
 
     /**
@@ -1705,9 +1680,7 @@
                         try {
                             cache.wait();
                         } catch (InterruptedException e) {
-                            // This shouldn't normally happen, but if someone interrupts the
-                            // thread, it will.
-                            Slog.wtf(TAG, "getService() interrupted");
+                            Log.w(TAG, "getService() interrupted");
                             Thread.currentThread().interrupt();
                             return null;
                         }
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index da0aadb..b892b8e 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -58,16 +58,6 @@
 
     @Override
     @UnsupportedAppUsage
-    public void onPinnedStackAnimationStarted() throws RemoteException {
-    }
-
-    @Override
-    @UnsupportedAppUsage
-    public void onPinnedStackAnimationEnded() throws RemoteException {
-    }
-
-    @Override
-    @UnsupportedAppUsage
     public void onActivityForcedResizable(String packageName, int taskId, int reason)
             throws RemoteException {
     }
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index f0eedf3..7f43640 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -260,6 +260,7 @@
                 Log.e(TAG, "Failed to notify app target event", e);
                 e.rethrowAsRuntimeException();
             }
+            mRegisteredCallbacks.clear();
         } else {
             throw new IllegalStateException("This client has already been destroyed.");
         }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 6dea1c6..ccd8199 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -182,6 +182,16 @@
     public static final String EXTRA_APPWIDGET_ID = "appWidgetId";
 
     /**
+     * A bundle extra that contains whether or not an app has finished restoring a widget.
+     * <p> After restore, the app should set OPTION_APPWIDGET_RESTORE_COMPLETED to true on its
+     * widgets followed by calling {@link #updateAppWidget} to update the views.
+     *
+     * @see #updateAppWidgetOptions(int, Bundle)
+     */
+    public static final String OPTION_APPWIDGET_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
+
+
+    /**
      * A bundle extra that contains the lower bound on the current width, in dips, of a widget instance.
      */
     public static final String OPTION_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
index ab91edf..a5d2198 100644
--- a/core/java/android/appwidget/AppWidgetProvider.java
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -200,6 +200,9 @@
      * provider can immediately generate new RemoteViews suitable for its newly-restored set
      * of instances.
      *
+     * <p>In addition, you should set {@link AppWidgetManager#OPTION_APPWIDGET_RESTORE_COMPLETED}
+     * to true indicate if a widget has been restored successfully from the provider's side.
+     *
      * {@more}
      *
      * @param context
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c6e84b7..536b6c3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5157,6 +5157,17 @@
     public static final String LIGHTS_SERVICE = "lights";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.DreamManager} for controlling Dream states.
+     *
+     * @see #getSystemService(String)
+
+     * @hide
+     */
+    @TestApi
+    public static final String DREAM_SERVICE = "dream";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 9d1c677..4c6fef2 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1630,6 +1630,9 @@
 
     @SuppressWarnings("unchecked")
     public void writeToParcel(Parcel dest, int parcelableFlags) {
+        if (dest.maybeWriteSquashed(this)) {
+            return;
+        }
         super.writeToParcel(dest, parcelableFlags);
         dest.writeString(taskAffinity);
         dest.writeString(permission);
@@ -1700,9 +1703,12 @@
 
     public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
             = new Parcelable.Creator<ApplicationInfo>() {
+        @Override
         public ApplicationInfo createFromParcel(Parcel source) {
-            return new ApplicationInfo(source);
+            return source.readSquashed(ApplicationInfo::new);
         }
+
+        @Override
         public ApplicationInfo[] newArray(int size) {
             return new ApplicationInfo[size];
         }
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index 8b41c04..362098c 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -20,7 +20,6 @@
 import android.content.ComponentName;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
-import android.os.Parcelable;
 import android.util.Printer;
 
 /**
@@ -197,12 +196,7 @@
 
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         super.writeToParcel(dest, parcelableFlags);
-        if ((parcelableFlags & Parcelable.PARCELABLE_ELIDE_DUPLICATES) != 0) {
-            dest.writeInt(0);
-        } else {
-            dest.writeInt(1);
-            applicationInfo.writeToParcel(dest, parcelableFlags);
-        }
+        applicationInfo.writeToParcel(dest, parcelableFlags);
         dest.writeString(processName);
         dest.writeString(splitName);
         dest.writeInt(descriptionRes);
@@ -213,10 +207,7 @@
     
     protected ComponentInfo(Parcel source) {
         super(source);
-        final boolean hasApplicationInfo = (source.readInt() != 0);
-        if (hasApplicationInfo) {
-            applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
-        }
+        applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
         processName = source.readString();
         splitName = source.readString();
         descriptionRes = source.readInt();
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 36fa572..85c698f 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -439,6 +439,8 @@
 
     @Override
     public void writeToParcel(Parcel dest, int parcelableFlags) {
+        // Allow ApplicationInfo to be squashed.
+        final boolean prevAllowSquashing = dest.allowSquashing();
         dest.writeString(packageName);
         dest.writeStringArray(splitNames);
         dest.writeInt(versionCode);
@@ -457,10 +459,10 @@
         dest.writeLong(firstInstallTime);
         dest.writeLong(lastUpdateTime);
         dest.writeIntArray(gids);
-        dest.writeTypedArray(activities, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
-        dest.writeTypedArray(receivers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
-        dest.writeTypedArray(services, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
-        dest.writeTypedArray(providers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+        dest.writeTypedArray(activities, parcelableFlags);
+        dest.writeTypedArray(receivers, parcelableFlags);
+        dest.writeTypedArray(services, parcelableFlags);
+        dest.writeTypedArray(providers, parcelableFlags);
         dest.writeTypedArray(instrumentation, parcelableFlags);
         dest.writeTypedArray(permissions, parcelableFlags);
         dest.writeStringArray(requestedPermissions);
@@ -488,6 +490,7 @@
             dest.writeInt(0);
         }
         dest.writeBoolean(isApex);
+        dest.restoreAllowSquashing(prevAllowSquashing);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<PackageInfo> CREATOR
@@ -550,21 +553,5 @@
             signingInfo = SigningInfo.CREATOR.createFromParcel(source);
         }
         isApex = source.readBoolean();
-        // The component lists were flattened with the redundant ApplicationInfo
-        // instances omitted.  Distribute the canonical one here as appropriate.
-        if (applicationInfo != null) {
-            propagateApplicationInfo(applicationInfo, activities);
-            propagateApplicationInfo(applicationInfo, receivers);
-            propagateApplicationInfo(applicationInfo, services);
-            propagateApplicationInfo(applicationInfo, providers);
-        }
-    }
-
-    private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
-        if (components != null) {
-            for (ComponentInfo ci : components) {
-                ci.applicationInfo = appInfo;
-            }
-        }
     }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f2ec938..fa751d3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7853,11 +7853,16 @@
     /**
      * Returns if the provided drawable represents the default activity icon provided by the system.
      *
-     * PackageManager provides a default icon for any package/activity if the app itself does not
-     * define one or if the system encountered any error when loading the icon.
+     * PackageManager silently returns a default application icon for any package/activity if the
+     * app itself does not define one or if the system encountered any error when loading the icon.
+     *
+     * Developers can use this to check implement app specific logic around retrying or caching.
      *
      * @return true if the drawable represents the default activity icon, false otherwise
      * @see #getDefaultActivityIcon()
+     * @see PackageItemInfo#loadDefaultIcon(PackageManager)
+     * @see #getActivityIcon
+     * @see LauncherActivityInfo#getIcon(int)
      */
     public boolean isDefaultApplicationIcon(@NonNull Drawable drawable) {
         int resId = drawable instanceof AdaptiveIconDrawable
diff --git a/core/java/android/content/pm/ProviderInfoList.aidl b/core/java/android/content/pm/ProviderInfoList.aidl
new file mode 100644
index 0000000..bb576d7
--- /dev/null
+++ b/core/java/android/content/pm/ProviderInfoList.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+parcelable ProviderInfoList;
diff --git a/core/java/android/content/pm/ProviderInfoList.java b/core/java/android/content/pm/ProviderInfoList.java
new file mode 100644
index 0000000..566be2e
--- /dev/null
+++ b/core/java/android/content/pm/ProviderInfoList.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Equivalent to List<ProviderInfo>, but it "squashes" the ApplicationInfo in the elements.
+ *
+ * @hide
+ */
+@TestApi
+public final class ProviderInfoList implements Parcelable {
+    private final List<ProviderInfo> mList;
+
+    private ProviderInfoList(Parcel source) {
+        final ArrayList<ProviderInfo> list = new ArrayList<>();
+        source.readTypedList(list, ProviderInfo.CREATOR);
+        mList = list;
+    }
+
+    private ProviderInfoList(List<ProviderInfo> list) {
+        mList = list;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // Allow ApplicationInfo to be squashed.
+        final boolean prevAllowSquashing = dest.allowSquashing();
+        dest.writeTypedList(mList, flags);
+        dest.restoreAllowSquashing(prevAllowSquashing);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<ProviderInfoList> CREATOR
+            = new Parcelable.Creator<ProviderInfoList>() {
+        @Override
+        public ProviderInfoList createFromParcel(@NonNull Parcel source) {
+            return new ProviderInfoList(source);
+        }
+
+        @Override
+        public ProviderInfoList[] newArray(int size) {
+            return new ProviderInfoList[size];
+        }
+    };
+
+    /**
+     * Return the stored list.
+     */
+    @NonNull
+    public List<ProviderInfo> getList() {
+        return mList;
+    }
+
+    /**
+     * Create a new instance with a {@code list}. The passed list will be shared with the new
+     * instance, so the caller shouldn't modify it.
+     */
+    @NonNull
+    public static ProviderInfoList fromList(@NonNull List<ProviderInfo> list) {
+        return new ProviderInfoList(list);
+    }
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1a4dac7..f0b7b5f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 import android.util.Size;
 import android.util.SizeF;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
@@ -1827,6 +1828,179 @@
     }
 
     /**
+     * A map used by {@link #maybeWriteSquashed} to keep track of what parcelables have
+     * been seen, and what positions they were written. The value is the absolute position of
+     * each parcelable.
+     */
+    private ArrayMap<Parcelable, Integer> mWrittenSquashableParcelables;
+
+    private void ensureWrittenSquashableParcelables() {
+        if (mWrittenSquashableParcelables != null) {
+            return;
+        }
+        mWrittenSquashableParcelables = new ArrayMap<>();
+    }
+
+    private boolean mAllowSquashing = false;
+
+    /**
+     * Allow "squashing" writes in {@link #maybeWriteSquashed}. This allows subsequent calls to
+     * {@link #maybeWriteSquashed(Parcelable)} to "squash" the same instances into one in a Parcel.
+     *
+     * Typically, this method is called at the beginning of {@link Parcelable#writeToParcel}. The
+     * caller must retain the return value from this method and call {@link #restoreAllowSquashing}
+     * with it.
+     *
+     * See {@link #maybeWriteSquashed(Parcelable)} for the details.
+     *
+     * @see #restoreAllowSquashing(boolean)
+     * @see #maybeWriteSquashed(Parcelable)
+     * @see #readSquashed(SquashReadHelper)
+     *
+     * @hide
+     */
+    @TestApi
+    public boolean allowSquashing() {
+        boolean previous = mAllowSquashing;
+        mAllowSquashing = true;
+        return previous;
+    }
+
+    /**
+     * @see #allowSquashing()
+     * @hide
+     */
+    @TestApi
+    public void restoreAllowSquashing(boolean previous) {
+        mAllowSquashing = previous;
+        if (!mAllowSquashing) {
+            mWrittenSquashableParcelables = null;
+        }
+    }
+
+    private void resetSqaushingState() {
+        if (mAllowSquashing) {
+            Slog.wtf(TAG, "allowSquashing wasn't restored.");
+        }
+        mWrittenSquashableParcelables = null;
+        mReadSquashableParcelables = null;
+        mAllowSquashing = false;
+    }
+
+    /**
+     * A map used by {@link #readSquashed} to cache parcelables. It's a map from
+     * an absolute position in a Parcel to the parcelable stored at the position.
+     */
+    private ArrayMap<Integer, Parcelable> mReadSquashableParcelables;
+
+    private void ensureReadSquashableParcelables() {
+        if (mReadSquashableParcelables != null) {
+            return;
+        }
+        mReadSquashableParcelables = new ArrayMap<>();
+    }
+
+    /**
+     * Write a parcelable with "squash" -- that is, when the same instance is written to the
+     * same Parcelable multiple times, instead of writing the entire instance multiple times,
+     * only write it once, and in subsequent writes we'll only write the offset to the original
+     * object.
+     *
+     * This approach does not work of the resulting Parcel is copied with {@link #appendFrom} with
+     * a non-zero offset, so we do not enable this behavior by default. Instead, we only enable
+     * it between {@link #allowSquashing} and {@link #restoreAllowSquashing}, in order to make sure
+     * we only do so within each "top level" Parcelable.
+     *
+     * Usage: Use this method in {@link Parcelable#writeToParcel}.
+     * If this method returns TRUE, it's a subsequent call, and the offset is already written,
+     * so the caller doesn't have to do anything. If this method returns FALSE, it's the first
+     * time for the instance to be written to this parcel. The caller has to proceed with its
+     * {@link Parcelable#writeToParcel}.
+     *
+     * (See {@code ApplicationInfo} for the example.)
+     *
+     * @param p the target Parcelable to write.
+     *
+     * @see #allowSquashing()
+     * @see #restoreAllowSquashing(boolean)
+     * @see #readSquashed(SquashReadHelper)
+     *
+     * @hide
+     */
+    public boolean maybeWriteSquashed(@NonNull Parcelable p) {
+        if (!mAllowSquashing) {
+            // Don't squash, and don't put it in the map either.
+            writeInt(0);
+            return false;
+        }
+        ensureWrittenSquashableParcelables();
+        final Integer firstPos = mWrittenSquashableParcelables.get(p);
+        if (firstPos != null) {
+            // Already written.
+            // Write the relative offset from the current position to the first position.
+            final int pos = dataPosition();
+
+            // We want the offset from the next byte of this integer, so we need to +4.
+            writeInt(pos - firstPos + 4);
+            return true;
+        }
+        // First time seen, write a marker.
+        writeInt(0);
+
+        // Remember the position.
+        final int pos = dataPosition();
+        mWrittenSquashableParcelables.put(p, pos);
+
+        // Return false and let the caller actually write the content.
+        return false;
+    }
+
+    /**
+     * Helper function that's used by {@link #readSquashed(SquashReadHelper)}
+     * @hide
+     */
+    public interface SquashReadHelper<T> {
+        /** Read and instantiate {@code T} from a Parcel. */
+        @NonNull
+        T readRawParceled(@NonNull Parcel p);
+    }
+
+    /**
+     * Read a {@link Parcelable} that's written with {@link #maybeWriteSquashed}.
+     *
+     * @param reader a callback function that instantiates an instance from a parcel.
+     * Typicallly, a lambda to the instructor that takes a {@link Parcel} is passed.
+     *
+     * @see #maybeWriteSquashed(Parcelable)
+     *
+     * @hide
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) {
+        final int offset = readInt();
+        final int pos = dataPosition();
+
+        if (offset == 0) {
+            // First time read. Unparcel, and remember it.
+            final T p = reader.readRawParceled(this);
+            ensureReadSquashableParcelables();
+            mReadSquashableParcelables.put(pos, p);
+            return p;
+        }
+        // Subsequent read.
+        final int firstAbsolutePos = pos - offset;
+
+        final Parcelable p = mReadSquashableParcelables.get(firstAbsolutePos);
+        if (p == null) {
+            Slog.wtfStack(TAG, "Map doesn't contain offset "
+                    + firstAbsolutePos
+                    + " : contains=" + new ArrayList<>(mReadSquashableParcelables.keySet()));
+        }
+        return (T) p;
+    }
+
+    /**
      * Write a generic serializable object in to a Parcel.  It is strongly
      * recommended that this method be avoided, since the serialization
      * overhead is extremely large, and this approach will be much slower than
@@ -3247,6 +3421,7 @@
     }
 
     private void freeBuffer() {
+        resetSqaushingState();
         if (mOwnsNativeParcelObject) {
             updateNativeSize(nativeFreeBuffer(mNativePtr));
         }
@@ -3254,6 +3429,7 @@
     }
 
     private void destroy() {
+        resetSqaushingState();
         if (mNativePtr != 0) {
             if (mOwnsNativeParcelObject) {
                 nativeDestroy(mNativePtr);
@@ -3261,7 +3437,6 @@
             }
             mNativePtr = 0;
         }
-        mReadWriteHelper = null;
     }
 
     @Override
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 5cb3361..50d8d80 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -96,9 +96,6 @@
 
     private static final String TAG = "DynSystemClient";
 
-    private static final long DEFAULT_USERDATA_SIZE = (10L << 30);
-
-
     /** Listener for installation status updates. */
     public interface OnStatusChangedListener {
         /**
@@ -386,7 +383,7 @@
     @SystemApi
     @TestApi
     public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) {
-        start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
+        start(systemUrl, systemSize, 0 /* Use the default userdata size */);
     }
 
     /**
diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
index c389b1a..dd434b4 100644
--- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -28,5 +28,5 @@
 oneway interface IInlineSuggestionRenderService {
     void renderSuggestion(in IInlineSuggestionUiCallback callback,
                           in InlinePresentation presentation, int width, int height,
-                          in IBinder hostInputToken);
+                          in IBinder hostInputToken, int displayId);
 }
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 29069e7..17e0456 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -23,14 +23,18 @@
 import android.annotation.TestApi;
 import android.app.Service;
 import android.app.slice.Slice;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
@@ -61,7 +65,8 @@
     private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
 
     private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
-            InlinePresentation presentation, int width, int height, IBinder hostInputToken) {
+            InlinePresentation presentation, int width, int height, IBinder hostInputToken,
+            int displayId) {
         if (hostInputToken == null) {
             try {
                 callback.onError();
@@ -70,8 +75,17 @@
             }
             return;
         }
-        final SurfaceControlViewHost host = new SurfaceControlViewHost(this, this.getDisplay(),
-                hostInputToken);
+
+        final DisplayManager displayManager = getSystemService(DisplayManager.class);
+        final Display targetDisplay = displayManager.getDisplay(displayId);
+        if (targetDisplay == null) {
+            sendResult(callback, /*surface*/ null);
+            return;
+        }
+        final Context displayContext = createDisplayContext(targetDisplay);
+
+        final SurfaceControlViewHost host = new SurfaceControlViewHost(displayContext,
+                displayContext.getDisplay(), hostInputToken);
         final SurfaceControl surface = host.getSurfacePackage().getSurfaceControl();
 
         final View suggestionView = onRenderSuggestion(presentation, width, height);
@@ -90,6 +104,11 @@
                 new WindowManager.LayoutParams(width, height,
                         WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
         host.addView(suggestionRoot, lp);
+        sendResult(callback, surface);
+    }
+
+    private void sendResult(@NonNull IInlineSuggestionUiCallback callback,
+            @Nullable SurfaceControl surface) {
         try {
             callback.onContent(surface);
         } catch (RemoteException e) {
@@ -105,11 +124,11 @@
                 @Override
                 public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
                         @NonNull InlinePresentation presentation, int width, int height,
-                        @Nullable IBinder hostInputToken) {
+                        @Nullable IBinder hostInputToken, int displayId) {
                     mHandler.sendMessage(obtainMessage(
                             InlineSuggestionRenderService::handleRenderSuggestion,
                             InlineSuggestionRenderService.this, callback, presentation,
-                            width, height, hostInputToken));
+                            width, height, hostInputToken, displayId));
                 }
             }.asBinder();
         }
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
new file mode 100644
index 0000000..8cdd24e
--- /dev/null
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.dreams;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * The Activity used by the {@link DreamService} to draw screensaver content
+ * on the screen. This activity runs in dream application's process, but is started by a
+ * specialized method: {@link com.android.server.wm.ActivityTaskManagerService#startDreamActivity}.
+ * Hence, it does not have to be declared in the dream application's manifest.
+ *
+ * We use an activity as the dream canvas, because it interacts easier with other activities on
+ * the screen (compared to a hover window). However, the DreamService is in charge of the dream and
+ * it receives all Window.Callbacks from its main window. Since a window can have only one callback
+ * receiver, the activity will not receive any window callbacks.
+ *
+ * Prior to the DreamActivity, the DreamService used to work with a hovering window and give the
+ * screensaver application control over that window. The DreamActivity is a replacement to that
+ * hover window. Using an activity allows for better-defined interactions with the rest of the
+ * activities on screen. The switch to DreamActivity should be transparent to the screensaver
+ * application, i.e. the application will still use DreamService APIs and not notice that the
+ * system is using an activity behind the scenes.
+ *
+ * @hide
+ */
+public class DreamActivity extends Activity {
+    static final String EXTRA_CALLBACK = "binder";
+
+    public DreamActivity() {}
+
+    @Override
+    public void onCreate(@Nullable Bundle bundle) {
+        super.onCreate(bundle);
+
+        DreamService.DreamServiceWrapper callback =
+                (DreamService.DreamServiceWrapper) getIntent().getIBinderExtra(EXTRA_CALLBACK);
+
+        if (callback != null) {
+            callback.onActivityCreated(this);
+        }
+    }
+}
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index ff7cef9..41fdd0b 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -16,6 +16,8 @@
 
 package android.service.dreams;
 
+import android.content.ComponentName;
+
 /**
  * Dream manager local system service interface.
  *
@@ -42,4 +44,13 @@
      * Called by the power manager to determine whether a dream is running.
      */
     public abstract boolean isDreaming();
+
+    /**
+     * Called by the ActivityTaskManagerService to verify that the startDreamActivity
+     * request comes from the current active dream component.
+     *
+     * @param doze If true returns the current active doze component. Otherwise, returns the
+     *             active dream component.
+     */
+    public abstract ComponentName getActiveDreamComponent(boolean doze);
 }
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index de4a551..28f4929 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -15,25 +15,29 @@
  */
 package android.service.dreams;
 
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Activity;
+import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
 import android.app.Service;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.ColorDrawable;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.view.ActionMode;
@@ -48,10 +52,8 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
-import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 
-import com.android.internal.policy.PhoneWindow;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.DumpUtils.Dump;
 
@@ -176,10 +178,11 @@
      */
     public static final String DREAM_META_DATA = "android.service.dream";
 
-    private final IDreamManager mSandman;
-    private final Handler mHandler = new Handler();
-    private IBinder mWindowToken;
+    private final IDreamManager mDreamManager;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private IBinder mDreamToken;
     private Window mWindow;
+    private Activity mActivity;
     private boolean mInteractive;
     private boolean mLowProfile = true;
     private boolean mFullscreen;
@@ -195,8 +198,11 @@
 
     private boolean mDebug = false;
 
+    private DreamServiceWrapper mDreamServiceWrapper;
+    private Runnable mDispatchAfterOnAttachedToWindow;
+
     public DreamService() {
-        mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
+        mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE));
     }
 
     /**
@@ -602,6 +608,8 @@
      * Marks this dream as windowless.  Only available to doze dreams.
      *
      * @hide
+     *
+     * TODO: Remove @UnsupportedAppUsage.
      */
     @UnsupportedAppUsage
     public void setWindowless(boolean windowless) {
@@ -670,14 +678,14 @@
     }
 
     private void updateDoze() {
-        if (mWindowToken == null) {
-            Slog.w(TAG, "Updating doze without a window token.");
+        if (mDreamToken == null) {
+            Slog.w(TAG, "Updating doze without a dream token.");
             return;
         }
 
         if (mDozing) {
             try {
-                mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness);
+                mDreamManager.startDozing(mDreamToken, mDozeScreenState, mDozeScreenBrightness);
             } catch (RemoteException ex) {
                 // system server died
             }
@@ -700,7 +708,7 @@
         if (mDozing) {
             mDozing = false;
             try {
-                mSandman.stopDozing(mWindowToken);
+                mDreamManager.stopDozing(mDreamToken);
             } catch (RemoteException ex) {
                 // system server died
             }
@@ -875,14 +883,15 @@
      * </p>
      */
     public void onWakeUp() {
-        finish();
+        mActivity.finishAndRemoveTask();
     }
 
     /** {@inheritDoc} */
     @Override
     public final IBinder onBind(Intent intent) {
         if (mDebug) Slog.v(TAG, "onBind() intent = " + intent);
-        return new DreamServiceWrapper();
+        mDreamServiceWrapper = new DreamServiceWrapper();
+        return mDreamServiceWrapper;
     }
 
     /**
@@ -895,20 +904,25 @@
     public final void finish() {
         if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished);
 
+        if (mActivity == null) {
+            Slog.w(TAG, "Finish was called before the dream was attached.");
+        } else if (!mActivity.isFinishing()) {
+            // In case the activity is not finished yet, do it now. This can happen if someone calls
+            // finish() directly, without going through wakeUp().
+            mActivity.finishAndRemoveTask();
+            return;
+        }
+
         if (!mFinished) {
             mFinished = true;
 
-            if (mWindowToken == null) {
-                Slog.w(TAG, "Finish was called before the dream was attached.");
-            } else {
-                try {
-                    mSandman.finishSelf(mWindowToken, true /*immediate*/);
-                } catch (RemoteException ex) {
-                    // system server died
-                }
+            try {
+                // finishSelf will unbind the dream controller from the dream service. This will
+                // trigger DreamService.this.onDestroy and DreamService.this will die.
+                mDreamManager.finishSelf(mDreamToken, true /*immediate*/);
+            } catch (RemoteException ex) {
+                // system server died
             }
-
-            stopSelf(); // if launched via any other means
         }
     }
 
@@ -938,11 +952,11 @@
             // Now tell the system we are waking gently, unless we already told
             // it we were finishing immediately.
             if (!fromSystem && !mFinished) {
-                if (mWindowToken == null) {
+                if (mActivity == null) {
                     Slog.w(TAG, "WakeUp was called before the dream was attached.");
                 } else {
                     try {
-                        mSandman.finishSelf(mWindowToken, false /*immediate*/);
+                        mDreamManager.finishSelf(mDreamToken, false /*immediate*/);
                     } catch (RemoteException ex) {
                         // system server died
                     }
@@ -977,20 +991,14 @@
             onDreamingStopped();
         }
 
-        if (mWindow != null) {
-            // force our window to be removed synchronously
-            if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager");
-            mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView());
-            mWindow = null;
+        if (mActivity != null && !mActivity.isFinishing()) {
+            mActivity.finishAndRemoveTask();
+        } else {
+            finish();
         }
 
-        if (mWindowToken != null) {
-            // the following will print a log message if it finds any other leaked windows
-            WindowManagerGlobal.getInstance().closeAll(mWindowToken,
-                    this.getClass().getName(), "Dream");
-            mWindowToken = null;
-            mCanDoze = false;
-        }
+        mDreamToken = null;
+        mCanDoze = false;
     }
 
     /**
@@ -998,95 +1006,107 @@
      *
      * Must run on mHandler.
      *
-     * @param windowToken A window token that will allow a window to be created in the correct layer.
+     * @param dreamToken Token for this dream service.
      * @param started A callback that will be invoked once onDreamingStarted has completed.
      */
-    private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) {
-        if (mWindowToken != null) {
-            Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken);
+    private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) {
+        if (mActivity != null) {
+            Slog.e(TAG, "attach() called when dream with token=" + mDreamToken
+                    + " already attached");
             return;
         }
         if (mFinished || mWaking) {
             Slog.w(TAG, "attach() called after dream already finished");
             try {
-                mSandman.finishSelf(windowToken, true /*immediate*/);
+                mDreamManager.finishSelf(dreamToken, true /*immediate*/);
             } catch (RemoteException ex) {
                 // system server died
             }
             return;
         }
 
-        mWindowToken = windowToken;
+        mDreamToken = dreamToken;
         mCanDoze = canDoze;
         if (mWindowless && !mCanDoze) {
             throw new IllegalStateException("Only doze dreams can be windowless");
         }
-        if (!mWindowless) {
-            mWindow = new PhoneWindow(this);
-            mWindow.setCallback(this);
-            mWindow.requestFeature(Window.FEATURE_NO_TITLE);
-            mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000));
-            mWindow.setFormat(PixelFormat.OPAQUE);
 
-            if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s",
-                    windowToken, WindowManager.LayoutParams.TYPE_DREAM));
-
-            WindowManager.LayoutParams lp = mWindow.getAttributes();
-            lp.type = WindowManager.LayoutParams.TYPE_DREAM;
-            lp.token = windowToken;
-            lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
-            lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                        | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
-                        | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
-                        | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
-                        | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
-                        );
-            mWindow.setAttributes(lp);
-            // Workaround: Currently low-profile and in-window system bar backgrounds don't go
-            // along well. Dreams usually don't need such bars anyways, so disable them by default.
-            mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
-            mWindow.setWindowManager(null, windowToken, "dream", true);
-
-            applySystemUiVisibilityFlags(
-                    (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
-                    View.SYSTEM_UI_FLAG_LOW_PROFILE);
-
-            try {
-                getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes());
-            } catch (WindowManager.BadTokenException ex) {
-                // This can happen because the dream manager service will remove the token
-                // immediately without necessarily waiting for the dream to start.
-                // We should receive a finish message soon.
-                Slog.i(TAG, "attach() called after window token already removed, dream will "
-                        + "finish soon");
-                mWindow = null;
-                return;
-            }
-        }
-        // We need to defer calling onDreamingStarted until after onWindowAttached,
-        // which is posted to the handler by addView, so we post onDreamingStarted
-        // to the handler also.  Need to watch out here in case detach occurs before
-        // this callback is invoked.
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mWindow != null || mWindowless) {
-                    if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()");
-                    mStarted = true;
+        mDispatchAfterOnAttachedToWindow = () -> {
+            if (mWindow != null || mWindowless) {
+                mStarted = true;
+                try {
+                    onDreamingStarted();
+                } finally {
                     try {
-                        onDreamingStarted();
-                    } finally {
-                        try {
-                            started.sendResult(null);
-                        } catch (RemoteException e) {
-                            throw e.rethrowFromSystemServer();
-                        }
+                        started.sendResult(null);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
                     }
                 }
             }
-        });
+        };
+
+        // We need to defer calling onDreamingStarted until after the activity is created.
+        // If the dream is windowless, we can call it immediately. Otherwise, we wait
+        // for the DreamActivity to report onActivityCreated via
+        // DreamServiceWrapper.onActivityCreated.
+        if (!mWindowless) {
+            Intent i = new Intent(this, DreamActivity.class);
+            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            i.putExtra(DreamActivity.EXTRA_CALLBACK, mDreamServiceWrapper);
+
+            try {
+                if (!ActivityTaskManager.getService().startDreamActivity(i)) {
+                    detach();
+                    return;
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Could not connect to activity task manager to start dream activity");
+                e.rethrowFromSystemServer();
+            }
+        } else {
+            mDispatchAfterOnAttachedToWindow.run();
+        }
+    }
+
+    private void onWindowCreated(Window w) {
+        mWindow = w;
+        mWindow.setCallback(this);
+        mWindow.setType(TYPE_DREAM);
+        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+
+        WindowManager.LayoutParams lp = mWindow.getAttributes();
+        lp.windowAnimations = com.android.internal.R.style.Animation_Dream;
+        lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                    | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+                    | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+                    | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0)
+                    | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0)
+                    );
+        mWindow.setAttributes(lp);
+        // Workaround: Currently low-profile and in-window system bar backgrounds don't go
+        // along well. Dreams usually don't need such bars anyways, so disable them by default.
+        mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+
+        applySystemUiVisibilityFlags(
+                (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
+                View.SYSTEM_UI_FLAG_LOW_PROFILE);
+
+        mWindow.getDecorView().addOnAttachStateChangeListener(
+                new View.OnAttachStateChangeListener() {
+                    @Override
+                    public void onViewAttachedToWindow(View v) {
+                        mDispatchAfterOnAttachedToWindow.run();
+                    }
+
+                    @Override
+                    public void onViewDetachedFromWindow(View v) {
+                        finish();
+                    }
+                });
     }
 
     private boolean getWindowFlagValue(int flag, boolean defaultValue) {
@@ -1131,10 +1151,10 @@
     /** @hide */
     protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print(TAG + ": ");
-        if (mWindowToken == null) {
+        if (mFinished) {
             pw.println("stopped");
         } else {
-            pw.println("running (token=" + mWindowToken + ")");
+            pw.println("running (dreamToken=" + mDreamToken + ")");
         }
         pw.println("  window: " + mWindow);
         pw.print("  flags:");
@@ -1156,36 +1176,32 @@
         return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
     }
 
-    private final class DreamServiceWrapper extends IDreamService.Stub {
+    /**
+     * The DreamServiceWrapper is used as a gateway to the system_server, where DreamController
+     * uses it to control the DreamService. It is also used to receive callbacks from the
+     * DreamActivity.
+     */
+    final class DreamServiceWrapper extends IDreamService.Stub {
         @Override
-        public void attach(final IBinder windowToken, final boolean canDoze,
+        public void attach(final IBinder dreamToken, final boolean canDoze,
                 IRemoteCallback started) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    DreamService.this.attach(windowToken, canDoze, started);
-                }
-            });
+            mHandler.post(() -> DreamService.this.attach(dreamToken, canDoze, started));
         }
 
         @Override
         public void detach() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    DreamService.this.detach();
-                }
-            });
+            mHandler.post(DreamService.this::detach);
         }
 
         @Override
         public void wakeUp() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    DreamService.this.wakeUp(true /*fromSystem*/);
-                }
-            });
+            mHandler.post(() -> DreamService.this.wakeUp(true /*fromSystem*/));
+        }
+
+        /** @hide */
+        void onActivityCreated(DreamActivity a) {
+            mActivity = a;
+            onWindowCreated(a.getWindow());
         }
     }
 }
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index d254ffd..6496de3 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -31,12 +31,14 @@
     void setDreamComponents(in ComponentName[] componentNames);
     @UnsupportedAppUsage
     ComponentName[] getDreamComponents();
-    ComponentName getDefaultDreamComponent();
-    void testDream(in ComponentName componentName);
+    ComponentName getDefaultDreamComponentForUser(int userId);
+    void testDream(int userId, in ComponentName componentName);
     @UnsupportedAppUsage
     boolean isDreaming();
     void finishSelf(in IBinder token, boolean immediate);
     void startDozing(in IBinder token, int screenState, int screenBrightness);
     void stopDozing(in IBinder token);
     void forceAmbientDisplayEnabled(boolean enabled);
+    ComponentName[] getDreamComponentsForUser(int userId);
+    void setDreamComponentsForUser(int userId, in ComponentName[] componentNames);
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index e976e18..975e75c 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -184,7 +184,7 @@
     /**
      * Implement this to know when the notification panel is revealed
      *
-     * @param items Number of items on the panel at time of opening
+     * @param items Number of notifications on the panel at time of opening
      */
     public void onPanelRevealed(int items) {
 
diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS
new file mode 100644
index 0000000..2e94be5
--- /dev/null
+++ b/core/java/android/service/notification/OWNERS
@@ -0,0 +1,4 @@
+juliacr@google.com
+beverlyt@google.com
+dsandler@android.com
+pixel@google.com
\ No newline at end of file
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 7b24ba9..9a555c16 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -32,7 +32,7 @@
  * <p>
  * This API is not as convenient or type safe as the standard protobuf
  * classes. If possible, the best recommended library is to use protobuf lite.
- * However, in environements (such as the Android platform itself), a
+ * However, in environments (such as the Android platform itself), a
  * more memory efficient version is necessary.
  *
  * <p>Each write method takes an ID code from the protoc generated classes
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 31fc161..0ab856e 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -554,26 +554,12 @@
      */
     public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
         if (insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0
-                || isBoundsEmpty()) {
+                || (isBoundsEmpty() && mWaterfallInsets.equals(Insets.NONE))) {
             return this;
         }
 
-        Rect safeInsets = new Rect(mSafeInsets);
-
-        // Note: it's not really well defined what happens when the inset is negative, because we
-        // don't know if the safe inset needs to expand in general.
-        if (insetTop > 0 || safeInsets.top > 0) {
-            safeInsets.top = atLeastZero(safeInsets.top - insetTop);
-        }
-        if (insetBottom > 0 || safeInsets.bottom > 0) {
-            safeInsets.bottom = atLeastZero(safeInsets.bottom - insetBottom);
-        }
-        if (insetLeft > 0 || safeInsets.left > 0) {
-            safeInsets.left = atLeastZero(safeInsets.left - insetLeft);
-        }
-        if (insetRight > 0 || safeInsets.right > 0) {
-            safeInsets.right = atLeastZero(safeInsets.right - insetRight);
-        }
+        Rect safeInsets = insetInsets(insetLeft, insetTop, insetRight, insetBottom,
+                new Rect(mSafeInsets));
 
         // If we are not cutting off part of the cutout by insetting it on bottom/right, and we also
         // don't move it around, we can avoid the allocation and copy of the instance.
@@ -581,6 +567,9 @@
             return this;
         }
 
+        Rect waterfallInsets = insetInsets(insetLeft, insetTop, insetRight, insetBottom,
+                mWaterfallInsets.toRect());
+
         Rect[] bounds = mBounds.getRects();
         for (int i = 0; i < bounds.length; ++i) {
             if (!bounds[i].equals(ZERO_RECT)) {
@@ -588,7 +577,27 @@
             }
         }
 
-        return new DisplayCutout(safeInsets, mWaterfallInsets, bounds, false /* copyArguments */);
+        return new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds,
+                false /* copyArguments */);
+    }
+
+    private Rect insetInsets(int insetLeft, int insetTop, int insetRight, int insetBottom,
+            Rect insets) {
+        // Note: it's not really well defined what happens when the inset is negative, because we
+        // don't know if the safe inset needs to expand in general.
+        if (insetTop > 0 || insets.top > 0) {
+            insets.top = atLeastZero(insets.top - insetTop);
+        }
+        if (insetBottom > 0 || insets.bottom > 0) {
+            insets.bottom = atLeastZero(insets.bottom - insetBottom);
+        }
+        if (insetLeft > 0 || insets.left > 0) {
+            insets.left = atLeastZero(insets.left - insetLeft);
+        }
+        if (insetRight > 0 || insets.right > 0) {
+            insets.right = atLeastZero(insets.right - insetRight);
+        }
+        return insets;
     }
 
     /**
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index cb82f16..1cf83a3 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -25,32 +25,8 @@
  * @hide
  */
 interface IPinnedStackController {
-
-    /**
-     * Notifies the controller that the PiP is currently minimized.
-     */
-    oneway void setIsMinimized(boolean isMinimized);
-
     /**
      * @return what WM considers to be the current device rotation.
      */
     int getDisplayRotation();
-
-    /**
-     * Notifies the controller to actually start the PiP animation.
-     * The bounds would be calculated based on the last save reentry fraction internally.
-     * {@param destinationBounds} is the stack bounds of the final PiP window
-     * and {@param sourceRectHint} is the source bounds hint used when entering picture-in-picture,
-     * expect the same bound passed via IPinnedStackListener#onPrepareAnimation.
-     * {@param animationDuration} suggests the animation duration transitioning to PiP window.
-     */
-    void startAnimation(in Rect destinationBounds, in Rect sourceRectHint, int animationDuration);
-
-    /**
-     * Notifies the controller to reset on bounds animation, if there is any.
-     * This could happen when screen rotation is happening and we need to notify the WM to reset
-     * any running bounds animation on the pinned stack.
-     * {@param bounds} here is the final destination bounds.
-     */
-    void resetBoundsAnimation(in Rect bounds);
 }
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index d01c933..596d55a 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -43,8 +43,7 @@
      * pinned stack (the final bounds if animating, the current bounds if not),
      * which may be helpful in calculating dependent animation bounds.
      */
-    void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment,
-            boolean fromShelfAdjustment);
+    void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment);
 
     /**
      * Called when window manager decides to adjust the pinned stack bounds because of the IME, or
@@ -55,12 +54,6 @@
     void onImeVisibilityChanged(boolean imeVisible, int imeHeight);
 
     /**
-     * Called when window manager decides to adjust the minimized state, or when the listener
-     * is first registered to allow the listener to synchronized its state with the controller.
-     */
-    void onMinimizedStateChanged(boolean isMinimized);
-
-    /**
      * Called when the set of actions for the current PiP activity changes, or when the listener
      * is first registered to allow the listener to synchronized its state with the controller.
      */
@@ -99,13 +92,4 @@
      * Called by the window manager when the aspect ratio is reset.
      */
     void onAspectRatioChanged(float aspectRatio);
-
-    /**
-     * Called by the window manager to notify the listener to prepare for PiP animation.
-     * Internally, the target bounds would be calculated from the given {@param aspectRatio}
-     * and {@param bounds}, the saved reentry snap fraction also contributes.
-     * Caller would wait for a IPinnedStackController#startAnimation callback to actually
-     * start the animation, see details in IPinnedStackController.
-     */
-    void onPrepareAnimation(in Rect sourceRectHint, float aspectRatio, in Rect bounds);
 }
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 762366e..530dffb 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -77,13 +77,6 @@
     void hideCurrentInputMethod();
 
     /**
-     * This call is deprecated, use #setDeferCancelUntilNextTransition() instead
-     * TODO(138144750): Remove this method once there are no callers
-     * @deprecated
-     */
-    void setCancelWithDeferredScreenshot(boolean screenshot);
-
-    /**
      * Clean up the screenshot of previous task which was created during recents animation that
      * was cancelled by a stack order change.
      *
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 5f9c480..cb9e746 100644
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -233,6 +233,21 @@
         return mSeq;
     }
 
+    /**
+     * Gets the ID of this event. This is generated when an event is created and preserved until its
+     * last stage. It won't change just because the event crosses process boundary, but should
+     * change when making a copy with modifications.
+     * <p>
+     * To avoid exposing app usage to other processes this ID is generated from a CSPRNG. Therefore
+     * there isn't 100% guarantee on the uniqueness of this ID, though the chance of ID collisions
+     * is considerably low. The rule of thumb is not to rely on the uniqueness for production logic,
+     * but a good source for tracking an event (e.g. logging and profiling).
+     *
+     * @return The ID of this event.
+     * @hide
+     */
+    public abstract int getId();
+
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c91096e..e249c77 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1265,6 +1265,7 @@
 
     private KeyEvent mNext;
 
+    private int mId;
     @UnsupportedAppUsage
     private int mDeviceId;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -1350,9 +1351,9 @@
 
     private static native String nativeKeyCodeToString(int keyCode);
     private static native int nativeKeyCodeFromString(String keyCode);
+    private static native int nativeNextId();
 
-    private KeyEvent() {
-    }
+    private KeyEvent() {}
 
     /**
      * Create a new key event.
@@ -1362,6 +1363,7 @@
      * @param code The key code.
      */
     public KeyEvent(int action, int code) {
+        mId = nativeNextId();
         mAction = action;
         mKeyCode = code;
         mRepeatCount = 0;
@@ -1383,6 +1385,7 @@
      */
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat) {
+        mId = nativeNextId();
         mDownTime = downTime;
         mEventTime = eventTime;
         mAction = action;
@@ -1407,6 +1410,7 @@
      */
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat, int metaState) {
+        mId = nativeNextId();
         mDownTime = downTime;
         mEventTime = eventTime;
         mAction = action;
@@ -1435,6 +1439,7 @@
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat, int metaState,
                     int deviceId, int scancode) {
+        mId = nativeNextId();
         mDownTime = downTime;
         mEventTime = eventTime;
         mAction = action;
@@ -1465,6 +1470,7 @@
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat, int metaState,
                     int deviceId, int scancode, int flags) {
+        mId = nativeNextId();
         mDownTime = downTime;
         mEventTime = eventTime;
         mAction = action;
@@ -1497,6 +1503,7 @@
     public KeyEvent(long downTime, long eventTime, int action,
                     int code, int repeat, int metaState,
                     int deviceId, int scancode, int flags, int source) {
+        mId = nativeNextId();
         mDownTime = downTime;
         mEventTime = eventTime;
         mAction = action;
@@ -1523,6 +1530,7 @@
      * @param flags The flags for this key event
      */
     public KeyEvent(long time, String characters, int deviceId, int flags) {
+        mId = nativeNextId();
         mDownTime = time;
         mEventTime = time;
         mCharacters = characters;
@@ -1539,6 +1547,7 @@
      * Make an exact copy of an existing key event.
      */
     public KeyEvent(KeyEvent origEvent) {
+        mId = origEvent.mId;
         mDownTime = origEvent.mDownTime;
         mEventTime = origEvent.mEventTime;
         mAction = origEvent.mAction;
@@ -1567,6 +1576,7 @@
      */
     @Deprecated
     public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) {
+        mId = nativeNextId();  // Not an exact copy so assign a new ID.
         mDownTime = origEvent.mDownTime;
         mEventTime = eventTime;
         mAction = origEvent.mAction;
@@ -1598,15 +1608,16 @@
     }
 
     /**
-     * Obtains a (potentially recycled) key event.
+     * Obtains a (potentially recycled) key event. Used by native code to create a Java object.
      *
      * @hide
      */
-    public static KeyEvent obtain(long downTime, long eventTime, int action,
+    public static KeyEvent obtain(int id, long downTime, long eventTime, int action,
             int code, int repeat, int metaState,
             int deviceId, int scancode, int flags, int source, int displayId, @Nullable byte[] hmac,
             String characters) {
         KeyEvent ev = obtain();
+        ev.mId = id;
         ev.mDownTime = downTime;
         ev.mEventTime = eventTime;
         ev.mAction = action;
@@ -1628,12 +1639,24 @@
      *
      * @hide
      */
+    public static KeyEvent obtain(long downTime, long eventTime, int action,
+            int code, int repeat, int metaState,
+            int deviceId, int scanCode, int flags, int source, int displayId, String characters) {
+        return obtain(nativeNextId(), downTime, eventTime, action, code, repeat, metaState,
+                deviceId, scanCode, flags, source, displayId, null /* hmac */, characters);
+    }
+
+    /**
+     * Obtains a (potentially recycled) key event.
+     *
+     * @hide
+     */
     @UnsupportedAppUsage
     public static KeyEvent obtain(long downTime, long eventTime, int action,
             int code, int repeat, int metaState,
             int deviceId, int scancode, int flags, int source, String characters) {
         return obtain(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode,
-                flags, source, INVALID_DISPLAY, null /* hmac */, characters);
+                flags, source, INVALID_DISPLAY, characters);
     }
 
     /**
@@ -1645,6 +1668,7 @@
      */
     public static KeyEvent obtain(KeyEvent other) {
         KeyEvent ev = obtain();
+        ev.mId = other.mId;
         ev.mDownTime = other.mDownTime;
         ev.mEventTime = other.mEventTime;
         ev.mAction = other.mAction;
@@ -1695,6 +1719,12 @@
         // Do nothing.
     }
 
+    /** @hide */
+    @Override
+    public int getId() {
+        return mId;
+    }
+
     /**
      * Create a new key event that is the same as the given one, but whose
      * event time and repeat count are replaced with the given value.
@@ -1723,6 +1753,7 @@
     public static KeyEvent changeTimeRepeat(KeyEvent event, long eventTime,
             int newRepeat, int newFlags) {
         KeyEvent ret = new KeyEvent(event);
+        ret.mId = nativeNextId();  // Not an exact copy so assign a new ID.
         ret.mEventTime = eventTime;
         ret.mRepeatCount = newRepeat;
         ret.mFlags = newFlags;
@@ -1736,6 +1767,7 @@
      * @param action The new action code of the event.
      */
     private KeyEvent(KeyEvent origEvent, int action) {
+        mId = nativeNextId();  // Not an exact copy so assign a new ID.
         mDownTime = origEvent.mDownTime;
         mEventTime = origEvent.mEventTime;
         mAction = action;
@@ -1772,6 +1804,7 @@
      */
     public static KeyEvent changeFlags(KeyEvent event, int flags) {
         event = new KeyEvent(event);
+        event.mId = nativeNextId();  // Not an exact copy so assign a new ID.
         event.mFlags = flags;
         return event;
     }
@@ -1941,7 +1974,6 @@
     /** @hide */
     public static final boolean isWakeKey(int keyCode) {
         switch (keyCode) {
-            case KeyEvent.KEYCODE_BACK:
             case KeyEvent.KEYCODE_CAMERA:
             case KeyEvent.KEYCODE_MENU:
             case KeyEvent.KEYCODE_PAIRING:
@@ -3096,6 +3128,7 @@
     }
 
     private KeyEvent(Parcel in) {
+        mId = in.readInt();
         mDeviceId = in.readInt();
         mSource = in.readInt();
         mDisplayId = in.readInt();
@@ -3115,6 +3148,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(PARCEL_TOKEN_KEY_EVENT);
 
+        out.writeInt(mId);
         out.writeInt(mDeviceId);
         out.writeInt(mSource);
         out.writeInt(mDisplayId);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 0068476..19eff72 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1550,6 +1550,8 @@
     private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
             boolean keepHistory);
     @CriticalNative
+    private static native int nativeGetId(long nativePtr);
+    @CriticalNative
     private static native int nativeGetDeviceId(long nativePtr);
     @CriticalNative
     private static native int nativeGetSource(long nativePtr);
@@ -2024,6 +2026,12 @@
         }
     }
 
+    /** @hide */
+    @Override
+    public int getId() {
+        return nativeGetId(mNativePtr);
+    }
+
     /** {@inheritDoc} */
     @Override
     public final int getDeviceId() {
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 680a878..4badede 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -111,6 +111,7 @@
             @NonNull WindowlessWindowManager wwm) {
         mWm = wwm;
         mViewRoot = new ViewRootImpl(c, d, mWm);
+        mViewRoot.forceDisableBLAST();
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
@@ -135,6 +136,7 @@
         mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
                 mSurfaceControl, hostToken);
         mViewRoot = new ViewRootImpl(context, display, mWm);
+        mViewRoot.forceDisableBLAST();
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 73707ca..fb7c04a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -392,7 +392,7 @@
                  * This gets called on a RenderThread worker thread, so members accessed here must
                  * be protected by a lock.
                  */
-                final boolean useBLAST = WindowManagerGlobal.useBLAST();
+                final boolean useBLAST = viewRoot.useBLAST();
                 viewRoot.registerRtFrameCallback(frame -> {
                     try {
                         final SurfaceControl.Transaction t = useBLAST ?
@@ -930,7 +930,7 @@
                                     mSurfaceHeight);
                         }
                     } else if ((layoutSizeChanged || positionChanged) &&
-                            WindowManagerGlobal.useBLAST()) {
+                            viewRoot.useBLAST()) {
                         viewRoot.setUseBLASTSyncTransaction();
                     }
                     mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
@@ -1132,9 +1132,8 @@
 
     private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
             Rect position, long frameNumber) {
-        if (frameNumber > 0 && !WindowManagerGlobal.useBLAST()) {
-            final ViewRootImpl viewRoot = getViewRootImpl();
-
+        final ViewRootImpl viewRoot = getViewRootImpl();
+        if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) {
             t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
                     frameNumber);
         }
@@ -1150,8 +1149,8 @@
     }
 
     private void setParentSpaceRectangle(Rect position, long frameNumber) {
-        final boolean useBLAST = WindowManagerGlobal.useBLAST();
         final ViewRootImpl viewRoot = getViewRootImpl();
+        final boolean useBLAST = viewRoot.useBLAST();
         final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
             mRtTransaction;
 
@@ -1211,7 +1210,8 @@
 
         @Override
         public void positionLost(long frameNumber) {
-            boolean useBLAST = WindowManagerGlobal.useBLAST();
+            final ViewRootImpl viewRoot = getViewRootImpl();
+            boolean useBLAST = viewRoot != null && viewRoot.useBLAST();
             if (DEBUG) {
                 Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
                         System.identityHashCode(this), frameNumber));
@@ -1222,8 +1222,6 @@
                 return;
             }
 
-            final ViewRootImpl viewRoot = getViewRootImpl();
-
             final SurfaceControl.Transaction t = useBLAST ?
                 (viewRoot != null ? viewRoot.getBLASTSyncTransaction() : mRtTransaction) :
                 mRtTransaction;
@@ -1646,7 +1644,7 @@
     }
 
     private void updateScreenMatrixForEmbeddedHierarchy() {
-        getBoundsOnScreen(mTmpRect, true);
+        getBoundsOnScreen(mTmpRect);
         mTmpMatrix.reset();
         mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top);
         mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 148b327..918b5a3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -322,7 +322,7 @@
      */
     private boolean mForceNextConfigUpdate;
 
-    private final boolean mUseBLASTAdapter;
+    private boolean mUseBLASTAdapter;
 
     /**
      * Signals that compatibility booleans have been initialized according to
@@ -5379,7 +5379,8 @@
             if (mTracePrefix == null) {
                 mTracePrefix = getClass().getSimpleName();
             }
-            Trace.traceBegin(traceTag, mTracePrefix + " seq#=" + q.mEvent.getSequenceNumber());
+            Trace.traceBegin(traceTag, mTracePrefix + " id=0x"
+                    + Integer.toHexString(q.mEvent.getId()));
         }
     }
 
@@ -7986,12 +7987,13 @@
 
     private void deliverInputEvent(QueuedInputEvent q) {
         Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
-                q.mEvent.getSequenceNumber());
+                q.mEvent.getId());
 
         if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent src=0x"
                     + Integer.toHexString(q.mEvent.getSource()) + " eventTimeNano="
-                    + q.mEvent.getEventTimeNano() + " seq#=" + q.mEvent.getSequenceNumber());
+                    + q.mEvent.getEventTimeNano() + " id=0x"
+                    + Integer.toHexString(q.mEvent.getId()));
         }
         try {
             if (mInputEventConsistencyVerifier != null) {
@@ -8032,7 +8034,7 @@
 
     private void finishInputEvent(QueuedInputEvent q) {
         Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
-                q.mEvent.getSequenceNumber());
+                q.mEvent.getId());
 
         if (q.mReceiver != null) {
             boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
@@ -9639,4 +9641,16 @@
     public void onDescendantUnbufferedRequested() {
         mUnbufferedInputSource = mView.mUnbufferedInputSource;
     }
+
+    /**
+     * Force disabling use of the BLAST adapter regardless of the system
+     * flag. Needs to be called before addView.
+     */
+    void forceDisableBLAST() {
+        mUseBLASTAdapter = false;
+    }
+
+    boolean useBLAST() {
+        return mUseBLASTAdapter;
+    }
 }
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index d40f505..6435b42 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,6 +16,11 @@
 
 package android.view;
 
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+
 import android.annotation.NonNull;
 import android.app.ResourcesManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -67,10 +72,6 @@
 
     private IBinder mDefaultToken;
 
-    private boolean mIsViewAdded;
-    private View mLastView;
-    private WindowManager.LayoutParams mLastParams;
-
     public WindowManagerImpl(Context context) {
         this(context, null);
     }
@@ -102,9 +103,6 @@
     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
         applyDefaultToken(params);
         mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow);
-        mIsViewAdded = true;
-        mLastView = view;
-        mLastParams = (WindowManager.LayoutParams) params;
     }
 
     @Override
@@ -247,21 +245,19 @@
     }
 
     private WindowInsets computeWindowInsets() {
-        // TODO(window-context): This can only be properly implemented
+        // TODO(b/118118435): This can only be properly implemented
         //  once we flip the new insets mode flag.
-        if (mParentWindow != null) {
-            if (mParentWindow.getDecorView().isAttachedToWindow()) {
-                return mParentWindow.getDecorView().getViewRootImpl()
-                        .getWindowInsets(true /* forceConstruct */);
-            }
-            return getWindowInsetsFromServer(mParentWindow.getAttributes());
-        }
-        if (mIsViewAdded) {
-            return mLastView.getViewRootImpl().getWindowInsets(true /* forceConstruct */);
-        } else {
-            return getWindowInsetsFromServer(new WindowManager.LayoutParams());
-        }
+        // Initialize params which used for obtaining all system insets.
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+        params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+        params.token = (mParentWindow != null) ? mParentWindow.getContext().getActivityToken()
+                : mContext.getActivityToken();
+        params.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+        params.setFitInsetsTypes(0);
+        params.setFitInsetsSides(0);
 
+        return getWindowInsetsFromServer(params);
     }
 
     private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs) {
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index 8caf5b7..ab5a06e 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.NonNull;
+import android.graphics.Point;
 import android.util.Size;
 
 /**
@@ -40,6 +41,30 @@
 
     /**
      * Returns the size of the window.
+     * <p>
+     * <b>Note that this reports a different size than {@link Display#getSize(Point)}.</b>
+     * This method reports the window size including all system bars area, while
+     * {@link Display#getSize(Point)} reports the area excluding navigation bars and display cutout
+     * areas. The value reported by {@link Display#getSize(Point)} can be obtained by using:
+     * <pre class="prettyprint">
+     * final WindowMetrics metrics = windowManager.getCurrentMetrics();
+     * // Gets all excluding insets
+     * final WindowInsets windowInsets = metrics.getWindowInsets();
+     * Insets insets = windowInsets.getInsets(WindowInsets.Type.navigationBars());
+     * final DisplayCutout cutout = windowInsets.getCutout();
+     * if (cutout != null) {
+     *     final Insets cutoutSafeInsets = Insets.of(cutout.getSafeInsetsLeft(), ...);
+     *     insets = insets.max(insets, cutoutSafeInsets);
+     * }
+     *
+     * int insetsWidth = insets.right + insets.left;
+     * int insetsHeight = insets.top + insets.bottom;
+     *
+     * // Legacy size that Display#getSize reports
+     * final Size legacySize = new Size(metrics.getWidth() - insetsWidth,
+     *         metrics.getHeight() - insetsHeight);
+     * </pre>
+     * </p>
      *
      * @return window size in pixel.
      */
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 5700dda..e50da40 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -22,7 +22,9 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.LocaleList;
+import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.Display;
 import android.view.inline.InlinePresentationSpec;
 
 import com.android.internal.util.DataClass;
@@ -67,7 +69,11 @@
      */
     private @NonNull LocaleList mSupportedLocales;
 
-    // TODO(b/149609075): the generated code needs to be manually fixed due to the bug.
+    /**
+     * The extras state propagated from the IME to pass extra data.
+     */
+    private @Nullable Bundle mExtras;
+
     /**
      * The host input token of the IME that made the request. This will be set by the system for
      * safety reasons.
@@ -77,9 +83,12 @@
     private @Nullable IBinder mHostInputToken;
 
     /**
-     * The extras state propagated from the IME to pass extra data.
+     * The host display id of the IME that made the request. This will be set by the system for
+     * safety reasons.
+     *
+     * @hide
      */
-    private @Nullable Bundle mExtras;
+    private int mHostDisplayId;
 
     /**
      * @hide
@@ -89,6 +98,24 @@
         mHostInputToken = hostInputToken;
     }
 
+    // TODO(b/149609075): remove once IBinder parcelling is natively supported
+    private void parcelHostInputToken(@NonNull Parcel parcel, int flags) {
+        parcel.writeStrongBinder(mHostInputToken);
+    }
+
+    // TODO(b/149609075): remove once IBinder parcelling is natively supported
+    private @Nullable IBinder unparcelHostInputToken(Parcel parcel) {
+        return parcel.readStrongBinder();
+    }
+
+    /**
+     * @hide
+     * @see {@link #mHostDisplayId}.
+     */
+    public void setHostDisplayId(int hostDisplayId) {
+        mHostDisplayId = hostDisplayId;
+    }
+
     private void onConstructed() {
         Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
     }
@@ -111,10 +138,17 @@
     }
 
     @Nullable
+    private static int defaultHostDisplayId() {
+        return Display.INVALID_DISPLAY;
+    }
+
+    @Nullable
     private static Bundle defaultExtras() {
         return null;
     }
 
+
+
     /** @hide */
     abstract static class BaseBuilder {
         abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value);
@@ -122,6 +156,8 @@
         abstract Builder setHostPackageName(@Nullable String value);
 
         abstract Builder setHostInputToken(IBinder hostInputToken);
+
+        abstract Builder setHostDisplayId(int value);
     }
 
 
@@ -145,8 +181,9 @@
             @NonNull List<InlinePresentationSpec> presentationSpecs,
             @NonNull String hostPackageName,
             @NonNull LocaleList supportedLocales,
+            @Nullable Bundle extras,
             @Nullable IBinder hostInputToken,
-            @Nullable Bundle extras) {
+            int hostDisplayId) {
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mPresentationSpecs = presentationSpecs;
         com.android.internal.util.AnnotationValidations.validate(
@@ -157,8 +194,9 @@
         this.mSupportedLocales = supportedLocales;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mSupportedLocales);
-        this.mHostInputToken = hostInputToken;
         this.mExtras = extras;
+        this.mHostInputToken = hostInputToken;
+        this.mHostDisplayId = hostDisplayId;
 
         onConstructed();
     }
@@ -202,6 +240,14 @@
     }
 
     /**
+     * The extras state propagated from the IME to pass extra data.
+     */
+    @DataClass.Generated.Member
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
      * The host input token of the IME that made the request. This will be set by the system for
      * safety reasons.
      *
@@ -213,11 +259,14 @@
     }
 
     /**
-     * The extras state propagated from the IME to pass extra data.
+     * The host display id of the IME that made the request. This will be set by the system for
+     * safety reasons.
+     *
+     * @hide
      */
     @DataClass.Generated.Member
-    public @Nullable Bundle getExtras() {
-        return mExtras;
+    public int getHostDisplayId() {
+        return mHostDisplayId;
     }
 
     @Override
@@ -231,8 +280,9 @@
                 "presentationSpecs = " + mPresentationSpecs + ", " +
                 "hostPackageName = " + mHostPackageName + ", " +
                 "supportedLocales = " + mSupportedLocales + ", " +
+                "extras = " + mExtras + ", " +
                 "hostInputToken = " + mHostInputToken + ", " +
-                "extras = " + mExtras +
+                "hostDisplayId = " + mHostDisplayId +
         " }";
     }
 
@@ -253,8 +303,9 @@
                 && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs)
                 && java.util.Objects.equals(mHostPackageName, that.mHostPackageName)
                 && java.util.Objects.equals(mSupportedLocales, that.mSupportedLocales)
+                && java.util.Objects.equals(mExtras, that.mExtras)
                 && java.util.Objects.equals(mHostInputToken, that.mHostInputToken)
-                && java.util.Objects.equals(mExtras, that.mExtras);
+                && mHostDisplayId == that.mHostDisplayId;
     }
 
     @Override
@@ -268,27 +319,29 @@
         _hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs);
         _hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName);
         _hash = 31 * _hash + java.util.Objects.hashCode(mSupportedLocales);
-        _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
         _hash = 31 * _hash + java.util.Objects.hashCode(mExtras);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
+        _hash = 31 * _hash + mHostDisplayId;
         return _hash;
     }
 
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mHostInputToken != null) flg |= 0x10;
-        if (mExtras != null) flg |= 0x20;
+        if (mExtras != null) flg |= 0x10;
+        if (mHostInputToken != null) flg |= 0x20;
         dest.writeByte(flg);
         dest.writeInt(mMaxSuggestionCount);
         dest.writeParcelableList(mPresentationSpecs, flags);
         dest.writeString(mHostPackageName);
         dest.writeTypedObject(mSupportedLocales, flags);
-        if (mHostInputToken != null) dest.writeStrongBinder(mHostInputToken);
         if (mExtras != null) dest.writeBundle(mExtras);
+        parcelHostInputToken(dest, flags);
+        dest.writeInt(mHostDisplayId);
     }
 
     @Override
@@ -298,7 +351,7 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ InlineSuggestionsRequest(@NonNull android.os.Parcel in) {
+    /* package-private */ InlineSuggestionsRequest(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
@@ -308,8 +361,9 @@
         in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader());
         String hostPackageName = in.readString();
         LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR);
-        IBinder hostInputToken = (flg & 0x10) == 0 ? null : in.readStrongBinder();
-        Bundle extras = (flg & 0x20) == 0 ? null : in.readBundle();
+        Bundle extras = (flg & 0x10) == 0 ? null : in.readBundle();
+        IBinder hostInputToken = unparcelHostInputToken(in);
+        int hostDisplayId = in.readInt();
 
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mPresentationSpecs = presentationSpecs;
@@ -321,8 +375,9 @@
         this.mSupportedLocales = supportedLocales;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mSupportedLocales);
-        this.mHostInputToken = hostInputToken;
         this.mExtras = extras;
+        this.mHostInputToken = hostInputToken;
+        this.mHostDisplayId = hostDisplayId;
 
         onConstructed();
     }
@@ -336,7 +391,7 @@
         }
 
         @Override
-        public InlineSuggestionsRequest createFromParcel(@NonNull android.os.Parcel in) {
+        public InlineSuggestionsRequest createFromParcel(@NonNull Parcel in) {
             return new InlineSuggestionsRequest(in);
         }
     };
@@ -352,8 +407,9 @@
         private @NonNull List<InlinePresentationSpec> mPresentationSpecs;
         private @NonNull String mHostPackageName;
         private @NonNull LocaleList mSupportedLocales;
-        private @Nullable IBinder mHostInputToken;
         private @Nullable Bundle mExtras;
+        private @Nullable IBinder mHostInputToken;
+        private int mHostDisplayId;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -436,6 +492,17 @@
         }
 
         /**
+         * The extras state propagated from the IME to pass extra data.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setExtras(@Nullable Bundle value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mExtras = value;
+            return this;
+        }
+
+        /**
          * The host input token of the IME that made the request. This will be set by the system for
          * safety reasons.
          *
@@ -445,26 +512,30 @@
         @Override
         @NonNull Builder setHostInputToken(@Nullable IBinder value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x10;
+            mBuilderFieldsSet |= 0x20;
             mHostInputToken = value;
             return this;
         }
 
         /**
-         * The extras state propagated from the IME to pass extra data.
+         * The host display id of the IME that made the request. This will be set by the system for
+         * safety reasons.
+         *
+         * @hide
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setExtras(@Nullable Bundle value) {
+        @Override
+        @NonNull Builder setHostDisplayId(int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x20;
-            mExtras = value;
+            mBuilderFieldsSet |= 0x40;
+            mHostDisplayId = value;
             return this;
         }
 
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull InlineSuggestionsRequest build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x40; // Mark builder used
+            mBuilderFieldsSet |= 0x80; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -476,23 +547,27 @@
                 mSupportedLocales = defaultSupportedLocales();
             }
             if ((mBuilderFieldsSet & 0x10) == 0) {
-                mHostInputToken = defaultHostInputToken();
+                mExtras = defaultExtras();
             }
             if ((mBuilderFieldsSet & 0x20) == 0) {
-                mExtras = defaultExtras();
+                mHostInputToken = defaultHostInputToken();
+            }
+            if ((mBuilderFieldsSet & 0x40) == 0) {
+                mHostDisplayId = defaultHostDisplayId();
             }
             InlineSuggestionsRequest o = new InlineSuggestionsRequest(
                     mMaxSuggestionCount,
                     mPresentationSpecs,
                     mHostPackageName,
                     mSupportedLocales,
+                    mExtras,
                     mHostInputToken,
-                    mExtras);
+                    mHostDisplayId);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x40) != 0) {
+            if ((mBuilderFieldsSet & 0x80) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -500,10 +575,10 @@
     }
 
     @DataClass.Generated(
-            time = 1581747892762L,
+            time = 1582339908980L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate @android.annotation.Nullable android.os.Bundle mExtras\npublic  void setHostInputToken(android.os.IBinder)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic  void setHostInputToken(android.os.IBinder)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index bb40465..5cdcab0 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -412,8 +412,13 @@
      * Class to wrap TextToSpeech for shortcut dialog spoken feedback.
      */
     private class TtsPrompt implements TextToSpeech.OnInitListener {
+        private static final int RETRY_MILLIS = 1000;
+
         private final CharSequence mText;
+
+        private int mRetryCount = 3;
         private boolean mDismiss;
+        private boolean mLanguageReady = false;
         private TextToSpeech mTts;
 
         TtsPrompt(String serviceName) {
@@ -437,17 +442,15 @@
                 playNotificationTone();
                 return;
             }
-            mHandler.sendMessage(PooledLambda.obtainMessage(TtsPrompt::play, this));
+            mHandler.sendMessage(PooledLambda.obtainMessage(
+                    TtsPrompt::waitForTtsReady, this));
         }
 
         private void play() {
             if (mDismiss) {
                 return;
             }
-            int status = TextToSpeech.ERROR;
-            if (setLanguage(Locale.getDefault())) {
-                status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null);
-            }
+            final int status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null);
             if (status != TextToSpeech.SUCCESS) {
                 Slog.d(TAG, "Tts play fail");
                 playNotificationTone();
@@ -455,21 +458,42 @@
         }
 
         /**
-         * @return false if tts language is not available
+         * Waiting for tts is ready to speak. Trying again if tts language pack is not available
+         * or tts voice data is not installed yet.
          */
-        private boolean setLanguage(final Locale locale) {
-            int status = mTts.isLanguageAvailable(locale);
-            if (status == TextToSpeech.LANG_MISSING_DATA
-                    || status == TextToSpeech.LANG_NOT_SUPPORTED) {
-                return false;
+        private void waitForTtsReady() {
+            if (mDismiss) {
+                return;
             }
-            mTts.setLanguage(locale);
-            Voice voice = mTts.getVoice();
-            if (voice == null || (voice.getFeatures() != null && voice.getFeatures()
-                    .contains(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED))) {
-                return false;
+            if (!mLanguageReady) {
+                final int status = mTts.setLanguage(Locale.getDefault());
+                // True if language is available and TTS#loadVoice has called once
+                // that trigger TTS service to start initialization.
+                mLanguageReady = status != TextToSpeech.LANG_MISSING_DATA
+                    && status != TextToSpeech.LANG_NOT_SUPPORTED;
             }
-            return true;
+            if (mLanguageReady) {
+                final Voice voice = mTts.getVoice();
+                final boolean voiceDataInstalled = voice != null
+                        && voice.getFeatures() != null
+                        && !voice.getFeatures().contains(
+                                TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED);
+                if (voiceDataInstalled) {
+                    mHandler.sendMessage(PooledLambda.obtainMessage(
+                            TtsPrompt::play, this));
+                    return;
+                }
+            }
+
+            if (mRetryCount == 0) {
+                Slog.d(TAG, "Tts not ready to speak.");
+                playNotificationTone();
+                return;
+            }
+            // Retry if TTS service not ready yet.
+            mRetryCount -= 1;
+            mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
+                    TtsPrompt::waitForTtsReady, this), RETRY_MILLIS);
         }
     }
 
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index 382a254b..3876976 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.inputmethod;
 
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 
@@ -25,6 +27,7 @@
  * Provides useful methods for debugging.
  */
 public final class InputMethodDebug {
+
     /**
      * Not intended to be instantiated.
      */
@@ -174,4 +177,71 @@
 
         return joiner.setEmptyValue("(none)").toString();
     }
+
+
+    /**
+     * Converts {@link SoftInputShowHideReason} to {@link String} for history dump.
+     */
+    public static String softInputDisplayReasonToString(@SoftInputShowHideReason int reason) {
+        switch (reason) {
+            case SoftInputShowHideReason.SHOW_SOFT_INPUT:
+                return "SHOW_SOFT_INPUT";
+            case SoftInputShowHideReason.ATTACH_NEW_INPUT:
+                return "ATTACH_NEW_INPUT";
+            case SoftInputShowHideReason.SHOW_MY_SOFT_INPUT:
+                return "SHOW_MY_SOFT_INPUT";
+            case SoftInputShowHideReason.HIDE_SOFT_INPUT:
+                return "HIDE_SOFT_INPUT";
+            case SoftInputShowHideReason.HIDE_MY_SOFT_INPUT:
+                return "HIDE_MY_SOFT_INPUT";
+            case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV:
+                return "SHOW_AUTO_EDITOR_FORWARD_NAV";
+            case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV:
+                return "SHOW_STATE_VISIBLE_FORWARD_NAV";
+            case SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE:
+                return "SHOW_STATE_ALWAYS_VISIBLE";
+            case SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE:
+                return "SHOW_SETTINGS_ON_CHANGE";
+            case SoftInputShowHideReason.HIDE_SWITCH_USER:
+                return "HIDE_SWITCH_USER";
+            case SoftInputShowHideReason.HIDE_INVALID_USER:
+                return "HIDE_INVALID_USER";
+            case SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW:
+                return "HIDE_UNSPECIFIED_WINDOW";
+            case SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV:
+                return "HIDE_STATE_HIDDEN_FORWARD_NAV";
+            case SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE:
+                return "HIDE_ALWAYS_HIDDEN_STATE";
+            case SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND:
+                return "HIDE_RESET_SHELL_COMMAND";
+            case SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE:
+                return "HIDE_SETTINGS_ON_CHANGE";
+            case SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME:
+                return "HIDE_POWER_BUTTON_GO_HOME";
+            case SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED:
+                return "HIDE_DOCKED_STACK_ATTACHED";
+            case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION:
+                return "HIDE_RECENTS_ANIMATION";
+            default:
+                return "Unknown=" + reason;
+        }
+    }
+
+    /**
+     * Return a fixed size string of the object.
+     * TODO(b/141738570): Take & return with StringBuilder to make more memory efficient.
+     */
+    @NonNull
+    @AnyThread
+    public static String objToString(Object obj) {
+        if (obj == null) {
+            return "null";
+        }
+        StringBuilder sb = new StringBuilder(64);
+        sb.setLength(0);
+        sb.append(obj.getClass().getName());
+        sb.append("@");
+        sb.append(Integer.toHexString(obj.hashCode()));
+        return sb.toString();
+    }
 }
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
new file mode 100644
index 0000000..79397b8
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2020 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.inputmethod;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.view.WindowManager.LayoutParams;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Describes the reason why Soft input window visible / hidden.
+ */
+@Retention(SOURCE)
+@IntDef(value = {
+        SoftInputShowHideReason.SHOW_SOFT_INPUT,
+        SoftInputShowHideReason.ATTACH_NEW_INPUT,
+        SoftInputShowHideReason.SHOW_MY_SOFT_INPUT,
+        SoftInputShowHideReason.HIDE_SOFT_INPUT,
+        SoftInputShowHideReason.HIDE_MY_SOFT_INPUT,
+        SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV,
+        SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV,
+        SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE,
+        SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE,
+        SoftInputShowHideReason.HIDE_SWITCH_USER,
+        SoftInputShowHideReason.HIDE_INVALID_USER,
+        SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW,
+        SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV,
+        SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE,
+        SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND,
+        SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE,
+        SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME,
+        SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED,
+        SoftInputShowHideReason.HIDE_RECENTS_ANIMATION})
+public @interface SoftInputShowHideReason {
+    /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
+    int SHOW_SOFT_INPUT = 0;
+
+    /** Show soft input when {@code InputMethodManagerService#attachNewInputLocked} called. */
+    int ATTACH_NEW_INPUT = 1;
+
+    /** Show soft input by {@code InputMethodManagerService#showMySoftInput}. */
+    int SHOW_MY_SOFT_INPUT = 2;
+
+    /**
+     * Hide soft input by
+     * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow}.
+     */
+    int HIDE_SOFT_INPUT = 3;
+
+    /** Hide soft input by {@code InputMethodManagerService#hideMySoftInput}. */
+    int HIDE_MY_SOFT_INPUT = 4;
+
+    /**
+     * Show soft input when navigated forward to the window (with
+     * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}} which the focused view is text
+     * editor and system will auto-show the IME when the window can resize or running on a large
+     * screen.
+     */
+    int SHOW_AUTO_EDITOR_FORWARD_NAV = 5;
+
+    /**
+     * Show soft input when navigated forward to the window with
+     * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and
+     * {@link LayoutParams#SOFT_INPUT_STATE_VISIBLE}.
+     */
+    int SHOW_STATE_VISIBLE_FORWARD_NAV = 6;
+
+    /**
+     * Show soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE}.
+     */
+    int SHOW_STATE_ALWAYS_VISIBLE = 7;
+
+    /**
+     * Show soft input during {@code InputMethodManagerService} receive changes from
+     * {@code SettingsProvider}.
+     */
+    int SHOW_SETTINGS_ON_CHANGE = 8;
+
+    /** Hide soft input during switching user. */
+    int HIDE_SWITCH_USER = 9;
+
+    /** Hide soft input when the user is invalid. */
+    int HIDE_INVALID_USER = 10;
+
+    /**
+     * Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_UNSPECIFIED} which
+     * the focused view is not text editor.
+     */
+    int HIDE_UNSPECIFIED_WINDOW = 11;
+
+    /**
+     * Hide soft input when navigated forward to the window with
+     * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and
+     * {@link LayoutParams#SOFT_INPUT_STATE_HIDDEN}.
+     */
+    int HIDE_STATE_HIDDEN_FORWARD_NAV = 12;
+
+    /**
+     * Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}.
+     */
+    int HIDE_ALWAYS_HIDDEN_STATE = 13;
+
+    /** Hide soft input when "adb shell ime <command>" called. */
+    int HIDE_RESET_SHELL_COMMAND = 14;
+
+    /**
+     * Hide soft input during {@code InputMethodManagerService} receive changes from
+     * {@code SettingsProvider}.
+     */
+    int HIDE_SETTINGS_ON_CHANGE = 15;
+
+    /**
+     * Hide soft input from {@link com.android.server.policy.PhoneWindowManager} when setting
+     * {@link com.android.internal.R.integer#config_shortPressOnPowerBehavior} in config.xml as
+     * dismiss IME.
+     */
+    int HIDE_POWER_BUTTON_GO_HOME = 16;
+
+    /** Hide soft input when attaching docked stack. */
+    int HIDE_DOCKED_STACK_ATTACHED = 17;
+
+    /**
+     * Hide soft input when {@link com.android.server.wm.RecentsAnimationController} starts
+     * intercept touch from app window.
+     */
+    int HIDE_RECENTS_ANIMATION = 18;
+}
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 84acf9a..0a2b1d4 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -114,9 +114,9 @@
 
     uint32_t publishedSeq = mNextPublishedSeq++;
     status_t status =
-            mInputPublisher.publishKeyEvent(publishedSeq, event->getDeviceId(), event->getSource(),
-                                            event->getDisplayId(), event->getHmac(),
-                                            event->getAction(), event->getFlags(),
+            mInputPublisher.publishKeyEvent(publishedSeq, event->getId(), event->getDeviceId(),
+                                            event->getSource(), event->getDisplayId(),
+                                            event->getHmac(), event->getAction(), event->getFlags(),
                                             event->getKeyCode(), event->getScanCode(),
                                             event->getMetaState(), event->getRepeatCount(),
                                             event->getDownTime(), event->getEventTime());
@@ -138,12 +138,12 @@
     for (size_t i = 0; i <= event->getHistorySize(); i++) {
         publishedSeq = mNextPublishedSeq++;
         status_t status =
-                mInputPublisher.publishMotionEvent(publishedSeq, event->getDeviceId(),
-                                                   event->getSource(), event->getDisplayId(),
-                                                   event->getHmac(), event->getAction(),
-                                                   event->getActionButton(), event->getFlags(),
-                                                   event->getEdgeFlags(), event->getMetaState(),
-                                                   event->getButtonState(),
+                mInputPublisher.publishMotionEvent(publishedSeq, event->getId(),
+                                                   event->getDeviceId(), event->getSource(),
+                                                   event->getDisplayId(), event->getHmac(),
+                                                   event->getAction(), event->getActionButton(),
+                                                   event->getFlags(), event->getEdgeFlags(),
+                                                   event->getMetaState(), event->getButtonState(),
                                                    event->getClassification(), event->getXScale(),
                                                    event->getYScale(), event->getXOffset(),
                                                    event->getYOffset(), event->getXPrecision(),
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index bbe563e..54567e5 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -75,6 +75,7 @@
     jmethodID obtain;
     jmethodID recycle;
 
+    jfieldID mId;
     jfieldID mDeviceId;
     jfieldID mSource;
     jfieldID mDisplayId;
@@ -96,6 +97,7 @@
     ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event->getHmac());
     jobject eventObj =
             env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
+                                        event->getId(),
                                         nanoseconds_to_milliseconds(event->getDownTime()),
                                         nanoseconds_to_milliseconds(event->getEventTime()),
                                         event->getAction(), event->getKeyCode(),
@@ -114,6 +116,7 @@
 
 status_t android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj,
         KeyEvent* event) {
+    jint id = env->GetIntField(eventObj, gKeyEventClassInfo.mId);
     jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
     jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
     jint displayId = env->GetIntField(eventObj, gKeyEventClassInfo.mDisplayId);
@@ -131,7 +134,7 @@
     jlong downTime = env->GetLongField(eventObj, gKeyEventClassInfo.mDownTime);
     jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime);
 
-    event->initialize(deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode,
+    event->initialize(id, deviceId, source, displayId, *hmac, action, flags, keyCode, scanCode,
                       metaState, repeatCount, milliseconds_to_nanoseconds(downTime),
                       milliseconds_to_nanoseconds(eventTime));
     return OK;
@@ -159,14 +162,18 @@
     return KeyEvent::getKeyCodeFromLabel(keyLabel.c_str());
 }
 
+static jint android_view_KeyEvent_nativeNextId() {
+    return static_cast<jint>(InputEvent::nextId());
+}
 
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod g_methods[] = {
-    { "nativeKeyCodeToString", "(I)Ljava/lang/String;",
-        (void*)android_view_KeyEvent_nativeKeyCodeToString},
-    { "nativeKeyCodeFromString", "(Ljava/lang/String;)I",
-        (void*)android_view_KeyEvent_nativeKeyCodeFromString},
+        {"nativeKeyCodeToString", "(I)Ljava/lang/String;",
+         (void*)android_view_KeyEvent_nativeKeyCodeToString},
+        {"nativeKeyCodeFromString", "(Ljava/lang/String;)I",
+         (void*)android_view_KeyEvent_nativeKeyCodeFromString},
+        {"nativeNextId", "()I", (void*)android_view_KeyEvent_nativeNextId},
 };
 
 int register_android_view_KeyEvent(JNIEnv* env) {
@@ -175,10 +182,11 @@
 
     gKeyEventClassInfo.obtain =
             GetStaticMethodIDOrDie(env, gKeyEventClassInfo.clazz, "obtain",
-                                   "(JJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;");
+                                   "(IJJIIIIIIIII[BLjava/lang/String;)Landroid/view/KeyEvent;");
     gKeyEventClassInfo.recycle = GetMethodIDOrDie(env, gKeyEventClassInfo.clazz,
             "recycle", "()V");
 
+    gKeyEventClassInfo.mId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mId", "I");
     gKeyEventClassInfo.mDeviceId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDeviceId", "I");
     gKeyEventClassInfo.mSource = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mSource", "I");
     gKeyEventClassInfo.mDisplayId = GetFieldIDOrDie(env, gKeyEventClassInfo.clazz, "mDisplayId",
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 3335fb2..9816d71 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -369,9 +369,10 @@
         env->DeleteLocalRef(pointerCoordsObj);
     }
 
-    event->initialize(deviceId, source, displayId, INVALID_HMAC, action, 0, flags, edgeFlags,
-                      metaState, buttonState, static_cast<MotionClassification>(classification),
-                      1 /*xScale*/, 1 /*yScale*/, xOffset, yOffset, xPrecision, yPrecision,
+    event->initialize(InputEvent::nextId(), deviceId, source, displayId, INVALID_HMAC, action, 0,
+                      flags, edgeFlags, metaState, buttonState,
+                      static_cast<MotionClassification>(classification), 1 /*xScale*/, 1 /*yScale*/,
+                      xOffset, yOffset, xPrecision, yPrecision,
                       AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                       downTimeNanos, eventTimeNanos, pointerCount, pointerProperties,
                       rawPointerCoords);
@@ -592,6 +593,11 @@
     return reinterpret_cast<jlong>(destEvent);
 }
 
+static jint android_view_MotionEvent_nativeGetId(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getId();
+}
+
 static jint android_view_MotionEvent_nativeGetDeviceId(jlong nativePtr) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
     return event->getDeviceId();
@@ -790,6 +796,7 @@
         // --------------- @CriticalNative ------------------
 
         {"nativeCopy", "(JJZ)J", (void*)android_view_MotionEvent_nativeCopy},
+        {"nativeGetId", "(J)I", (void*)android_view_MotionEvent_nativeGetId},
         {"nativeGetDeviceId", "(J)I", (void*)android_view_MotionEvent_nativeGetDeviceId},
         {"nativeGetSource", "(J)I", (void*)android_view_MotionEvent_nativeGetSource},
         {"nativeSetSource", "(JI)V", (void*)android_view_MotionEvent_nativeSetSource},
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 7d1cf5d..d6687f0 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -58,6 +58,7 @@
 import "frameworks/base/core/proto/android/service/usb.proto";
 import "frameworks/base/core/proto/android/util/event_log_tags.proto";
 import "frameworks/base/core/proto/android/util/log.proto";
+import "frameworks/base/core/proto/android/util/textdump.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
 import "frameworks/base/core/proto/android/section.proto";
 import "frameworks/base/proto/src/ipconnectivity.proto";
@@ -510,6 +511,17 @@
         (section).args = "sensorservice --proto"
     ];
 
+    // Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
+    optional android.util.TextDumpProto textdump_wifi = 4000 [
+        (section).type = SECTION_TEXT_DUMPSYS,
+        (section).args = "wifi"
+    ];
+
+    optional android.util.TextDumpProto textdump_bluetooth = 4001 [
+        (section).type = SECTION_TEXT_DUMPSYS,
+        (section).args = "bluetooth_manager"
+    ];
+
     // Reserved for OEMs.
     extensions 50000 to 100000;
 }
diff --git a/core/proto/android/section.proto b/core/proto/android/section.proto
index 5afe22a..299d6f9a 100644
--- a/core/proto/android/section.proto
+++ b/core/proto/android/section.proto
@@ -46,6 +46,10 @@
 
     // incidentd calls tombstoned for annotated field
     SECTION_TOMBSTONE = 6;
+
+    // incidentd calls legacy text dumpsys for annotated field. The section will only be generated
+    // on userdebug and eng builds.
+    SECTION_TEXT_DUMPSYS = 7;
 }
 
 message SectionFlags {
diff --git a/core/proto/android/service/appwidget.proto b/core/proto/android/service/appwidget.proto
index cd7173a..97350ef 100644
--- a/core/proto/android/service/appwidget.proto
+++ b/core/proto/android/service/appwidget.proto
@@ -36,4 +36,5 @@
     optional int32 minHeight = 7;
     optional int32 maxWidth = 8;
     optional int32 maxHeight = 9;
+    optional bool restoreCompleted = 10;
 }
diff --git a/core/proto/android/util/textdump.proto b/core/proto/android/util/textdump.proto
new file mode 100644
index 0000000..6118487
--- /dev/null
+++ b/core/proto/android/util/textdump.proto
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+syntax = "proto2";
+package android.util;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+message TextDumpProto {
+    option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+    // The command that was executed
+    optional string command = 1;
+    // The content that was dumped
+    optional string content = 2;
+    // The duration of the dump process
+    optional int64 dump_duration_ns = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 61d2298..6062102 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1817,6 +1817,9 @@
         android:protectionLevel="normal" />
 
     <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
+         Applications holding this permission can access OMAPI reset system API
+         and bypass OMAPI AccessControlEnforcer.
+         <p>Not for use by third-party applications.
          @hide -->
     <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED"
         android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4ac51c6..fb9158f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2752,7 +2752,9 @@
 
     <!-- The amount to scale reduced scale snapshots for Overview and snapshot starting windows.
          Reduced scale snapshots are loaded before full screen snapshots to improve load times and
-         minimize the chance the user will see an empty task card. -->
+         minimize the chance the user will see an empty task card. If set to 0, reduced scale
+         snapshots are disabled, and snapshots will only be stored at config_highResTaskSnapshotScale
+         -->
     <item name="config_lowResTaskSnapshotScale" format="float" type="dimen">0.5</item>
 
     <!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. -->
@@ -4404,6 +4406,7 @@
          Determines whether the specified key groups can be used to wake up the device. -->
     <bool name="config_wakeOnDpadKeyPress">true</bool>
     <bool name="config_wakeOnAssistKeyPress">true</bool>
+    <bool name="config_wakeOnBackKeyPress">true</bool>
 
     <!-- Whether to default to an expanded list of users on the lock screen user switcher. -->
     <bool name="config_expandLockScreenUserSwitcher">false</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4f221d0..e1d94f50f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -685,7 +685,7 @@
     <!-- The size of the right icon image when on low ram -->
     <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
 
-    <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
+    <dimen name="messaging_avatar_size">52dp</dimen>
 
     <dimen name="messaging_group_sending_progress_size">24dp</dimen>
 
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 64768cf..966f495 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -146,7 +146,7 @@
         <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item>
     </style>
     <style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName">
-        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item>
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.MessagingName</item>
     </style>
     <style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/>
     <style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/>
@@ -290,6 +290,9 @@
     <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title">
         <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
     </style>
+    <style name="TextAppearance.DeviceDefault.Notification.MessagingName" parent="TextAppearance.DeviceDefault.Notification.Title">
+        <item name="textSize">16sp</item>
+    </style>
     <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d595459..7690b94 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1612,6 +1612,7 @@
   <java-symbol type="style" name="TextAppearance.SlidingTabNormal" />
   <java-symbol type="style" name="Theme.DeviceDefault.Dialog.NoFrame" />
   <java-symbol type="style" name="Theme.IconMenu" />
+  <java-symbol type="style" name="Theme.Dream" />
   <java-symbol type="style" name="Theme.DeviceDefault.VoiceInteractionSession" />
   <java-symbol type="style" name="Pointer" />
   <java-symbol type="style" name="LargePointer" />
@@ -3051,6 +3052,7 @@
   <!-- Override Wake Key Behavior When Screen is Off -->
   <java-symbol type="bool" name="config_wakeOnDpadKeyPress" />
   <java-symbol type="bool" name="config_wakeOnAssistKeyPress" />
+  <java-symbol type="bool" name="config_wakeOnBackKeyPress" />
 
   <!-- Pinner Service -->
   <java-symbol type="array" name="config_defaultPinnerServiceFiles" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 5d9cb48..2ef0c92 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -701,6 +701,11 @@
         <item name="windowNoDisplay">true</item>
     </style>
 
+    <style name="Theme.Dream">
+        <item name="windowBackground">@null</item>
+        <item name="windowDisablePreview">true</item>
+    </style>
+
     <!-- Default theme for dialog windows and activities (on API level 10 and lower),
          which is used by the
          {@link android.app.Dialog} class.  This changes the window to be
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 718ca46..b42fce0 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -138,6 +138,9 @@
     <!-- vr test permissions -->
     <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" />
 
+    <!-- WindowMetricsTest permissions -->
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
     <application android:theme="@style/Theme" android:supportsRtl="true">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 372b8c2..f4fbefe 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -37,6 +37,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
+import android.content.pm.ProviderInfoList;
 import android.content.pm.ServiceInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -397,7 +398,7 @@
 
         @Override
         public void bindApplication(String s, ApplicationInfo applicationInfo,
-                List<ProviderInfo> list, ComponentName componentName, ProfilerInfo profilerInfo,
+                ProviderInfoList list, ComponentName componentName, ProfilerInfo profilerInfo,
                 Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher,
                 IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1,
                 boolean b2, boolean b3, Configuration configuration,
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 7c2b98f..d02c6d5 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -230,6 +230,16 @@
     }
 
     @Test
+    public void inset_insets_withWaterfallCutout() throws Exception {
+        DisplayCutout cutout = createCutoutWaterfallOnly(Insets.of(0, 10, 0, 10)).inset(1, 2, 3, 4);
+
+        assertEquals(cutout.getSafeInsetLeft(), 0);
+        assertEquals(cutout.getSafeInsetTop(), 8);
+        assertEquals(cutout.getSafeInsetRight(), 0);
+        assertEquals(cutout.getSafeInsetBottom(), 6);
+    }
+
+    @Test
     public void inset_insets_consumeInset() throws Exception {
         DisplayCutout cutout = mCutoutTop.inset(0, 1000, 0, 0);
 
@@ -457,7 +467,8 @@
 
     private static DisplayCutout createCutoutWaterfallOnly(Insets waterfallInsets) {
         return new DisplayCutout(
-                Insets.of(20, 0, 20, 0),
+                Insets.of(waterfallInsets.left, waterfallInsets.top, waterfallInsets.right,
+                        waterfallInsets.bottom),
                 ZERO_RECT,
                 ZERO_RECT,
                 ZERO_RECT,
diff --git a/core/tests/coretests/src/android/view/KeyEventTest.java b/core/tests/coretests/src/android/view/KeyEventTest.java
index 14999c7..623008e 100644
--- a/core/tests/coretests/src/android/view/KeyEventTest.java
+++ b/core/tests/coretests/src/android/view/KeyEventTest.java
@@ -19,6 +19,7 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 
 import android.os.Parcel;
 
@@ -28,10 +29,14 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.HashSet;
+import java.util.Set;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KeyEventTest {
 
+    private static final int ID = 0xabcdef;
     private static final int DOWN_TIME = 50;
     private static final long EVENT_TIME = 100;
     private static final int ACTION = KeyEvent.ACTION_DOWN;
@@ -45,6 +50,8 @@
     private static final byte[] HMAC = null;
     private static final String CHARACTERS = null;
 
+    private static final int ID_SOURCE_MASK = 0x3 << 30;
+
     @Test
     public void testObtain() {
         KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
@@ -75,8 +82,7 @@
     public void testObtainWithDisplayId() {
         final int displayId = 5;
         KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
-                METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, null /* hmac*/,
-                CHARACTERS);
+                METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, displayId, CHARACTERS);
         assertEquals(DOWN_TIME, keyEvent.getDownTime());
         assertEquals(EVENT_TIME, keyEvent.getEventTime());
         assertEquals(ACTION, keyEvent.getAction());
@@ -91,6 +97,52 @@
         assertEquals(CHARACTERS, keyEvent.getCharacters());
     }
 
+    /**
+     * Tests that it can generate 500 consecutive distinct numbers. This is a non-deterministic test
+     * but with 30 bits randomness the failure rate is roughly 4.52e-5, which is negligible enough.
+     * Probability formula: N * (N - 1) * ... * (N - n + 1) / N^n, where N = 2^30 and n = 500 for
+     * this test.
+     */
+    @Test
+    public void testObtainGeneratesUniqueId() {
+        Set<Integer> set = new HashSet<>();
+        for (int i = 0; i < 500; ++i) {
+            KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+                    METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+            assertFalse("Found duplicate ID in round " + i,
+                    set.contains(keyEvent.getId()));
+            set.add(keyEvent.getSequenceNumber());
+        }
+    }
+
+    @Test
+    public void testConstructorGeneratesUniqueId() {
+        Set<Integer> set = new HashSet<>();
+        for (int i = 0; i < 500; ++i) {
+            KeyEvent keyEvent = new KeyEvent(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT);
+            assertFalse("Found duplicate sequence number in round " + i,
+                    set.contains(keyEvent.getId()));
+            set.add(keyEvent.getSequenceNumber());
+        }
+    }
+
+    @Test
+    public void testObtainGeneratesIdWithRightSource() {
+        for (int i = 0; i < 500; ++i) {
+            KeyEvent keyEvent = KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
+                    METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, CHARACTERS);
+            assertEquals(0x3 << 30, ID_SOURCE_MASK & keyEvent.getId());
+        }
+    }
+
+    @Test
+    public void testConstructorGeneratesIdWithRightSource() {
+        for (int i = 0; i < 500; ++i) {
+            KeyEvent keyEvent = new KeyEvent(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT);
+            assertEquals(0x3 << 30, ID_SOURCE_MASK & keyEvent.getId());
+        }
+    }
+
     @Test
     public void testParcelUnparcel() {
         KeyEvent key1 = createKey();
@@ -112,11 +164,12 @@
     }
 
     private static KeyEvent createKey() {
-        return KeyEvent.obtain(DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT,
-                METASTATE, DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS);
+        return KeyEvent.obtain(ID, DOWN_TIME, EVENT_TIME, ACTION, KEYCODE, REPEAT, METASTATE,
+                DEVICE_ID, SCAN_CODE, FLAGS, SOURCE, INVALID_DISPLAY, HMAC, CHARACTERS);
     }
 
     private static void compareKeys(KeyEvent key1, KeyEvent key2) {
+        assertEquals(key1.getId(), key2.getId());
         assertEquals(key1.getDownTime(), key2.getDownTime());
         assertEquals(key1.getEventTime(), key2.getEventTime());
         assertEquals(key1.getAction(), key2.getAction());
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 9d09830..786ae89 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -23,6 +23,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 
 import android.view.MotionEvent.PointerCoords;
@@ -34,9 +35,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.HashSet;
+import java.util.Set;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MotionEventTest {
+    private static final int ID_SOURCE_MASK = 0x3 << 30;
 
     @Test
     public void testObtainWithDisplayId() {
@@ -138,4 +143,30 @@
         assertEquals(30, event.getXCursorPosition(), 0.1);
         assertEquals(50, event.getYCursorPosition(), 0.1);
     }
+
+    /**
+     * Tests that it can generate 500 consecutive distinct numbers. This is a non-deterministic test
+     * but with 30 bits randomness the failure rate is roughly 4.52e-5, which is negligible enough.
+     * Probability formula: N * (N - 1) * ... * (N - n + 1) / N^n, where N = 2^30 and n = 500 for
+     * this test.
+     */
+    @Test
+    public void testObtainGeneratesUniqueId() {
+        Set<Integer> set = new HashSet<>();
+        for (int i = 0; i < 500; ++i) {
+            final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+                    ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */);
+            assertFalse("Found duplicate ID in round " + i, set.contains(event.getId()));
+            set.add(event.getSequenceNumber());
+        }
+    }
+
+    @Test
+    public void testObtainGeneratesIdWithRightSource() {
+        for (int i = 0; i < 500; ++i) {
+            final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
+                    ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */);
+            assertEquals(0x3 << 30, ID_SOURCE_MASK & event.getId());
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java
new file mode 100644
index 0000000..fa68860
--- /dev/null
+++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+import android.util.Size;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link WindowManager#getCurrentWindowMetrics()} and
+ * {@link WindowManager#getMaximumWindowMetrics()}.
+ *
+ * <p>Build/Install/Run:
+ *  atest FrameworksCoreTests:WindowMetricsTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@FlakyTest(bugId = 148789183, detail = "Remove after confirmed it's stable.")
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class WindowMetricsTest {
+    private Context mWindowContext;
+    private WindowManager mWm;
+
+    @Before
+    public void setUp() {
+        final Context insetContext = InstrumentationRegistry.getInstrumentation()
+                .getTargetContext();
+        final Display display = insetContext.getSystemService(DisplayManager.class)
+                .getDisplay(DEFAULT_DISPLAY);
+        mWindowContext = insetContext.createDisplayContext(display)
+                .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
+        mWm = mWindowContext.getSystemService(WindowManager.class);
+    }
+
+    @Test
+    public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() {
+        final View view = new View(mWindowContext);
+        final WindowManager.LayoutParams params =
+                new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+        Handler.getMain().runWithScissors(() -> {
+            mWm.addView(view, params);
+            // Check get metrics do not crash.
+            WindowMetrics currentMetrics = mWm.getCurrentWindowMetrics();
+            WindowMetrics maxMetrics = mWm.getMaximumWindowMetrics();
+            verifyMetricsSanity(currentMetrics, maxMetrics);
+
+            mWm.removeViewImmediate(view);
+            // Check get metrics do not crash.
+            currentMetrics = mWm.getCurrentWindowMetrics();
+            maxMetrics = mWm.getMaximumWindowMetrics();
+            verifyMetricsSanity(currentMetrics, maxMetrics);
+        }, 0);
+    }
+
+    private static void verifyMetricsSanity(WindowMetrics currentMetrics,
+            WindowMetrics maxMetrics) {
+        Size currentSize = currentMetrics.getSize();
+        Size maxSize = maxMetrics.getSize();
+
+        assertTrue(maxSize.getWidth() >= currentSize.getWidth());
+        assertTrue(maxSize.getHeight() >= currentSize.getHeight());
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index bbf3b12..9af0ed0 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -36,6 +36,7 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -85,7 +86,9 @@
 
 import java.lang.reflect.Field;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 
 @RunWith(AndroidJUnit4.class)
@@ -534,6 +537,36 @@
         verify(mRingtone).play();
     }
 
+    @Test
+    public void testOnAccessibilityShortcut_showsWarningDialog_ttsLongTimeInit_retrySpoken()
+            throws Exception {
+        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
+        configureValidShortcutService();
+        configureTtsSpokenPromptEnabled();
+        configureHandlerCallbackInvocation();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0);
+        Set<String> features = new HashSet<>();
+        features.add(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED);
+        doReturn(features, Collections.emptySet()).when(mVoice).getFeatures();
+        doReturn(TextToSpeech.LANG_NOT_SUPPORTED, TextToSpeech.LANG_AVAILABLE)
+                .when(mTextToSpeech).setLanguage(any());
+        accessibilityShortcutController.performAccessibilityShortcut();
+
+        verify(mAlertDialog).show();
+        ArgumentCaptor<TextToSpeech.OnInitListener> onInitCap = ArgumentCaptor.forClass(
+                TextToSpeech.OnInitListener.class);
+        verify(mFrameworkObjectProvider).getTextToSpeech(any(), onInitCap.capture());
+        onInitCap.getValue().onInit(TextToSpeech.SUCCESS);
+        verify(mTextToSpeech).speak(any(), eq(TextToSpeech.QUEUE_FLUSH), any(), any());
+        ArgumentCaptor<DialogInterface.OnDismissListener> onDismissCap = ArgumentCaptor.forClass(
+                DialogInterface.OnDismissListener.class);
+        verify(mAlertDialog).setOnDismissListener(onDismissCap.capture());
+        onDismissCap.getValue().onDismiss(mAlertDialog);
+        verify(mTextToSpeech).shutdown();
+        verify(mRingtone, times(0)).play();
+    }
+
     private void configureNoShortcutService() throws Exception {
         when(mAccessibilityManagerService
                 .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY))
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f2b4db1..f800f9e 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2531,7 +2531,7 @@
             int offset, int size, long presentationTimeUs, int flags)
         throws CryptoException {
         synchronized(mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
             invalidateByteBuffer(mCachedInputBuffers, index);
@@ -2783,7 +2783,7 @@
             long presentationTimeUs,
             int flags) throws CryptoException {
         synchronized(mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
             invalidateByteBuffer(mCachedInputBuffers, index);
@@ -2818,7 +2818,7 @@
      */
     public final int dequeueInputBuffer(long timeoutUs) {
         synchronized (mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
         }
@@ -3534,7 +3534,7 @@
     public final int dequeueOutputBuffer(
             @NonNull BufferInfo info, long timeoutUs) {
         synchronized (mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
         }
@@ -3916,7 +3916,7 @@
     @NonNull
     public ByteBuffer[] getInputBuffers() {
         synchronized (mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
             if (mCachedInputBuffers == null) {
@@ -3952,7 +3952,7 @@
     @NonNull
     public ByteBuffer[] getOutputBuffers() {
         synchronized (mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
             if (mCachedOutputBuffers == null) {
@@ -3984,7 +3984,7 @@
     @Nullable
     public ByteBuffer getInputBuffer(int index) {
         synchronized (mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
         }
@@ -4018,7 +4018,7 @@
     @Nullable
     public Image getInputImage(int index) {
         synchronized (mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
         }
@@ -4052,7 +4052,7 @@
     @Nullable
     public ByteBuffer getOutputBuffer(int index) {
         synchronized (mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
         }
@@ -4085,7 +4085,7 @@
     @Nullable
     public Image getOutputImage(int index) {
         synchronized (mBufferLock) {
-            if (mBufferMode != BUFFER_MODE_LEGACY) {
+            if (mBufferMode == BUFFER_MODE_BLOCK) {
                 throw new IncompatibleWithBlockModelException();
             }
         }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index b579144..95199cc 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -240,7 +240,7 @@
      * @param inputId The ID of the TV input associated with the session.
      */
     @Nullable
-    public abstract Session onCreateSession(String inputId);
+    public abstract Session onCreateSession(@NonNull String inputId);
 
     /**
      * Returns a concrete implementation of {@link RecordingSession}.
@@ -251,7 +251,7 @@
      * @param inputId The ID of the TV input associated with the recording session.
      */
     @Nullable
-    public RecordingSession onCreateRecordingSession(String inputId) {
+    public RecordingSession onCreateRecordingSession(@NonNull String inputId) {
         return null;
     }
 
diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
index 6837244..598ff8f 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
+++ b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
@@ -78,7 +78,8 @@
      *                {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE}
      *                {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD}.
      *                New [use case : priority value] pair can be defined in the manifest by the
-     *                OEM. Any undefined use case would cause IllegalArgumentException.
+     *                OEM. The id of the useCaseVendor should be passed through this parameter. Any
+     *                undefined use case would cause IllegalArgumentException.
      */
     public ResourceClientProfile(@NonNull String tvInputSessionId,
                                  int useCase) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 37a77be..f36f97d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -82,6 +82,9 @@
     static final String DEFAULT_DSU_SLOT = "dsu";
     static final String KEY_PUBKEY = "KEY_PUBKEY";
 
+    // Default userdata partition size is 2GiB.
+    private static final long DEFAULT_USERDATA_SIZE = 2L << 30;
+
     /*
      * Intent actions
      */
@@ -270,6 +273,10 @@
         String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT);
         String publicKey = intent.getStringExtra(KEY_PUBKEY);
 
+        if (userdataSize == 0) {
+            userdataSize = DEFAULT_USERDATA_SIZE;
+        }
+
         if (TextUtils.isEmpty(dsuSlot)) {
             dsuSlot = DEFAULT_DSU_SLOT;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 3c0f6fe..57e6808 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -136,7 +136,7 @@
         if (mDreamManager == null)
             return null;
         try {
-            return mDreamManager.getDefaultDreamComponent();
+            return mDreamManager.getDefaultDreamComponentForUser(mContext.getUserId());
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to get default dream", e);
             return null;
@@ -269,7 +269,7 @@
         if (mDreamManager == null || dreamInfo == null || dreamInfo.componentName == null)
             return;
         try {
-            mDreamManager.testDream(dreamInfo.componentName);
+            mDreamManager.testDream(mContext.getUserId(), dreamInfo.componentName);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to preview " + dreamInfo, e);
         }
diff --git a/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml
new file mode 100644
index 0000000..e3d010e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?android:attr/colorControlHighlight">
+   <item>
+      <shape>
+         <solid android:color="@color/qs_user_detail_avatar_frame" />
+
+         <padding
+                 android:left="1dp"
+                 android:right="1dp"
+                 android:bottom="1dp"
+                 android:top="1dp" />
+
+         <corners android:radius="48dp" />
+      </shape>
+   </item>
+</ripple>
diff --git a/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
new file mode 100644
index 0000000..1f38b1e
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<!-- LinearLayout -->
+<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:sysui="http://schemas.android.com/apk/res-auto"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="8dp"
+        android:layout_marginEnd="8dp"
+        android:gravity="end|center_vertical"
+        android:clickable="true"
+        android:background="@drawable/rounded_user_switcher_bg"
+        sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
+        sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
+    <TextView android:id="@+id/user_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="13dp"
+            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
+            />
+    <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
+            android:layout_width="@dimen/framed_avatar_size"
+            android:layout_height="@dimen/framed_avatar_size"
+            android:contentDescription="@null"
+            sysui:badgeDiameter="18dp"
+            sysui:badgeMargin="1dp" />
+</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index fb38e1c..b1e5165 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -27,8 +27,6 @@
         android:gravity="center_vertical"
         android:clickable="true"
         android:background="@drawable/ripple_drawable"
-        android:clipChildren="false"
-        android:clipToPadding="false"
         sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher"
         sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
     <TextView android:id="@+id/user_name"
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index 23b64e3..b375364 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -22,4 +22,28 @@
     <style name="UserDetailView">
         <item name="numColumns">4</item>
     </style>
+
+    <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
+        <item name="android:textSize">@dimen/kg_user_switcher_text_size</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">?attr/wallpaperTextColor</item>
+    </style>
+
+    <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.Activated">
+        <item name="android:fontWeight">700</item>
+        <item name="android:textStyle">bold</item>
+        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
+    </style>
+
+    <style name="TextAppearance.QS.UserSwitcher">
+        <item name="android:textSize">@dimen/qs_detail_item_primary_text_size</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+    </style>
+
+    <style name="TextAppearance.QS.UserSwitcher.Activated">
+        <item name="android:fontWeight">700</item>
+        <item name="android:textStyle">bold</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1c2404f..9caaa9f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -124,6 +124,9 @@
     <!-- Increased height of a collapsed media notification in the status bar -->
     <dimen name="notification_min_height_media">160dp</dimen>
 
+    <!-- Increased height of a collapsed messaging notification in the status bar -->
+    <dimen name="notification_min_height_messaging">118dp</dimen>
+
     <!-- Height of a small notification in the status bar which was used before android N -->
     <dimen name="notification_min_height_legacy">64dp</dimen>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl
new file mode 100644
index 0000000..97aa512
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.recents;
+
+/**
+ * Listener interface that Launcher attaches to SystemUI to get
+ * pinned stack animation callbacks.
+ */
+oneway interface IPinnedStackAnimationListener {
+    /**
+     * Notifies the pinned stack animation is started.
+     */
+    void onPinnedStackAnimationStarted();
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index b1d39f5..80fd826 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -22,9 +22,11 @@
 import android.os.Bundle;
 import android.view.MotionEvent;
 
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+
 /**
  * Temporary callbacks into SystemUI.
- * Next id = 22
+ * Next id = 25
  */
 interface ISystemUiProxy {
 
@@ -121,11 +123,21 @@
     /**
      * Handle the provided image as if it was a screenshot.
      */
-     void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
+    void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
               in Insets visibleInsets, int taskId) = 21;
 
     /**
      * Sets the split-screen divider minimized state
      */
     void setSplitScreenMinimized(boolean minimized) = 22;
+
+    /*
+     * Notifies that the swipe-to-home (recents animation) is finished.
+     */
+    void notifySwipeToHomeFinished() = 23;
+
+    /**
+     * Sets listener to get pinned stack animation callbacks.
+     */
+    void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
index 4474a49..eca6ebf 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
@@ -62,7 +62,9 @@
         orientation = snapshot.getOrientation();
         rotation = snapshot.getRotation();
         reducedResolution = snapshot.isLowResolution();
-        scale = snapshot.getScale();
+        // TODO(b/149579527): Pass task size instead of computing scale.
+        // Assume width and height were scaled the same; compute scale only for width
+        scale = (float) thumbnail.getWidth() / snapshot.getTaskSize().x;
         isRealSnapshot = snapshot.isRealSnapshot();
         isTranslucent = snapshot.isTranslucent();
         windowingMode = snapshot.getWindowingMode();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
index 8c0ffb8..360244c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
@@ -53,11 +53,9 @@
     }
 
     @Override
-    public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
-            boolean fromShelfAdjustment) {
+    public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {
         for (PinnedStackListener listener : mListeners) {
-            listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment,
-                    fromShelfAdjustment);
+            listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment);
         }
     }
 
@@ -69,13 +67,6 @@
     }
 
     @Override
-    public void onMinimizedStateChanged(boolean isMinimized) {
-        for (PinnedStackListener listener : mListeners) {
-            listener.onMinimizedStateChanged(isMinimized);
-        }
-    }
-
-    @Override
     public void onActionsChanged(ParceledListSlice actions) {
         for (PinnedStackListener listener : mListeners) {
             listener.onActionsChanged(actions);
@@ -117,13 +108,6 @@
         }
     }
 
-    @Override
-    public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
-        for (PinnedStackListener listener : mListeners) {
-            listener.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
-        }
-    }
-
     /**
      * A counterpart of {@link IPinnedStackListener} with empty implementations.
      * Subclasses can ignore those methods they do not intend to take action upon.
@@ -131,13 +115,10 @@
     public static class PinnedStackListener {
         public void onListenerRegistered(IPinnedStackController controller) {}
 
-        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
-                boolean fromShelfAdjustment) {}
+        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {}
 
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
 
-        public void onMinimizedStateChanged(boolean isMinimized) {}
-
         public void onActionsChanged(ParceledListSlice actions) {}
 
         public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {}
@@ -149,7 +130,5 @@
         public void onConfigurationChanged() {}
 
         public void onAspectRatioChanged(float aspectRatio) {}
-
-        public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {}
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 748f356..6cd6118 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -91,15 +91,6 @@
         }
     }
 
-    @Deprecated
-    public void setCancelWithDeferredScreenshot(boolean screenshot) {
-        try {
-            mAnimationController.setCancelWithDeferredScreenshot(screenshot);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to set cancel with deferred screenshot", e);
-        }
-    }
-
     public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
         try {
             mAnimationController.setDeferCancelUntilNextTransition(defer, screenshot);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 5f92b28..1c6223b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -20,8 +20,6 @@
 import android.app.ITaskStackListener;
 import android.content.ComponentName;
 import android.os.IBinder;
-import android.os.UserHandle;
-import android.util.Log;
 
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -40,8 +38,6 @@
     public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
     public void onActivityUnpinned() { }
     public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
-    public void onPinnedStackAnimationStarted() { }
-    public void onPinnedStackAnimationEnded() { }
     public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
     public void onActivityDismissingDockedStack() { }
     public void onActivityLaunchOnSecondaryDisplayFailed() { }
@@ -117,22 +113,4 @@
 
     /** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */
     public void onRecentTaskListFrozenChanged(boolean frozen) { }
-
-    /**
-     * Checks that the current user matches the process. Since
-     * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
-     * {@link TaskStackChangeListener} should make this call to verify that we don't act on events
-     * originating from another user's interactions.
-     */
-    protected final boolean checkCurrentUserId(int currentUserId, boolean debug) {
-        int processUserId = UserHandle.myUserId();
-        if (processUserId != currentUserId) {
-            if (debug) {
-                Log.d("TaskStackChangeListener", "UID mismatch. Process is uid=" + processUserId
-                        + " and the current user is uid=" + currentUserId);
-            }
-            return false;
-        }
-        return true;
-    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index acce41c..cbdd3f8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -128,18 +128,6 @@
     }
 
     @Override
-    public void onPinnedStackAnimationStarted() throws RemoteException {
-        mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED);
-        mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED);
-    }
-
-    @Override
-    public void onPinnedStackAnimationEnded() throws RemoteException {
-        mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
-        mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
-    }
-
-    @Override
     public void onActivityForcedResizable(String packageName, int taskId, int reason)
             throws RemoteException {
         mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
@@ -249,11 +237,9 @@
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
         private static final int ON_ACTIVITY_PINNED = 3;
         private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
-        private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5;
         private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
         private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
         private static final int ON_TASK_PROFILE_LOCKED = 8;
-        private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
         private static final int ON_ACTIVITY_UNPINNED = 10;
         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
         private static final int ON_TASK_CREATED = 12;
@@ -317,18 +303,6 @@
                         }
                         break;
                     }
-                    case ON_PINNED_STACK_ANIMATION_STARTED: {
-                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
-                        }
-                        break;
-                    }
-                    case ON_PINNED_STACK_ANIMATION_ENDED: {
-                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
-                        }
-                        break;
-                    }
                     case ON_ACTIVITY_FORCED_RESIZABLE: {
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                             mTaskStackListeners.get(i).onActivityForcedResizable(
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index a2a08502..bb665fe 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -67,6 +67,7 @@
         internal const val CONTROLS_AVAILABLE = "systemui.controls_available"
         internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE)
         private const val USER_CHANGE_RETRY_DELAY = 500L // ms
+        private const val DEFAULT_ENABLED = 1
     }
 
     // Map of map: ComponentName -> (String -> ControlInfo).
@@ -79,7 +80,8 @@
 
     private val contentResolver: ContentResolver
         get() = context.contentResolver
-    override var available = Settings.Secure.getInt(contentResolver, CONTROLS_AVAILABLE, 0) != 0
+    override var available = Settings.Secure.getInt(
+            contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED) != 0
         private set
 
     private var currentUser = context.user
@@ -104,7 +106,7 @@
                 userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME)
         persistenceWrapper.changeFile(fileName)
         available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
-                /* default */ 0, newUser.identifier) != 0
+                /* default */ DEFAULT_ENABLED, newUser.identifier) != 0
         synchronized(currentFavorites) {
             currentFavorites.clear()
         }
@@ -140,7 +142,7 @@
                 return
             }
             available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE,
-                    /* default */ 0, currentUserId) != 0
+                    /* default */ DEFAULT_ENABLED, currentUserId) != 0
             synchronized(currentFavorites) {
                 currentFavorites.clear()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 9203e6d..5da02dc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -25,7 +25,6 @@
 import android.app.ActivityManager;
 import android.app.Dialog;
 import android.app.IActivityManager;
-import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.app.WallpaperManager;
@@ -93,6 +92,7 @@
 import com.android.systemui.MultiListLayout.MultiListAdapter;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.controls.management.ControlsListingController;
 import com.android.systemui.controls.ui.ControlsUiController;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -152,7 +152,7 @@
     private final IDreamManager mDreamManager;
     private final DevicePolicyManager mDevicePolicyManager;
     private final LockPatternUtils mLockPatternUtils;
-    private final KeyguardManager mKeyguardManager;
+    private final KeyguardStateController mKeyguardStateController;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final ContentResolver mContentResolver;
     private final Resources mResources;
@@ -190,6 +190,8 @@
     private ControlsUiController mControlsUiController;
     private final IWindowManager mIWindowManager;
     private final Executor mBackgroundExecutor;
+    private final ControlsListingController mControlsListingController;
+    private boolean mAnyControlsProviders = false;
 
     /**
      * @param context everything needs a context :(
@@ -198,7 +200,7 @@
     public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs,
             AudioManager audioManager, IDreamManager iDreamManager,
             DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils,
-            KeyguardManager keyguardManager, BroadcastDispatcher broadcastDispatcher,
+            BroadcastDispatcher broadcastDispatcher,
             ConnectivityManager connectivityManager, TelephonyManager telephonyManager,
             ContentResolver contentResolver, @Nullable Vibrator vibrator, @Main Resources resources,
             ConfigurationController configurationController, ActivityStarter activityStarter,
@@ -209,14 +211,15 @@
             IStatusBarService statusBarService,
             NotificationShadeWindowController notificationShadeWindowController,
             ControlsUiController controlsUiController, IWindowManager iWindowManager,
-            @Background Executor backgroundExecutor) {
+            @Background Executor backgroundExecutor,
+            ControlsListingController controlsListingController) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
         mDreamManager = iDreamManager;
         mDevicePolicyManager = devicePolicyManager;
         mLockPatternUtils = lockPatternUtils;
-        mKeyguardManager = keyguardManager;
+        mKeyguardStateController = keyguardStateController;
         mBroadcastDispatcher = broadcastDispatcher;
         mContentResolver = contentResolver;
         mResources = resources;
@@ -233,6 +236,7 @@
         mControlsUiController = controlsUiController;
         mIWindowManager = iWindowManager;
         mBackgroundExecutor = backgroundExecutor;
+        mControlsListingController = controlsListingController;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -269,6 +273,8 @@
                 }
             }
         });
+
+        mControlsListingController.addCallback(list -> mAnyControlsProviders = !list.isEmpty());
     }
 
     /**
@@ -427,7 +433,7 @@
                                                 .startPendingIntentDismissingKeyguard(intent);
                                     }
                                 },
-                                mKeyguardManager.isDeviceLocked())
+                                !mKeyguardStateController.isUnlocked())
                         : null;
 
         ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
@@ -444,12 +450,12 @@
     }
 
     private boolean shouldDisplayLockdown() {
-        int userId = getCurrentUser().id;
         // Lockdown is meaningless without a place to go.
-        if (!mKeyguardManager.isDeviceSecure(userId)) {
+        if (!mKeyguardStateController.isMethodSecure()) {
             return false;
         }
 
+        int userId = getCurrentUser().id;
         // Only show the lockdown button if the device isn't locked down (for whatever reason).
         int state = mLockPatternUtils.getStrongAuthForUser(userId);
         return (state == STRONG_AUTH_NOT_REQUIRED
@@ -1914,7 +1920,8 @@
     }
 
     private boolean shouldShowControls() {
-        return !mKeyguardManager.isDeviceLocked()
-                && mControlsUiController.getAvailable();
+        return mKeyguardStateController.isUnlocked()
+                && mControlsUiController.getAvailable()
+                && mAnyControlsProviders;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
index adee7f23..38744fe 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
@@ -18,6 +18,8 @@
 
 import android.content.res.Configuration;
 
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+
 import java.io.PrintWriter;
 
 
@@ -27,5 +29,7 @@
     default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {}
     void onConfigurationChanged(Configuration newConfig);
     default void setShelfHeight(boolean visible, int height) {}
+    default void setPinnedStackAnimationType(int animationType) {}
+    default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {}
     default void dump(PrintWriter pw) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
new file mode 100644
index 0000000..b5fd406
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.annotation.MainThread;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Controller class of PiP animations (both from and to PiP mode).
+ */
+public class PipAnimationController {
+    private static final float FRACTION_START = 0f;
+    private static final float FRACTION_END = 1f;
+
+    public static final int DURATION_NONE = 0;
+    public static final int DURATION_DEFAULT_MS = 425;
+    public static final int ANIM_TYPE_BOUNDS = 0;
+    public static final int ANIM_TYPE_ALPHA = 1;
+
+    @IntDef(prefix = { "ANIM_TYPE_" }, value = {
+            ANIM_TYPE_BOUNDS,
+            ANIM_TYPE_ALPHA
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AnimationType {}
+
+    private final Interpolator mFastOutSlowInInterpolator;
+
+    private PipTransitionAnimator mCurrentAnimator;
+
+    PipAnimationController(Context context) {
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_slow_in);
+    }
+
+    @MainThread
+    PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
+            Rect destinationBounds, float alphaStart, float alphaEnd) {
+        if (mCurrentAnimator == null) {
+            mCurrentAnimator = setupPipTransitionAnimator(
+                    PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
+                            destinationBounds, alphaStart, alphaEnd));
+        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
+                && mCurrentAnimator.isRunning()) {
+            mCurrentAnimator.updateEndValue(alphaEnd);
+        } else {
+            mCurrentAnimator.cancel();
+            mCurrentAnimator = setupPipTransitionAnimator(
+                    PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
+                            destinationBounds, alphaStart, alphaEnd));
+        }
+        return mCurrentAnimator;
+    }
+
+    @MainThread
+    PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
+            Rect startBounds, Rect endBounds) {
+        if (mCurrentAnimator == null) {
+            mCurrentAnimator = setupPipTransitionAnimator(
+                    PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
+        } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
+                && mCurrentAnimator.isRunning()) {
+            mCurrentAnimator.setDestinationBounds(endBounds);
+            // construct new Rect instances in case they are recycled
+            mCurrentAnimator.updateEndValue(new Rect(endBounds));
+        } else {
+            mCurrentAnimator.cancel();
+            mCurrentAnimator = setupPipTransitionAnimator(
+                    PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
+        }
+        return mCurrentAnimator;
+    }
+
+    private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) {
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setFloatValues(FRACTION_START, FRACTION_END);
+        return animator;
+    }
+
+    /**
+     * Additional callback interface for PiP animation
+     */
+    public static class PipAnimationCallback {
+        /**
+         * Called when PiP animation is started.
+         */
+        public void onPipAnimationStart(IWindowContainer wc, PipTransitionAnimator animator) {}
+
+        /**
+         * Called when PiP animation is ended.
+         */
+        public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx,
+                PipTransitionAnimator animator) {}
+
+        /**
+         * Called when PiP animation is cancelled.
+         */
+        public void onPipAnimationCancel(IWindowContainer wc, PipTransitionAnimator animator) {}
+    }
+
+    /**
+     * Animator for PiP transition animation which supports both alpha and bounds animation.
+     * @param <T> Type of property to animate, either alpha (float) or bounds (Rect)
+     */
+    public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
+            ValueAnimator.AnimatorUpdateListener,
+            ValueAnimator.AnimatorListener {
+        private final IWindowContainer mWindowContainer;
+        private final boolean mScheduleFinishPip;
+        private final SurfaceControl mLeash;
+        private final @AnimationType int mAnimationType;
+        private final Rect mDestinationBounds = new Rect();
+
+        private T mStartValue;
+        private T mEndValue;
+        private T mCurrentValue;
+        private PipAnimationCallback mPipAnimationCallback;
+        private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;
+
+        private PipTransitionAnimator(IWindowContainer wc, boolean scheduleFinishPip,
+                @AnimationType int animationType, Rect destinationBounds,
+                T startValue, T endValue) {
+            mWindowContainer = wc;
+            mScheduleFinishPip = scheduleFinishPip;
+            try {
+                mLeash = wc.getLeash();
+                mAnimationType = animationType;
+                mDestinationBounds.set(destinationBounds);
+                mStartValue = startValue;
+                mEndValue = endValue;
+                addListener(this);
+                addUpdateListener(this);
+                mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mCurrentValue = mStartValue;
+            applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START);
+            if (mPipAnimationCallback != null) {
+                mPipAnimationCallback.onPipAnimationStart(mWindowContainer, this);
+            }
+        }
+
+        @Override
+        public void onAnimationUpdate(ValueAnimator animation) {
+            applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
+                    animation.getAnimatedFraction());
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mCurrentValue = mEndValue;
+            final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
+            applySurfaceControlTransaction(mLeash, tx, FRACTION_END);
+            if (mPipAnimationCallback != null) {
+                mPipAnimationCallback.onPipAnimationEnd(mWindowContainer, tx, this);
+            }
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            if (mPipAnimationCallback != null) {
+                mPipAnimationCallback.onPipAnimationCancel(mWindowContainer, this);
+            }
+        }
+
+        @Override public void onAnimationRepeat(Animator animation) {}
+
+        @AnimationType int getAnimationType() {
+            return mAnimationType;
+        }
+
+        PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) {
+            mPipAnimationCallback = callback;
+            return this;
+        }
+
+        boolean shouldScheduleFinishPip() {
+            return mScheduleFinishPip;
+        }
+
+        T getStartValue() {
+            return mStartValue;
+        }
+
+        T getEndValue() {
+            return mEndValue;
+        }
+
+        Rect getDestinationBounds() {
+            return mDestinationBounds;
+        }
+
+        void setDestinationBounds(Rect destinationBounds) {
+            mDestinationBounds.set(destinationBounds);
+        }
+
+        void setCurrentValue(T value) {
+            mCurrentValue = value;
+        }
+
+        /**
+         * Updates the {@link #mEndValue}.
+         *
+         * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation.
+         * This is typically used when we receive a shelf height adjustment during the bounds
+         * animation. In which case we can update the end bounds and keep the existing animation
+         * running instead of cancelling it.
+         */
+        void updateEndValue(T endValue) {
+            mEndValue = endValue;
+            mStartValue = mCurrentValue;
+        }
+
+        SurfaceControl.Transaction newSurfaceControlTransaction() {
+            return mSurfaceControlTransactionFactory.getTransaction();
+        }
+
+        @VisibleForTesting
+        void setSurfaceControlTransactionFactory(SurfaceControlTransactionFactory factory) {
+            mSurfaceControlTransactionFactory = factory;
+        }
+
+        abstract void applySurfaceControlTransaction(SurfaceControl leash,
+                SurfaceControl.Transaction tx, float fraction);
+
+        static PipTransitionAnimator<Float> ofAlpha(IWindowContainer wc, boolean scheduleFinishPip,
+                Rect destinationBounds, float startValue, float endValue) {
+            return new PipTransitionAnimator<Float>(wc, scheduleFinishPip, ANIM_TYPE_ALPHA,
+                    destinationBounds, startValue, endValue) {
+                @Override
+                void applySurfaceControlTransaction(SurfaceControl leash,
+                        SurfaceControl.Transaction tx, float fraction) {
+                    final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction;
+                    setCurrentValue(alpha);
+                    tx.setAlpha(leash, alpha);
+                    if (Float.compare(fraction, FRACTION_START) == 0) {
+                        // Ensure the start condition
+                        final Rect bounds = getDestinationBounds();
+                        tx.setPosition(leash, bounds.left, bounds.top)
+                                .setWindowCrop(leash, bounds.width(), bounds.height());
+                    }
+                    tx.apply();
+                }
+            };
+        }
+
+        static PipTransitionAnimator<Rect> ofBounds(IWindowContainer wc, boolean scheduleFinishPip,
+                Rect startValue, Rect endValue) {
+            // construct new Rect instances in case they are recycled
+            return new PipTransitionAnimator<Rect>(wc, scheduleFinishPip, ANIM_TYPE_BOUNDS,
+                    endValue, new Rect(startValue), new Rect(endValue)) {
+                private final Rect mTmpRect = new Rect();
+
+                private int getCastedFractionValue(float start, float end, float fraction) {
+                    return (int) (start * (1 - fraction) + end * fraction + .5f);
+                }
+
+                @Override
+                void applySurfaceControlTransaction(SurfaceControl leash,
+                        SurfaceControl.Transaction tx, float fraction) {
+                    final Rect start = getStartValue();
+                    final Rect end = getEndValue();
+                    mTmpRect.set(
+                            getCastedFractionValue(start.left, end.left, fraction),
+                            getCastedFractionValue(start.top, end.top, fraction),
+                            getCastedFractionValue(start.right, end.right, fraction),
+                            getCastedFractionValue(start.bottom, end.bottom, fraction));
+                    setCurrentValue(mTmpRect);
+                    tx.setPosition(leash, mTmpRect.left, mTmpRect.top)
+                            .setWindowCrop(leash, mTmpRect.width(), mTmpRect.height());
+                    if (Float.compare(fraction, FRACTION_START) == 0) {
+                        // Ensure the start condition
+                        tx.setAlpha(leash, 1f);
+                    }
+                    tx.apply();
+                }
+            };
+        }
+    }
+
+    interface SurfaceControlTransactionFactory {
+        SurfaceControl.Transaction getTransaction();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 41b3130..8c3ccaa 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -36,7 +36,6 @@
 import android.util.TypedValue;
 import android.view.DisplayInfo;
 import android.view.Gravity;
-import android.view.IPinnedStackController;
 import android.view.IWindowManager;
 import android.view.WindowContainerTransaction;
 import android.view.WindowManagerGlobal;
@@ -56,9 +55,7 @@
     private final IWindowManager mWindowManager;
     private final PipSnapAlgorithm mSnapAlgorithm;
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
-    private final Rect mStableInsets = new Rect();
     private final Rect mTmpInsets = new Rect();
-    private final Point mTmpDisplaySize = new Point();
 
     /**
      * Tracks the destination bounds, used for any following
@@ -66,7 +63,6 @@
      */
     private final Rect mLastDestinationBounds = new Rect();
 
-    private IPinnedStackController mPinnedStackController;
     private ComponentName mLastPipComponentName;
     private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
     private Size mReentrySize = null;
@@ -80,7 +76,6 @@
     private Point mScreenEdgeInsets;
     private int mCurrentMinSize;
 
-    private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
     private boolean mIsShelfShowing;
@@ -123,10 +118,6 @@
                 com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
     }
 
-    public void setPinnedStackController(IPinnedStackController controller) {
-        mPinnedStackController = controller;
-    }
-
     public void setMinEdgeSize(int minEdgeSize) {
         mCurrentMinSize = minEdgeSize;
     }
@@ -155,14 +146,6 @@
     }
 
     /**
-     * Responds to IPinnedStackListener on minimized state change.
-     */
-    public void onMinimizedStateChanged(boolean minimized) {
-        mIsMinimized = minimized;
-        mSnapAlgorithm.setMinimized(minimized);
-    }
-
-    /**
      * Responds to IPinnedStackListener on movement bounds change.
      * Note that both inset and normal bounds will be calculated here rather than in the caller.
      */
@@ -238,9 +221,9 @@
     }
 
     /**
-     * Responds to IPinnedStackListener on preparing the pinned stack animation.
+     * @return {@link Rect} of the destination PiP window bounds.
      */
-    public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
+    Rect getDestinationBounds(float aspectRatio, Rect bounds) {
         final Rect destinationBounds;
         final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
         if (bounds == null) {
@@ -253,17 +236,16 @@
                     false /* useCurrentMinEdgeSize */);
         }
         if (destinationBounds.equals(bounds)) {
-            return;
+            return bounds;
         }
         mAspectRatio = aspectRatio;
         onResetReentryBoundsUnchecked();
-        try {
-            mPinnedStackController.startAnimation(destinationBounds, sourceRectHint,
-                    -1 /* animationDuration */);
-            mLastDestinationBounds.set(destinationBounds);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to start PiP animation from SysUI", e);
-        }
+        mLastDestinationBounds.set(destinationBounds);
+        return destinationBounds;
+    }
+
+    float getDefaultAspectRatio() {
+        return mDefaultAspectRatio;
     }
 
     /**
@@ -307,18 +289,10 @@
                 false /* adjustForIme */);
         mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
                 snapFraction);
-        if (mIsMinimized) {
-            applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds);
-        }
 
-        try {
-            outBounds.set(postChangeStackBounds);
-            mLastDestinationBounds.set(outBounds);
-            mPinnedStackController.resetBoundsAnimation(outBounds);
-            t.setBounds(pinnedStackInfo.stackToken, outBounds);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to resize PiP on display rotation", e);
-        }
+        outBounds.set(postChangeStackBounds);
+        mLastDestinationBounds.set(outBounds);
+        t.setBounds(pinnedStackInfo.stackToken, outBounds);
         return true;
     }
 
@@ -370,9 +344,6 @@
         final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
         stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
         mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
-        if (mIsMinimized) {
-            applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds));
-        }
     }
 
     /**
@@ -436,20 +407,6 @@
     }
 
     /**
-     * Applies the minimized offsets to the given stack bounds.
-     */
-    private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
-        mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
-        try {
-            mWindowManager.getStableInsets(mContext.getDisplayId(), mStableInsets);
-            mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
-                    mStableInsets);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get stable insets from WM", e);
-        }
-    }
-
-    /**
      * @return the default snap fraction to apply instead of the default gravity when calculating
      *         the default stack bounds when first entering PiP.
      */
@@ -486,7 +443,6 @@
         pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio);
         pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
         pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity);
-        pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
         pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
         pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
         pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
index f3e707c..6b89718 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java
@@ -63,14 +63,9 @@
 
     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
 
-    private final int mMinimizedVisibleSize;
-    private boolean mIsMinimized;
-
     public PipSnapAlgorithm(Context context) {
         Resources res = context.getResources();
         mContext = context;
-        mMinimizedVisibleSize = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.pip_minimized_visible_size);
         mDefaultSizePercent = res.getFloat(
                 com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
         mMaxAspectRatioForMinSize = res.getFloat(
@@ -92,13 +87,6 @@
     }
 
     /**
-     * Sets the PIP's minimized state.
-     */
-    public void setMinimized(boolean isMinimized) {
-        mIsMinimized = isMinimized;
-    }
-
-    /**
      * @return the closest absolute snap stack bounds for the given {@param stackBounds} moving at
      * the given {@param velocityX} and {@param velocityY}.  The {@param movementBounds} should be
      * those for the given {@param stackBounds}.
@@ -235,20 +223,6 @@
     }
 
     /**
-     * Applies the offset to the {@param stackBounds} to adjust it to a minimized state.
-     */
-    public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize,
-            Rect stableInsets) {
-        if (stackBounds.left <= movementBounds.centerX()) {
-            stackBounds.offsetTo(stableInsets.left + mMinimizedVisibleSize - stackBounds.width(),
-                    stackBounds.top);
-        } else {
-            stackBounds.offsetTo(displaySize.x - stableInsets.right - mMinimizedVisibleSize,
-                    stackBounds.top);
-        }
-    }
-
-    /**
      * @return returns a fraction that describes where along the {@param movementBounds} the
      *         {@param stackBounds} are. If the {@param stackBounds} are not currently on the
      *         {@param movementBounds} exactly, then they will be snapped to the movement bounds.
@@ -402,16 +376,11 @@
      * the new bounds out to {@param boundsOut}.
      */
     private void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut) {
-        // If the stackBounds are minimized, then it should only be snapped back horizontally
         final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right,
                 stackBounds.left));
         final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom,
                 stackBounds.top));
         boundsOut.set(stackBounds);
-        if (mIsMinimized) {
-            boundsOut.offsetTo(boundedLeft, boundedTop);
-            return;
-        }
 
         // Otherwise, just find the closest edge
         final int fromLeft = Math.abs(stackBounds.left - movementBounds.left);
@@ -479,7 +448,5 @@
         pw.println(prefix + PipSnapAlgorithm.class.getSimpleName());
         pw.println(innerPrefix + "mSnapMode=" + mSnapMode);
         pw.println(innerPrefix + "mOrientation=" + mOrientation);
-        pw.println(innerPrefix + "mMinimizedVisibleSize=" + mMinimizedVisibleSize);
-        pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
new file mode 100644
index 0000000..1555153
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip;
+
+import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
+import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
+import static com.android.systemui.pip.PipAnimationController.DURATION_NONE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.ITaskOrganizerController;
+import android.app.PictureInPictureParams;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.DisplayInfo;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.WindowContainerTransaction;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Manages PiP tasks such as resize and offset.
+ *
+ * This class listens on {@link ITaskOrganizer} callbacks for windowing mode change
+ * both to and from PiP and issues corresponding animation if applicable.
+ * Normally, we apply series of {@link SurfaceControl.Transaction} when the animator is running
+ * and files a final {@link WindowContainerTransaction} at the end of the transition.
+ *
+ * This class is also responsible for general resize/offset PiP operations within SysUI component,
+ * see also {@link com.android.systemui.pip.phone.PipMotionHelper}.
+ */
+public class PipTaskOrganizer extends ITaskOrganizer.Stub {
+    private static final String TAG = PipTaskOrganizer.class.getSimpleName();
+
+    private final Handler mMainHandler;
+    private final ITaskOrganizerController mTaskOrganizerController;
+    private final PipBoundsHandler mPipBoundsHandler;
+    private final PipAnimationController mPipAnimationController;
+    private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
+    private final Rect mDisplayBounds = new Rect();
+    private final Rect mLastReportedBounds = new Rect();
+
+    private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
+            new PipAnimationController.PipAnimationCallback() {
+        @Override
+        public void onPipAnimationStart(IWindowContainer wc,
+                PipAnimationController.PipTransitionAnimator animator) {
+            mMainHandler.post(() -> {
+                for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+                    final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+                    callback.onPipTransitionStarted();
+                }
+            });
+        }
+
+        @Override
+        public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx,
+                PipAnimationController.PipTransitionAnimator animator) {
+            mMainHandler.post(() -> {
+                for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+                    final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+                    callback.onPipTransitionFinished();
+                }
+            });
+            final Rect destinationBounds = animator.getDestinationBounds();
+            mLastReportedBounds.set(destinationBounds);
+            try {
+                final WindowContainerTransaction wct = new WindowContainerTransaction();
+                if (animator.shouldScheduleFinishPip()) {
+                    wct.scheduleFinishEnterPip(wc, destinationBounds);
+                } else {
+                    wct.setBounds(wc, destinationBounds);
+                }
+                wct.setBoundsChangeTransaction(wc, tx);
+                mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to apply container transaction", e);
+            }
+        }
+
+        @Override
+        public void onPipAnimationCancel(IWindowContainer wc,
+                PipAnimationController.PipTransitionAnimator animator) {
+            mMainHandler.post(() -> {
+                for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
+                    final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
+                    callback.onPipTransitionCanceled();
+                }
+            });
+        }
+    };
+
+    private ActivityManager.RunningTaskInfo mTaskInfo;
+    private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+
+    public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) {
+        mMainHandler = new Handler(Looper.getMainLooper());
+        mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController();
+        mPipBoundsHandler = boundsHandler;
+        mPipAnimationController = new PipAnimationController(context);
+    }
+
+    /**
+     * Resize the PiP window, animate if the given duration is not {@link #DURATION_NONE}
+     */
+    public void resizePinnedStack(Rect destinationBounds, int durationMs) {
+        Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
+        resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip */,
+                mLastReportedBounds, destinationBounds, durationMs);
+    }
+
+    /**
+     * Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE}
+     */
+    public void offsetPinnedStack(Rect originalBounds, int xOffset, int yOffset, int durationMs) {
+        if (mTaskInfo == null) {
+            Log.w(TAG, "mTaskInfo is not set");
+            return;
+        }
+        final Rect destinationBounds = new Rect(originalBounds);
+        destinationBounds.offset(xOffset, yOffset);
+        resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip*/,
+                originalBounds, destinationBounds, durationMs);
+    }
+
+    /**
+     * Registers {@link PipTransitionCallback} to receive transition callbacks.
+     */
+    public void registerPipTransitionCallback(PipTransitionCallback callback) {
+        mPipTransitionCallbacks.add(callback);
+    }
+
+    /**
+     * Sets the preferred animation type for one time.
+     * This is typically used to set the animation type to {@link #ANIM_TYPE_ALPHA}.
+     */
+    public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
+        mOneShotAnimationType = animationType;
+    }
+
+    /**
+     * Updates the display dimension with given {@link DisplayInfo}
+     */
+    public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+        mDisplayBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+    }
+
+    /**
+     * Callback to issue the final {@link WindowContainerTransaction} on end of movements.
+     * @param destinationBounds the final bounds.
+     */
+    public void onMotionMovementEnd(Rect destinationBounds) {
+        try {
+            mLastReportedBounds.set(destinationBounds);
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.setBounds(mTaskInfo.token, destinationBounds);
+            mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to apply window container transaction", e);
+        }
+    }
+
+    @Override
+    public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+        Objects.requireNonNull(info, "Requires RunningTaskInfo");
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+                getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
+        Objects.requireNonNull(destinationBounds, "Missing destination bounds");
+        mTaskInfo = info;
+        if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
+            final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
+            resizePinnedStackInternal(mTaskInfo.token, true /* scheduleFinishPip */,
+                    currentBounds, destinationBounds,
+                    PipAnimationController.DURATION_DEFAULT_MS);
+        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+            mMainHandler.post(() -> mPipAnimationController
+                    .getAnimator(mTaskInfo.token, true /* scheduleFinishPip */,
+                            destinationBounds, 0f, 1f)
+                    .setPipAnimationCallback(mPipAnimationCallback)
+                    .setDuration(PipAnimationController.DURATION_DEFAULT_MS)
+                    .start());
+            mOneShotAnimationType = ANIM_TYPE_BOUNDS;
+        } else {
+            throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType);
+        }
+    }
+
+    @Override
+    public void taskVanished(IWindowContainer token) {
+        Objects.requireNonNull(token, "Requires valid IWindowContainer");
+        if (token.asBinder() != mTaskInfo.token.asBinder()) {
+            Log.wtf(TAG, "Unrecognized token: " + token);
+            return;
+        }
+        resizePinnedStackInternal(token, false /* scheduleFinishPip */,
+                mLastReportedBounds, mDisplayBounds,
+                PipAnimationController.DURATION_DEFAULT_MS);
+    }
+
+    @Override
+    public void transactionReady(int id, SurfaceControl.Transaction t) {
+    }
+
+    @Override
+    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+                getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
+        Objects.requireNonNull(destinationBounds, "Missing destination bounds");
+        resizePinnedStack(destinationBounds, PipAnimationController.DURATION_DEFAULT_MS);
+    }
+
+    private void resizePinnedStackInternal(IWindowContainer wc, boolean scheduleFinishPip,
+            Rect currentBounds, Rect destinationBounds, int animationDurationMs) {
+        try {
+            // Could happen when dismissPip
+            if (wc == null || wc.getLeash() == null) {
+                Log.w(TAG, "Abort animation, invalid leash");
+                return;
+            }
+            final SurfaceControl leash = wc.getLeash();
+            if (animationDurationMs == DURATION_NONE) {
+                // Directly resize if no animation duration is set. When fling, wait for final
+                // callback to issue the proper WindowContainerTransaction with destination bounds.
+                new SurfaceControl.Transaction()
+                        .setPosition(leash, destinationBounds.left, destinationBounds.top)
+                        .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
+                        .apply();
+            } else {
+                mMainHandler.post(() -> mPipAnimationController
+                        .getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds)
+                        .setPipAnimationCallback(mPipAnimationCallback)
+                        .setDuration(animationDurationMs)
+                        .start());
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "Abort animation, invalid window container", e);
+        } catch (Exception e) {
+            Log.e(TAG, "Should not reach here, terrible thing happened", e);
+        }
+    }
+
+    private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
+        return params == null
+                ? mPipBoundsHandler.getDefaultAspectRatio()
+                : params.getAspectRatio();
+    }
+
+    /**
+     * Callback interface for PiP transitions (both from and to PiP mode)
+     */
+    public interface PipTransitionCallback {
+        /**
+         * Callback when the pip transition is started.
+         */
+        void onPipTransitionStarted();
+
+        /**
+         * Callback when the pip transition is finished.
+         */
+        void onPipTransitionFinished();
+
+        /**
+         * Callback when the pip transition is cancelled.
+         */
+        void onPipTransitionCanceled();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
index 599c845..4fb675e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java
@@ -25,6 +25,7 @@
 import android.os.UserManager;
 
 import com.android.systemui.SystemUI;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.statusbar.CommandQueue;
 
 import java.io.FileDescriptor;
@@ -98,6 +99,18 @@
         mPipManager.setShelfHeight(visible, height);
     }
 
+    public void setPinnedStackAnimationType(int animationType) {
+        if (mPipManager != null) {
+            mPipManager.setPinnedStackAnimationType(animationType);
+        }
+    }
+
+    public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+        if (mPipManager != null) {
+            mPipManager.setPinnedStackAnimationListener(listener);
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mPipManager == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index cb94e28..e98dec0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -41,6 +41,8 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.pip.BasePipManager;
 import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
@@ -59,12 +61,11 @@
  * Manages the picture-in-picture (PIP) UI and states for Phones.
  */
 @Singleton
-public class PipManager implements BasePipManager {
+public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback {
     private static final String TAG = "PipManager";
 
     private Context mContext;
     private IActivityManager mActivityManager;
-    private IActivityTaskManager mActivityTaskManager;
     private Handler mHandler = new Handler();
 
     private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
@@ -78,7 +79,9 @@
     private PipMenuActivityController mMenuController;
     private PipMediaController mMediaController;
     private PipTouchHandler mTouchHandler;
+    private PipTaskOrganizer mPipTaskOrganizer;
     private PipAppOpsListener mAppOpsListener;
+    private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener;
 
     /**
      * Handler for display rotation changes.
@@ -124,20 +127,6 @@
         }
 
         @Override
-        public void onPinnedStackAnimationStarted() {
-            // Disable touches while the animation is running
-            mTouchHandler.setTouchEnabled(false);
-        }
-
-        @Override
-        public void onPinnedStackAnimationEnded() {
-            // Re-enable touches after the animation completes
-            mTouchHandler.setTouchEnabled(true);
-            mTouchHandler.onPinnedStackAnimationEnded();
-            mMenuController.onPinnedStackAnimationEnded();
-        }
-
-        @Override
         public void onPinnedActivityRestartAttempt(boolean clearedTask) {
             mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */);
         }
@@ -149,10 +138,7 @@
     private class PipManagerPinnedStackListener extends PinnedStackListener {
         @Override
         public void onListenerRegistered(IPinnedStackController controller) {
-            mHandler.post(() -> {
-                mPipBoundsHandler.setPinnedStackController(controller);
-                mTouchHandler.setPinnedStackController(controller);
-            });
+            mHandler.post(() -> mTouchHandler.setPinnedStackController(controller));
         }
 
         @Override
@@ -164,18 +150,9 @@
         }
 
         @Override
-        public void onMinimizedStateChanged(boolean isMinimized) {
-            mHandler.post(() -> {
-                mPipBoundsHandler.onMinimizedStateChanged(isMinimized);
-                mTouchHandler.setMinimizedState(isMinimized, true /* fromController */);
-            });
-        }
-
-        @Override
-        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
-                boolean fromShelfAdjustment) {
+        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {
             mHandler.post(() -> updateMovementBounds(animatingBounds, fromImeAdjustment,
-                    fromShelfAdjustment));
+                    false /* fromShelfAdjustment */));
         }
 
         @Override
@@ -207,7 +184,10 @@
 
         @Override
         public void onDisplayInfoChanged(DisplayInfo displayInfo) {
-            mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo));
+            mHandler.post(() -> {
+                mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+                mPipTaskOrganizer.onDisplayInfoChanged(displayInfo);
+            });
         }
 
         @Override
@@ -219,13 +199,6 @@
         public void onAspectRatioChanged(float aspectRatio) {
             mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio));
         }
-
-        @Override
-        public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
-            mHandler.post(() -> {
-                mPipBoundsHandler.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
-            });
-        }
     }
 
     @Inject
@@ -234,7 +207,6 @@
             FloatingContentCoordinator floatingContentCoordinator) {
         mContext = context;
         mActivityManager = ActivityManager.getService();
-        mActivityTaskManager = ActivityTaskManager.getService();
 
         try {
             WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
@@ -243,24 +215,29 @@
         }
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
 
+        final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
         mPipBoundsHandler = new PipBoundsHandler(context);
+        mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+        mPipTaskOrganizer.registerPipTransitionCallback(this);
         mInputConsumerController = InputConsumerController.getPipInputConsumer();
         mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
-        mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
+        mMenuController = new PipMenuActivityController(context, mMediaController,
                 mInputConsumerController);
-        mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager,
-                mMenuController, mInputConsumerController, mPipBoundsHandler,
+        mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager,
+                mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
                 floatingContentCoordinator);
         mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
                 mTouchHandler.getMotionHelper());
         displayController.addDisplayChangingController(mRotationController);
 
-        // If SystemUI restart, and it already existed a pinned stack,
-        // register the pip input consumer to ensure touch can send to it.
         try {
-            ActivityManager.StackInfo stackInfo = mActivityTaskManager.getStackInfo(
+            ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
+                    mPipTaskOrganizer, WINDOWING_MODE_PINNED);
+            ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo(
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
             if (stackInfo != null) {
+                // If SystemUI restart, and it already existed a pinned stack,
+                // register the pip input consumer to ensure touch can send to it.
                 mInputConsumerController.registerInputConsumer();
             }
         } catch (RemoteException e) {
@@ -320,6 +297,46 @@
         });
     }
 
+    @Override
+    public void setPinnedStackAnimationType(int animationType) {
+        mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType));
+    }
+
+    @Override
+    public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+        mHandler.post(() -> mPinnedStackAnimationRecentsListener = listener);
+    }
+
+    @Override
+    public void onPipTransitionStarted() {
+        // Disable touches while the animation is running
+        mTouchHandler.setTouchEnabled(false);
+        if (mPinnedStackAnimationRecentsListener != null) {
+            try {
+                mPinnedStackAnimationRecentsListener.onPinnedStackAnimationStarted();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to callback recents", e);
+            }
+        }
+    }
+
+    @Override
+    public void onPipTransitionFinished() {
+        onPipTransitionFinishedOrCanceled();
+    }
+
+    @Override
+    public void onPipTransitionCanceled() {
+        onPipTransitionFinishedOrCanceled();
+    }
+
+    private void onPipTransitionFinishedOrCanceled() {
+        // Re-enable touches after the animation completes
+        mTouchHandler.setTouchEnabled(true);
+        mTouchHandler.onPinnedStackAnimationEnded();
+        mMenuController.onPinnedStackAnimationEnded();
+    }
+
     private void updateMovementBounds(Rect animatingBounds, boolean fromImeAdjustment,
             boolean fromShelfAdjustment) {
         // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index c7bfc06..d660b67 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -22,7 +22,6 @@
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
 import android.app.RemoteAction;
 import android.content.Context;
 import android.content.Intent;
@@ -68,11 +67,8 @@
 
     public static final int MESSAGE_MENU_STATE_CHANGED = 100;
     public static final int MESSAGE_EXPAND_PIP = 101;
-    public static final int MESSAGE_MINIMIZE_PIP = 102;
     public static final int MESSAGE_DISMISS_PIP = 103;
     public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
-    public static final int MESSAGE_REGISTER_INPUT_CONSUMER = 105;
-    public static final int MESSAGE_UNREGISTER_INPUT_CONSUMER = 106;
     public static final int MESSAGE_SHOW_MENU = 107;
 
     public static final int MENU_STATE_NONE = 0;
@@ -100,11 +96,6 @@
         void onPipExpand();
 
         /**
-         * Called when the PIP requested to be minimized.
-         */
-        void onPipMinimize();
-
-        /**
          * Called when the PIP requested to be dismissed.
          */
         void onPipDismiss();
@@ -116,7 +107,6 @@
     }
 
     private Context mContext;
-    private IActivityManager mActivityManager;
     private PipMediaController mMediaController;
     private InputConsumerController mInputConsumerController;
 
@@ -146,10 +136,6 @@
                     mListeners.forEach(l -> l.onPipExpand());
                     break;
                 }
-                case MESSAGE_MINIMIZE_PIP: {
-                    mListeners.forEach(l -> l.onPipMinimize());
-                    break;
-                }
                 case MESSAGE_DISMISS_PIP: {
                     mListeners.forEach(l -> l.onPipDismiss());
                     break;
@@ -194,10 +180,9 @@
         }
     };
 
-    public PipMenuActivityController(Context context, IActivityManager activityManager,
+    public PipMenuActivityController(Context context,
             PipMediaController mediaController, InputConsumerController inputConsumerController) {
         mContext = context;
-        mActivityManager = activityManager;
         mMediaController = mediaController;
         mInputConsumerController = inputConsumerController;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index c6e2852..91f539c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager.StackInfo;
-import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
 import android.content.Context;
 import android.graphics.Point;
@@ -39,7 +38,9 @@
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.internal.os.SomeArgs;
+import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.util.FloatingContentCoordinator;
@@ -65,8 +66,6 @@
     /** Friction to use for PIP when it moves via physics fling animations. */
     private static final float DEFAULT_FRICTION = 2f;
 
-    // The fraction of the stack width that the user has to drag offscreen to minimize the PiP
-    private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f;
     // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
     private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;
 
@@ -74,10 +73,10 @@
     private static final int MSG_RESIZE_ANIMATE = 2;
     private static final int MSG_OFFSET_ANIMATE = 3;
 
-    private Context mContext;
-    private IActivityManager mActivityManager;
-    private IActivityTaskManager mActivityTaskManager;
-    private Handler mHandler;
+    private final Context mContext;
+    private final IActivityTaskManager mActivityTaskManager;
+    private final PipTaskOrganizer mPipTaskOrganizer;
+    private final Handler mHandler;
 
     private PipMenuActivityController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
@@ -139,14 +138,14 @@
                 new PhysicsAnimator.SpringConfig(
                         SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
 
-    public PipMotionHelper(Context context, IActivityManager activityManager,
-            IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
+    public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
+            PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
             PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
             FloatingContentCoordinator floatingContentCoordinator) {
         mContext = context;
         mHandler = new Handler(ForegroundThread.get().getLooper(), this);
-        mActivityManager = activityManager;
         mActivityTaskManager = activityTaskManager;
+        mPipTaskOrganizer = pipTaskOrganizer;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
         mFlingAnimationUtils = flingAnimationUtils;
@@ -285,35 +284,6 @@
     }
 
     /**
-     * @return the closest minimized PiP bounds.
-     */
-    Rect getClosestMinimizedBounds(Rect stackBounds) {
-        Point displaySize = new Point();
-        mContext.getDisplay().getRealSize(displaySize);
-        Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, stackBounds);
-        mSnapAlgorithm.applyMinimizedOffset(toBounds, mMovementBounds, displaySize, mStableInsets);
-        return toBounds;
-    }
-
-    /**
-     * @return whether the PiP at the current bounds should be minimized.
-     */
-    boolean shouldMinimizePip() {
-        Point displaySize = new Point();
-        mContext.getDisplay().getRealSize(displaySize);
-        if (mBounds.left < 0) {
-            float offscreenFraction = (float) -mBounds.left / mBounds.width();
-            return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
-        } else if (mBounds.right > displaySize.x) {
-            float offscreenFraction = (float) (mBounds.right - displaySize.x) /
-                    mBounds.width();
-            return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
-        } else {
-            return false;
-        }
-    }
-
-    /**
      * @return whether the PiP at the current bounds should be dismissed.
      */
     boolean shouldDismissPip() {
@@ -328,25 +298,6 @@
     }
 
     /**
-     * Animates the PiP to the minimized state, slightly offscreen.
-     */
-    void animateToClosestMinimizedState(@Nullable Runnable updateAction) {
-        final Rect toBounds = getClosestMinimizedBounds(mBounds);
-
-        mAnimatedBounds.set(mBounds);
-        mAnimatedBoundsPhysicsAnimator
-                .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
-                .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
-
-        if (updateAction != null) {
-            mAnimatedBoundsPhysicsAnimator.addUpdateListener(
-                    (target, values) -> updateAction.run());
-        }
-
-        startBoundsAnimation();
-    }
-
-    /**
      * Flings the PiP to the closest snap target.
      */
     void flingToSnapTarget(
@@ -436,8 +387,7 @@
      * Animates the PiP from the expanded state to the normal state after the menu is hidden.
      */
     void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
-            Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized,
-            boolean immediate) {
+            Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) {
         if (savedSnapFraction < 0f) {
             // If there are no saved snap fractions, then just use the current bounds
             savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
@@ -445,10 +395,6 @@
         }
         mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
 
-        if (minimized) {
-            normalBounds = getClosestMinimizedBounds(normalBounds);
-        }
-
         if (immediate) {
             movePip(normalBounds);
         } else {
@@ -503,6 +449,7 @@
         cancelAnimations();
 
         mAnimatedBoundsPhysicsAnimator
+                .withEndActions(() ->  mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds))
                 .addUpdateListener(mResizePipVsyncUpdateListener)
                 .start();
     }
@@ -594,13 +541,6 @@
     }
 
     /**
-     * @return the distance between points {@param p1} and {@param p2}.
-     */
-    private float distanceBetweenRectOffsets(Rect r1, Rect r2) {
-        return PointF.length(r1.left - r2.left, r1.top - r2.top);
-    }
-
-    /**
      * Handles messages to be processed on the background thread.
      */
     public boolean handleMessage(Message msg) {
@@ -608,13 +548,8 @@
             case MSG_RESIZE_IMMEDIATE: {
                 SomeArgs args = (SomeArgs) msg.obj;
                 Rect toBounds = (Rect) args.arg1;
-                try {
-                    mActivityTaskManager.resizePinnedStack(
-                            toBounds, null /* tempPinnedTaskBounds */);
-                    mBounds.set(toBounds);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
-                }
+                mPipTaskOrganizer.resizePinnedStack(toBounds, PipAnimationController.DURATION_NONE);
+                mBounds.set(toBounds);
                 return true;
             }
 
@@ -631,8 +566,7 @@
                         return true;
                     }
 
-                    mActivityTaskManager.animateResizePinnedStack(stackInfo.stackId, toBounds,
-                            duration);
+                    mPipTaskOrganizer.resizePinnedStack(toBounds, duration);
                     mBounds.set(toBounds);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
@@ -654,8 +588,8 @@
                         return true;
                     }
 
-                    mActivityTaskManager.offsetPinnedStackBounds(stackInfo.stackId, originalBounds,
-                            0/* xOffset */, offset, duration);
+                    mPipTaskOrganizer.offsetPinnedStack(originalBounds,
+                            0 /* xOffset */, offset, duration);
                     Rect toBounds = new Rect(originalBounds);
                     toBounds.offset(0, offset);
                     mBounds.set(toBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 8e588e6..79a25b2 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -45,6 +45,7 @@
 import com.android.systemui.R;
 import com.android.systemui.pip.PipBoundsHandler;
 import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.util.FloatingContentCoordinator;
@@ -58,8 +59,6 @@
 public class PipTouchHandler {
     private static final String TAG = "PipTouchHandler";
 
-    // Allow the PIP to be dragged to the edge of the screen to be minimized.
-    private static final boolean ENABLE_MINIMIZE = false;
     // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
     private static final boolean ENABLE_FLING_DISMISS = false;
 
@@ -67,12 +66,9 @@
     private static final int BOTTOM_OFFSET_BUFFER_DP = 1;
 
     // Allow dragging the PIP to a location to close it
-    private final boolean mEnableDimissDragToEdge;
+    private final boolean mEnableDismissDragToEdge;
     private final Context mContext;
     private final IActivityManager mActivityManager;
-    private final IActivityTaskManager mActivityTaskManager;
-    private final ViewConfiguration mViewConfig;
-    private final PipMenuListener mMenuListener = new PipMenuListener();
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipResizeGestureHandler mPipResizeGestureHandler;
     private IPinnedStackController mPinnedStackController;
@@ -104,7 +100,7 @@
     private Runnable mShowDismissAffordance = new Runnable() {
         @Override
         public void run() {
-            if (mEnableDimissDragToEdge) {
+            if (mEnableDismissDragToEdge) {
                 mDismissViewController.showDismissTarget();
             }
         }
@@ -112,7 +108,6 @@
 
     // Behaviour states
     private int mMenuState = MENU_STATE_NONE;
-    private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
     private int mImeOffset;
@@ -121,7 +116,6 @@
     private int mMovementBoundsExtraOffsets;
     private float mSavedSnapFraction = -1f;
     private boolean mSendingHoverAccessibilityEvents;
-    private boolean mMovementWithinMinimize;
     private boolean mMovementWithinDismiss;
     private PipAccessibilityInteractionConnection mConnection;
 
@@ -146,15 +140,7 @@
 
         @Override
         public void onPipExpand() {
-            if (!mIsMinimized) {
-                mMotionHelper.expandPip();
-            }
-        }
-
-        @Override
-        public void onPipMinimize() {
-            setMinimizedStateInternal(true);
-            mMotionHelper.animateToClosestMinimizedState(null /* updateAction */);
+            mMotionHelper.expandPip();
         }
 
         @Override
@@ -175,26 +161,24 @@
             IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
             InputConsumerController inputConsumerController,
             PipBoundsHandler pipBoundsHandler,
+            PipTaskOrganizer pipTaskOrganizer,
             FloatingContentCoordinator floatingContentCoordinator) {
-
         // Initialize the Pip input consumer
         mContext = context;
         mActivityManager = activityManager;
-        mActivityTaskManager = activityTaskManager;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
-        mViewConfig = ViewConfiguration.get(context);
         mMenuController = menuController;
-        mMenuController.addListener(mMenuListener);
+        mMenuController.addListener(new PipMenuListener());
         mDismissViewController = new PipDismissViewController(context);
         mSnapAlgorithm = new PipSnapAlgorithm(mContext);
         mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(),
                 2.5f);
         mGesture = new DefaultPipTouchGesture();
-        mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
+        mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer,
                 mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper);
-        mTouchState = new PipTouchState(mViewConfig, mHandler,
+        mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
 
@@ -203,7 +187,7 @@
                 R.dimen.pip_expanded_shortest_edge_size);
         mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
 
-        mEnableDimissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
+        mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
 
         // Register the listener for input consumer touch events
         inputConsumerController.setInputListener(this::handleTouchEvent);
@@ -339,8 +323,7 @@
         // If we have a deferred resize, apply it now
         if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
             mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
-                    mNormalMovementBounds, mMovementBounds, mIsMinimized,
-                    true /* immediate */);
+                    mNormalMovementBounds, mMovementBounds, true /* immediate */);
             mSavedSnapFraction = -1f;
             mDeferResizeToNormalBoundsUntilRotation = -1;
         }
@@ -482,44 +465,6 @@
     }
 
     /**
-     * Sets the minimized state.
-     */
-    private void setMinimizedStateInternal(boolean isMinimized) {
-        if (!ENABLE_MINIMIZE) {
-            return;
-        }
-        setMinimizedState(isMinimized, false /* fromController */);
-    }
-
-    /**
-     * Sets the minimized state.
-     */
-    void setMinimizedState(boolean isMinimized, boolean fromController) {
-        if (!ENABLE_MINIMIZE) {
-            return;
-        }
-        if (mIsMinimized != isMinimized) {
-            MetricsLoggerWrapper.logPictureInPictureMinimize(mContext,
-                    isMinimized, PipUtils.getTopPinnedActivity(mContext, mActivityManager));
-        }
-        mIsMinimized = isMinimized;
-        mSnapAlgorithm.setMinimized(isMinimized);
-
-        if (fromController) {
-            if (isMinimized) {
-                // Move the PiP to the new bounds immediately if minimized
-                mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds));
-            }
-        } else if (mPinnedStackController != null) {
-            try {
-                mPinnedStackController.setIsMinimized(isMinimized);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not set minimized state", e);
-            }
-        }
-    }
-
-    /**
      * Sets the menu visibility.
      */
     private void setMenuState(int menuState, boolean resize) {
@@ -562,8 +507,7 @@
                 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
                     Rect normalBounds = new Rect(mNormalBounds);
                     mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
-                            mNormalMovementBounds, mMovementBounds, mIsMinimized,
-                            false /* immediate */);
+                            mNormalMovementBounds, mMovementBounds, false /* immediate */);
                     mSavedSnapFraction = -1f;
                 }
             } else {
@@ -601,8 +545,6 @@
      * Gesture controlling normal movement of the PIP.
      */
     private class DefaultPipTouchGesture extends PipTouchGesture {
-        // Whether the PiP was on the left side of the screen at the start of the gesture
-        private boolean mStartedOnLeft;
         private final Point mStartPosition = new Point();
         private final PointF mDelta = new PointF();
 
@@ -615,17 +557,15 @@
             Rect bounds = mMotionHelper.getBounds();
             mDelta.set(0f, 0f);
             mStartPosition.set(bounds.left, bounds.top);
-            mStartedOnLeft = bounds.left < mMovementBounds.centerX();
-            mMovementWithinMinimize = true;
             mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
 
-            // If the menu is still visible, and we aren't minimized, then just poke the menu
+            // If the menu is still visible then just poke the menu
             // so that it will timeout after the user stops touching it
-            if (mMenuState != MENU_STATE_NONE && !mIsMinimized) {
+            if (mMenuState != MENU_STATE_NONE) {
                 mMenuController.pokeMenu();
             }
 
-            if (mEnableDimissDragToEdge) {
+            if (mEnableDismissDragToEdge) {
                 mDismissViewController.createDismissTarget();
                 mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
             }
@@ -640,7 +580,7 @@
             if (touchState.startedDragging()) {
                 mSavedSnapFraction = -1f;
 
-                if (mEnableDimissDragToEdge) {
+                if (mEnableDismissDragToEdge) {
                     mHandler.removeCallbacks(mShowDismissAffordance);
                     mDismissViewController.showDismissTarget();
                 }
@@ -662,17 +602,11 @@
                 mTmpBounds.offsetTo((int) left, (int) top);
                 mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
 
-                if (mEnableDimissDragToEdge) {
+                if (mEnableDismissDragToEdge) {
                     updateDismissFraction();
                 }
 
                 final PointF curPos = touchState.getLastTouchPosition();
-                if (mMovementWithinMinimize) {
-                    // Track if movement remains near starting edge to identify swipes to minimize
-                    mMovementWithinMinimize = mStartedOnLeft
-                            ? curPos.x <= mMovementBounds.left + mTmpBounds.width()
-                            : curPos.x >= mMovementBounds.right;
-                }
                 if (mMovementWithinDismiss) {
                     // Track if movement remains near the bottom edge to identify swipe to dismiss
                     mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom;
@@ -684,7 +618,7 @@
 
         @Override
         public boolean onUp(PipTouchState touchState) {
-            if (mEnableDimissDragToEdge) {
+            if (mEnableDismissDragToEdge) {
                 // Clean up the dismiss target regardless of the touch state in case the touch
                 // enabled state changes while the user is interacting
                 cleanUpDismissTarget();
@@ -704,7 +638,7 @@
                             vel.y, isFling);
             final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal
                     && (mMovementWithinDismiss || isUpWithinDimiss);
-            if (mEnableDimissDragToEdge) {
+            if (mEnableDismissDragToEdge) {
                 // Check if the user dragged or flung the PiP offscreen to dismiss it
                 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
                     MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
@@ -717,33 +651,10 @@
             }
 
             if (touchState.isDragging()) {
-                final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize
-                        && (mStartedOnLeft ? vel.x < 0 : vel.x > 0);
-                if (ENABLE_MINIMIZE &&
-                        !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) {
-                    // Pip should be minimized
-                    setMinimizedStateInternal(true);
-                    if (mMenuState == MENU_STATE_FULL) {
-                        // If the user dragged the expanded PiP to the edge, then hiding the menu
-                        // will trigger the PiP to be scaled back to the normal size with the
-                        // minimize offset adjusted
-                        mMenuController.hideMenu();
-                    } else {
-                        mMotionHelper.animateToClosestMinimizedState(
-                                PipTouchHandler.this::updateDismissFraction /* updateAction */);
-                    }
-                    return true;
-                }
-                if (mIsMinimized) {
-                    // If we're dragging and it wasn't a minimize gesture then we shouldn't be
-                    // minimized.
-                    setMinimizedStateInternal(false);
-                }
-
                 Runnable endAction = null;
                 if (mMenuState != MENU_STATE_NONE) {
-                    // If the menu is still visible, and we aren't minimized, then just poke the
-                    // menu so that it will timeout after the user stops touching it
+                    // If the menu is still visible, then just poke the menu so that
+                    // it will timeout after the user stops touching it
                     mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
                             mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
                 } else {
@@ -759,10 +670,6 @@
                 } else {
                     mMotionHelper.animateToClosestSnapTarget();
                 }
-            } else if (mIsMinimized) {
-                // This was a tap, so no longer minimized
-                mMotionHelper.animateToClosestSnapTarget();
-                setMinimizedStateInternal(false);
             } else if (mTouchState.isDoubleTap()) {
                 // Expand to fullscreen if this is a double tap
                 mMotionHelper.expandPip();
@@ -821,14 +728,12 @@
         pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
         pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
         pw.println(innerPrefix + "mMenuState=" + mMenuState);
-        pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
         pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
         pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
         pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
         pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
         pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
-        pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDimissDragToEdge);
-        pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE);
+        pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge);
         mSnapAlgorithm.dump(pw, innerPrefix);
         mTouchState.dump(pw, innerPrefix);
         mMotionHelper.dump(pw, innerPrefix);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 487c253..cb1a218 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -50,7 +50,9 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
 import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -66,7 +68,7 @@
  * Manages the picture-in-picture (PIP) UI and states.
  */
 @Singleton
-public class PipManager implements BasePipManager {
+public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback {
     private static final String TAG = "PipManager";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -91,7 +93,6 @@
     private static final int INVALID_RESOURCE_TYPE = -1;
 
     public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
-    public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
 
     /**
      * PIPed activity is playing a media and it can be paused.
@@ -112,6 +113,7 @@
 
     private Context mContext;
     private PipBoundsHandler mPipBoundsHandler;
+    private PipTaskOrganizer mPipTaskOrganizer;
     private IActivityTaskManager mActivityTaskManager;
     private MediaSessionManager mMediaSessionManager;
     private int mState = STATE_NO_PIP;
@@ -205,8 +207,7 @@
         }
 
         @Override
-        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
-                boolean fromShelfAdjustment) {
+        public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {
             mHandler.post(() -> {
                 // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
                 mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
@@ -234,6 +235,8 @@
         mInitialized = true;
         mContext = context;
         mPipBoundsHandler = new PipBoundsHandler(context);
+        mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler);
+        mPipTaskOrganizer.registerPipTransitionCallback(this);
         mActivityTaskManager = ActivityTaskManager.getService();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
         IntentFilter intentFilter = new IntentFilter();
@@ -279,9 +282,12 @@
 
         mMediaSessionManager =
                 (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        mPipTaskOrganizer.registerPipTransitionCallback(this);
 
         try {
             WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
+            ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(
+                    mPipTaskOrganizer, WINDOWING_MODE_PINNED);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register pinned stack listener", e);
         }
@@ -422,20 +428,13 @@
             case STATE_PIP_MENU:
                 mCurrentPipBounds = mMenuModePipBounds;
                 break;
-            case STATE_PIP:
-                mCurrentPipBounds = mPipBounds;
-                break;
+            case STATE_PIP: // fallthrough
             default:
                 mCurrentPipBounds = mPipBounds;
                 break;
         }
-        try {
-            int animationDurationMs = -1;
-            mActivityTaskManager.animateResizePinnedStack(mPinnedStackId, mCurrentPipBounds,
-                    animationDurationMs);
-        } catch (RemoteException e) {
-            Log.e(TAG, "resizeStack failed", e);
-        }
+        mPipTaskOrganizer.resizePinnedStack(
+                mCurrentPipBounds, PipAnimationController.DURATION_DEFAULT_MS);
     }
 
     /**
@@ -449,13 +448,6 @@
     }
 
     /**
-     * Returns the default PIP bound.
-     */
-    public Rect getPipBounds() {
-        return mPipBounds;
-    }
-
-    /**
      * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
      * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
      */
@@ -692,19 +684,28 @@
             // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
             movePipToFullscreen();
         }
-
-        @Override
-        public void onPinnedStackAnimationEnded() {
-            if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
-
-            switch (getState()) {
-                case STATE_PIP_MENU:
-                    showPipMenu();
-                    break;
-            }
-        }
     };
 
+    @Override
+    public void onPipTransitionStarted() { }
+
+    @Override
+    public void onPipTransitionFinished() {
+        onPipTransitionFinishedOrCanceled();
+    }
+
+    @Override
+    public void onPipTransitionCanceled() {
+        onPipTransitionFinishedOrCanceled();
+    }
+
+    private void onPipTransitionFinishedOrCanceled() {
+        if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()");
+        if (getState() == STATE_PIP_MENU) {
+            showPipMenu();
+        }
+    }
+
     /**
      * A listener interface to receive notification on changes in PIP.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index fb89ed2..bfac85b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -37,8 +37,8 @@
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.R;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.qs.QS;
@@ -86,6 +86,7 @@
     private int mY;
     private boolean mOpening;
     private boolean mIsShowingNavBackdrop;
+    private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
 
     @Inject
     public QSCustomizer(Context context, AttributeSet attrs,
@@ -187,7 +188,7 @@
             int containerLocation[] = findViewById(R.id.customize_container).getLocationOnScreen();
             mX = x - containerLocation[0];
             mY = y - containerLocation[1];
-            MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
+            mUiEventLogger.log(QSEditEvent.QS_EDIT_OPEN);
             isShown = true;
             mOpening = true;
             setTileSpecs();
@@ -224,7 +225,7 @@
     public void hide() {
         final boolean animate = mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF;
         if (isShown) {
-            MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
+            mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED);
             isShown = false;
             mToolbar.dismissPopupMenus();
             setCustomizing(false);
@@ -258,7 +259,7 @@
     public boolean onMenuItemClick(MenuItem item) {
         switch (item.getItemId()) {
             case MENU_RESET:
-                MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET);
+                mUiEventLogger.log(QSEditEvent.QS_EDIT_RESET);
                 reset();
                 break;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt
new file mode 100644
index 0000000..ff8ddec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.customize
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class QSEditEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+
+    @UiEvent(doc = "Tile removed from current tiles")
+    QS_EDIT_REMOVE(210),
+    @UiEvent(doc = "Tile added to current tiles")
+    QS_EDIT_ADD(211),
+    @UiEvent(doc = "Tile moved")
+    QS_EDIT_MOVE(212),
+    @UiEvent(doc = "QS customizer open")
+    QS_EDIT_OPEN(213),
+    @UiEvent(doc = "QS customizer closed")
+    QS_EDIT_CLOSED(214),
+    @UiEvent(doc = "QS tiles reset")
+    QS_EDIT_RESET(215);
+
+    override fun getId() = _id
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 3afc460..58de95d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -40,8 +40,8 @@
 import androidx.recyclerview.widget.RecyclerView.State;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.customize.TileAdapter.Holder;
@@ -92,6 +92,7 @@
     private int mAccessibilityFromIndex;
     private CharSequence mAccessibilityFromLabel;
     private QSTileHost mHost;
+    private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
 
     public TileAdapter(Context context) {
         mContext = context;
@@ -436,20 +437,11 @@
         move(from, to, mTiles);
         updateDividerLocations();
         if (to >= mEditIndex) {
-            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE_SPEC,
-                    strip(mTiles.get(to)));
-            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE,
-                    from);
+            mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to)));
         } else if (from >= mEditIndex) {
-            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD_SPEC,
-                    strip(mTiles.get(to)));
-            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD,
-                    to);
+            mUiEventLogger.log(QSEditEvent.QS_EDIT_ADD, 0, strip(mTiles.get(to)));
         } else {
-            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE_SPEC,
-                    strip(mTiles.get(to)));
-            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE,
-                    to);
+            mUiEventLogger.log(QSEditEvent.QS_EDIT_MOVE, 0, strip(mTiles.get(to)));
         }
         saveSpecs(mHost);
         return true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 34cad51..1cd6388 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -61,9 +61,11 @@
 import com.android.internal.util.ScreenshotHelper;
 import com.android.systemui.Dumpable;
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.pip.PipAnimationController;
 import com.android.systemui.pip.PipUI;
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -388,6 +390,32 @@
             }
         }
 
+        @Override
+        public void notifySwipeToHomeFinished() {
+            if (!verifyCaller("notifySwipeToHomeFinished")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mPipUI.setPinnedStackAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
+            if (!verifyCaller("setPinnedStackAnimationListener")) {
+                return;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                mPipUI.setPinnedStackAnimationListener(listener);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         private boolean verifyCaller(String reason) {
             final int callerId = Binder.getCallingUserHandle().getIdentifier();
             if (callerId != mCurrentBoundedUserId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 5008133..97755fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -28,6 +28,7 @@
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.pm.PackageInfo;
@@ -148,6 +149,7 @@
     private int mNotificationMinHeight;
     private int mNotificationMinHeightLarge;
     private int mNotificationMinHeightMedia;
+    private int mNotificationMinHeightMessaging;
     private int mNotificationMaxHeight;
     private int mIncreasedPaddingBetweenElements;
     private int mNotificationLaunchHeight;
@@ -630,10 +632,16 @@
                 && expandedView.findViewById(com.android.internal.R.id.media_actions) != null;
         boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar();
 
+        Class<? extends Notification.Style> style =
+                mEntry.getSbn().getNotification().getNotificationStyle();
+        boolean isMessagingLayout = Notification.MessagingStyle.class.equals(style);
+
         if (customView && beforeP && !mIsSummaryWithChildren) {
             minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP;
         } else if (isMediaLayout && showCompactMediaSeekbar) {
             minHeight = mNotificationMinHeightMedia;
+        } else if (isMessagingLayout) {
+            minHeight = mNotificationMinHeightMessaging;
         } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
             minHeight = mNotificationMinHeightLarge;
         } else {
@@ -1635,6 +1643,8 @@
                 R.dimen.notification_min_height_increased);
         mNotificationMinHeightMedia = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_min_height_media);
+        mNotificationMinHeightMessaging = NotificationUtils.getFontScaledHeight(mContext,
+                R.dimen.notification_min_height_messaging);
         mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext,
                 R.dimen.notification_max_height);
         mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index d090404..7d532a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -56,8 +56,9 @@
 
     private int mHotspotState;
     private volatile int mNumConnectedDevices;
-    private volatile boolean mIsTetheringSupported;
-    private volatile boolean mHasTetherableWifiRegexs;
+    // Assume tethering is available until told otherwise
+    private volatile boolean mIsTetheringSupported = true;
+    private volatile boolean mHasTetherableWifiRegexs = true;
     private boolean mWaitingForTerminalState;
 
     private TetheringManager.TetheringEventCallback mTetheringCallback =
@@ -97,6 +98,15 @@
                 new HandlerExecutor(backgroundHandler), mTetheringCallback);
     }
 
+    /**
+     * Whether hotspot is currently supported.
+     *
+     * This will return {@code true} immediately on creation of the controller, but may be updated
+     * later. Callbacks from this controllers will notify if the state changes.
+     *
+     * @return {@code true} if hotspot is supported (or we haven't been told it's not)
+     * @see #addCallback
+     */
     @Override
     public boolean isHotspotSupported() {
         return mIsTetheringSupported && mHasTetherableWifiRegexs
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
new file mode 100644
index 0000000..6c09a46
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests against {@link PipAnimationController} to ensure that it sends the right callbacks
+ * depending on the various interactions.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipAnimationControllerTest extends SysuiTestCase {
+
+    private PipAnimationController mPipAnimationController;
+
+    @Mock
+    private IWindowContainer mWindowContainer;
+
+    @Mock
+    private PipAnimationController.PipAnimationCallback mPipAnimationCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        mPipAnimationController = new PipAnimationController(mContext);
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void getAnimator_withAlpha_returnFloatAnimator() {
+        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        new Rect(), 0f, 1f);
+
+        assertEquals("Expect ANIM_TYPE_ALPHA animation",
+                animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA);
+    }
+
+    @Test
+    public void getAnimator_withBounds_returnBoundsAnimator() {
+        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        new Rect(), new Rect());
+
+        assertEquals("Expect ANIM_TYPE_BOUNDS animation",
+                animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
+    }
+
+    @Test
+    public void getAnimator_whenSameTypeRunning_updateExistingAnimator() {
+        final Rect startValue = new Rect(0, 0, 100, 100);
+        final Rect endValue1 = new Rect(100, 100, 200, 200);
+        final Rect endValue2 = new Rect(200, 200, 300, 300);
+        final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        startValue, endValue1);
+        oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
+        oldAnimator.start();
+
+        final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        startValue, endValue2);
+
+        assertEquals("getAnimator with same type returns same animator",
+                oldAnimator, newAnimator);
+        assertEquals("getAnimator with same type updates end value",
+                endValue2, newAnimator.getEndValue());
+    }
+
+    @Test
+    public void getAnimator_scheduleFinishPip() {
+        PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        new Rect(), 0f, 1f);
+        assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip());
+
+        animator = mPipAnimationController
+                .getAnimator(mWindowContainer, false /* scheduleFinishPip */,
+                        new Rect(), 0f, 1f);
+        assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip());
+    }
+
+    @Test
+    public void pipTransitionAnimator_updateEndValue() {
+        final Rect startValue = new Rect(0, 0, 100, 100);
+        final Rect endValue1 = new Rect(100, 100, 200, 200);
+        final Rect endValue2 = new Rect(200, 200, 300, 300);
+        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        startValue, endValue1);
+
+        animator.updateEndValue(endValue2);
+
+        assertEquals("updateEndValue updates end value", animator.getEndValue(), endValue2);
+    }
+
+    @Test
+    public void pipTransitionAnimator_setPipAnimationCallback() {
+        final Rect startValue = new Rect(0, 0, 100, 100);
+        final Rect endValue = new Rect(100, 100, 200, 200);
+        final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
+                .getAnimator(mWindowContainer, true /* scheduleFinishPip */,
+                        startValue, endValue);
+        animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
+
+        animator.setPipAnimationCallback(mPipAnimationCallback);
+
+        // onAnimationStart triggers onPipAnimationStart
+        animator.onAnimationStart(animator);
+        verify(mPipAnimationCallback).onPipAnimationStart(mWindowContainer, animator);
+
+        // onAnimationCancel triggers onPipAnimationCancel
+        animator.onAnimationCancel(animator);
+        verify(mPipAnimationCallback).onPipAnimationCancel(mWindowContainer, animator);
+
+        // onAnimationEnd triggers onPipAnimationEnd
+        animator.onAnimationEnd(animator);
+        verify(mPipAnimationCallback).onPipAnimationEnd(eq(mWindowContainer),
+                any(SurfaceControl.Transaction.class), eq(animator));
+    }
+
+    /**
+     * A dummy {@link SurfaceControl.Transaction} class.
+     * This is created as {@link Mock} does not support method chaining.
+     */
+    private static class DummySurfaceControlTx extends SurfaceControl.Transaction {
+        @Override
+        public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) {
+            return this;
+        }
+
+        @Override
+        public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) {
+            return this;
+        }
+
+        @Override
+        public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) {
+            return this;
+        }
+
+        @Override
+        public void apply() {}
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index bc3ce8b..1dbcf10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -16,13 +16,7 @@
 
 package com.android.systemui.pip;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
 
 import android.content.ComponentName;
 import android.graphics.Rect;
@@ -31,7 +25,6 @@
 import android.testing.TestableResources;
 import android.view.DisplayInfo;
 import android.view.Gravity;
-import android.view.IPinnedStackController;
 
 import androidx.test.filters.SmallTest;
 
@@ -40,9 +33,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 /**
  * Unit tests against {@link PipBoundsHandler}, including but not limited to:
@@ -55,22 +45,18 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class PipBoundsHandlerTest extends SysuiTestCase {
     private static final int ROUNDING_ERROR_MARGIN = 10;
+    private static final float DEFAULT_ASPECT_RATIO = 1f;
+    private static final Rect EMPTY_CURRENT_BOUNDS = null;
 
     private PipBoundsHandler mPipBoundsHandler;
     private DisplayInfo mDefaultDisplayInfo;
-    private Rect mDefaultDisplayRect;
-
-    @Mock
-    private IPinnedStackController mPinnedStackController;
 
     @Before
     public void setUp() throws Exception {
         mPipBoundsHandler = new PipBoundsHandler(mContext);
-        MockitoAnnotations.initMocks(this);
         initializeMockResources();
 
         mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
-        mPipBoundsHandler.setPinnedStackController(mPinnedStackController);
     }
 
     private void initializeMockResources() {
@@ -94,142 +80,80 @@
         mDefaultDisplayInfo.displayId = 1;
         mDefaultDisplayInfo.logicalWidth = 1000;
         mDefaultDisplayInfo.logicalHeight = 1500;
-        mDefaultDisplayRect = new Rect(0, 0,
-                mDefaultDisplayInfo.logicalWidth, mDefaultDisplayInfo.logicalHeight);
     }
 
     @Test
-    public void setShelfHeight_offsetBounds() throws Exception {
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+    public void setShelfHeight_offsetBounds() {
         final int shelfHeight = 100;
-
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect lastPosition = destinationBounds.getValue();
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
         mPipBoundsHandler.setShelfHeight(true, shelfHeight);
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        lastPosition.offset(0, -shelfHeight);
-        assertBoundsWithMargin("PiP bounds offset by shelf height",
-                lastPosition, destinationBounds.getValue());
+        oldPosition.offset(0, -shelfHeight);
+        assertBoundsWithMargin("PiP bounds offset by shelf height", oldPosition, newPosition);
     }
 
     @Test
-    public void onImeVisibilityChanged_offsetBounds() throws Exception {
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+    public void onImeVisibilityChanged_offsetBounds() {
         final int imeHeight = 100;
-
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect lastPosition = destinationBounds.getValue();
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
         mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        lastPosition.offset(0, -imeHeight);
-        assertBoundsWithMargin("PiP bounds offset by IME height",
-                lastPosition, destinationBounds.getValue());
+        oldPosition.offset(0, -imeHeight);
+        assertBoundsWithMargin("PiP bounds offset by IME height", oldPosition, newPosition);
     }
 
     @Test
-    public void onPrepareAnimation_startAnimation() throws Exception {
-        final Rect sourceRectHint = new Rect(100, 100, 200, 200);
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
-
-        mPipBoundsHandler.onPrepareAnimation(sourceRectHint, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), eq(sourceRectHint), anyInt());
-        final Rect capturedDestinationBounds = destinationBounds.getValue();
-        assertFalse("Destination bounds is not empty",
-                capturedDestinationBounds.isEmpty());
-        assertBoundsWithMargin("Destination bounds within Display",
-                mDefaultDisplayRect, capturedDestinationBounds);
-    }
-
-    @Test
-    public void onSaveReentryBounds_restoreLastPosition() throws Exception {
+    public void onSaveReentryBounds_restoreLastPosition() {
         final ComponentName componentName = new ComponentName(mContext, "component1");
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        oldPosition.offset(0, -100);
+        mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect lastPosition = destinationBounds.getValue();
-        lastPosition.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(componentName, lastPosition);
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        assertBoundsWithMargin("Last position is restored",
-                lastPosition, destinationBounds.getValue());
+        assertBoundsWithMargin("Last position is restored", oldPosition, newPosition);
     }
 
     @Test
-    public void onResetReentryBounds_componentMatch_useDefaultBounds() throws Exception {
+    public void onResetReentryBounds_componentMatch_useDefaultBounds() {
         final ComponentName componentName = new ComponentName(mContext, "component1");
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
-
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect defaultBounds = new Rect(destinationBounds.getValue());
+        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
         final Rect newBounds = new Rect(defaultBounds);
         newBounds.offset(0, -100);
         mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
 
         mPipBoundsHandler.onResetReentryBounds(componentName);
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect actualBounds = destinationBounds.getValue();
         assertBoundsWithMargin("Use default bounds", defaultBounds, actualBounds);
     }
 
     @Test
-    public void onResetReentryBounds_componentMismatch_restoreLastPosition() throws Exception {
+    public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
         final ComponentName componentName = new ComponentName(mContext, "component1");
-        final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class);
-
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect defaultBounds = new Rect(destinationBounds.getValue());
+        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
         final Rect newBounds = new Rect(defaultBounds);
         newBounds.offset(0, -100);
         mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
-        // Reset the pinned stack controller since we will do another verify later on
-        reset(mPinnedStackController);
 
         mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2"));
-        mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
 
-        verify(mPinnedStackController).startAnimation(
-                destinationBounds.capture(), isNull(), anyInt());
-        final Rect actualBounds = destinationBounds.getValue();
         assertBoundsWithMargin("Last position is restored", newBounds, actualBounds);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index f11c42b..f37836c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -208,7 +208,7 @@
                 .setTag("tag")
                 .setNotification(aN)
                 .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
-                .setUser(mContext.getUser())
+                .setUser(mContext.user)
                 .setOverrideGroupKey("")
                 .build()
         whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking))
@@ -226,12 +226,12 @@
                 .setTag("tag")
                 .setNotification(bN)
                 .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
-                .setUser(mContext.getUser())
+                .setUser(mContext.user)
                 .setOverrideGroupKey("")
                 .build()
-        whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking))
-                .thenReturn(false)
-        whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking))
+        whenever(personNotificationIdentifier.isImportantPeopleNotification(b.sbn, b.ranking))
+                .thenReturn(true)
+        whenever(personNotificationIdentifier.isPeopleNotification(b.sbn, b.ranking))
                 .thenReturn(true)
 
         assertEquals(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index cd91f22..5771472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -140,13 +139,16 @@
     }
 
     @Test
-    public void testDefault_hotspotNotSupported() {
-        assertFalse(mController.isHotspotSupported());
+    public void testHotspotSupported_default() {
+        assertTrue(mController.isHotspotSupported());
     }
 
     @Test
     public void testHotspotSupported_rightConditions() {
         mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+
+        assertTrue(mController.isHotspotSupported());
+
         mTetheringCallbackCaptor.getValue()
                 .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
 
@@ -154,13 +156,21 @@
     }
 
     @Test
-    public void testHotspotSupported_callbackCalledOnChange() {
+    public void testHotspotSupported_callbackCalledOnChange_tetheringSupported() {
         mController.addCallback(mCallback1);
-        mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+        mTetheringCallbackCaptor.getValue().onTetheringSupported(false);
+
+        verify(mCallback1).onHotspotAvailabilityChanged(false);
+    }
+
+    @Test
+    public void testHotspotSupported_callbackCalledOnChange_tetherableInterfaces() {
+        when(mTetheringInterfaceRegexps.getTetherableWifiRegexs())
+                .thenReturn(Collections.emptyList());
+        mController.addCallback(mCallback1);
         mTetheringCallbackCaptor.getValue()
                 .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
 
-        verify(mCallback1).onHotspotAvailabilityChanged(true);
+        verify(mCallback1).onHotspotAvailabilityChanged(false);
     }
-
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 0c9130d0..e6b0440 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -86,6 +86,7 @@
         mContext.addMockService(comp, mKeyChainService);
 
         when(mUserManager.getUserInfo(anyInt())).thenReturn(new UserInfo());
+        when(mUserManager.isUserUnlocked(any())).thenReturn(true);
 
         when(mKeyChainService.getUserCaAliases())
                 .thenReturn(new StringParceledListSlice(new ArrayList<String>()));
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 1a3d5b6..5b73dd5 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -41,8 +41,7 @@
 
 java_library {
     name: "framework-tethering",
-    // TODO (b/146757305): change to module_app_current once available
-    sdk_version: "core_platform",
+    sdk_version: "module_current",
     srcs: [
         "src/android/net/TetheredClient.java",
         "src/android/net/TetheringManager.java",
@@ -56,7 +55,6 @@
 
     libs: [
         "framework-annotations-lib",
-        "android_system_stubs_current",
     ],
 
     hostdex: true, // for hiddenapi check
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index c6905ec..59681e9 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -41,7 +41,7 @@
         "framework-minus-apex",
         "ext",
         "framework-res",
-        "framework-wifi-stubs",
+        "framework-wifi-stubs-module_libs_api",
         "framework-telephony-stubs",
         "android.test.runner",
         "android.test.base",
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index cff5504..c5a5d34 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -255,6 +255,10 @@
     // Inform the user a foreground service while-in-use permission is restricted.
     NOTE_FOREGROUND_SERVICE_WHILE_IN_USE_PERMISSION = 61;
 
+    // Display the Android Debug Protocol status
+    // Package: android
+    NOTE_ADB_WIFI_ACTIVE = 62;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto
index 789019c..2006fb3 100644
--- a/proto/src/task_snapshot.proto
+++ b/proto/src/task_snapshot.proto
@@ -32,7 +32,12 @@
      int32 system_ui_visibility = 8;
      bool is_translucent = 9;
      string top_activity_component = 10;
-     float scale = 11;
+     // deprecated because original width and height are stored now instead of the scale.
+     float legacy_scale = 11 [deprecated=true];
      int64 id = 12;
      int32 rotation = 13;
+     // The task width when the snapshot was taken
+     int32 task_width = 14;
+     // The task height when the snapshot was taken
+     int32 task_height = 15;
  }
diff --git a/services/api/current.txt b/services/api/current.txt
index 4a0a0d8..8c90165 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -49,14 +49,15 @@
   public abstract class SystemService {
     ctor public SystemService(@NonNull android.content.Context);
     method @NonNull public final android.content.Context getContext();
-    method public boolean isSupportedUser(@NonNull com.android.server.SystemService.TargetUser);
+    method public boolean isUserSupported(@NonNull com.android.server.SystemService.TargetUser);
     method public void onBootPhase(int);
-    method public void onCleanupUser(@NonNull com.android.server.SystemService.TargetUser);
     method public abstract void onStart();
-    method public void onStartUser(@NonNull com.android.server.SystemService.TargetUser);
-    method public void onStopUser(@NonNull com.android.server.SystemService.TargetUser);
-    method public void onSwitchUser(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser);
-    method public void onUnlockUser(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserStarting(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserStopped(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserStopping(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserSwitching(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserUnlocked(@NonNull com.android.server.SystemService.TargetUser);
+    method public void onUserUnlocking(@NonNull com.android.server.SystemService.TargetUser);
     method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder);
     method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder, boolean);
     field public static final int PHASE_ACTIVITY_MANAGER_READY = 550; // 0x226
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index a8a2791..b78d024 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -794,6 +794,8 @@
         proto.write(WidgetProto.PROVIDER_PACKAGE, widget.provider.id.componentName.getPackageName());
         proto.write(WidgetProto.PROVIDER_CLASS, widget.provider.id.componentName.getClassName());
         if (widget.options != null) {
+            proto.write(WidgetProto.RESTORE_COMPLETED,
+                    widget.options.getBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED));
             proto.write(WidgetProto.MIN_WIDTH,
                 widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 0));
             proto.write(WidgetProto.MIN_HEIGHT,
@@ -2509,7 +2511,8 @@
         out.endTag(null, "h");
     }
 
-    private static void serializeAppWidget(XmlSerializer out, Widget widget) throws IOException {
+    private static void serializeAppWidget(XmlSerializer out, Widget widget,
+            boolean saveRestoreCompleted) throws IOException {
         out.startTag(null, "g");
         out.attribute(null, "id", Integer.toHexString(widget.appWidgetId));
         out.attribute(null, "rid", Integer.toHexString(widget.restoredId));
@@ -2528,10 +2531,50 @@
             out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0));
             out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt(
                     AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
+            if (saveRestoreCompleted) {
+                boolean restoreCompleted = widget.options.getBoolean(
+                        AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED);
+                out.attribute(null, "restore_completed", Boolean.toString(restoreCompleted));
+            }
         }
         out.endTag(null, "g");
     }
 
+    private static Bundle parseWidgetIdOptions(XmlPullParser parser) {
+        Bundle options = new Bundle();
+        String restoreCompleted = parser.getAttributeValue(null, "restore_completed");
+        if (restoreCompleted != null) {
+            options.putBoolean(AppWidgetManager.OPTION_APPWIDGET_RESTORE_COMPLETED,
+                    Boolean.valueOf(restoreCompleted));
+        }
+        String minWidthString = parser.getAttributeValue(null, "min_width");
+        if (minWidthString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+                    Integer.parseInt(minWidthString, 16));
+        }
+        String minHeightString = parser.getAttributeValue(null, "min_height");
+        if (minHeightString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+                    Integer.parseInt(minHeightString, 16));
+        }
+        String maxWidthString = parser.getAttributeValue(null, "max_width");
+        if (maxWidthString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+                    Integer.parseInt(maxWidthString, 16));
+        }
+        String maxHeightString = parser.getAttributeValue(null, "max_height");
+        if (maxHeightString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+                    Integer.parseInt(maxHeightString, 16));
+        }
+        String categoryString = parser.getAttributeValue(null, "host_category");
+        if (categoryString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                    Integer.parseInt(categoryString, 16));
+        }
+        return options;
+    }
+
     @Override
     public List<String> getWidgetParticipants(int userId) {
         return mBackupRestoreController.getWidgetParticipants(userId);
@@ -3064,7 +3107,7 @@
                 if (widget.host.getUserId() != userId) {
                     continue;
                 }
-                serializeAppWidget(out, widget);
+                serializeAppWidget(out, widget, true);
             }
 
             Iterator<Pair<Integer, String>> it = mPackagesWithBindWidgetPermission.iterator();
@@ -3203,34 +3246,7 @@
                         String restoredIdString = parser.getAttributeValue(null, "rid");
                         widget.restoredId = (restoredIdString == null) ? 0
                                 : Integer.parseInt(restoredIdString, 16);
-
-                        Bundle options = new Bundle();
-                        String minWidthString = parser.getAttributeValue(null, "min_width");
-                        if (minWidthString != null) {
-                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
-                                    Integer.parseInt(minWidthString, 16));
-                        }
-                        String minHeightString = parser.getAttributeValue(null, "min_height");
-                        if (minHeightString != null) {
-                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
-                                    Integer.parseInt(minHeightString, 16));
-                        }
-                        String maxWidthString = parser.getAttributeValue(null, "max_width");
-                        if (maxWidthString != null) {
-                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
-                                    Integer.parseInt(maxWidthString, 16));
-                        }
-                        String maxHeightString = parser.getAttributeValue(null, "max_height");
-                        if (maxHeightString != null) {
-                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
-                                    Integer.parseInt(maxHeightString, 16));
-                        }
-                        String categoryString = parser.getAttributeValue(null, "host_category");
-                        if (categoryString != null) {
-                            options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
-                                    Integer.parseInt(categoryString, 16));
-                        }
-                        widget.options = options;
+                        widget.options = parseWidgetIdOptions(parser);
 
                         final int hostTag = Integer.parseInt(parser.getAttributeValue(
                                 null, "h"), 16);
@@ -4382,7 +4398,7 @@
                         if (widget.host.isInPackageForUser(backedupPackage, userId)
                                 || (provider != null
                                 &&  provider.isInPackageForUser(backedupPackage, userId))) {
-                            serializeAppWidget(out, widget);
+                            serializeAppWidget(out, widget, false);
                         }
                     }
 
@@ -4815,36 +4831,6 @@
                     || widget.provider.getUserId() == userId);
         }
 
-        private Bundle parseWidgetIdOptions(XmlPullParser parser) {
-            Bundle options = new Bundle();
-            String minWidthString = parser.getAttributeValue(null, "min_width");
-            if (minWidthString != null) {
-                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
-                        Integer.parseInt(minWidthString, 16));
-            }
-            String minHeightString = parser.getAttributeValue(null, "min_height");
-            if (minHeightString != null) {
-                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
-                        Integer.parseInt(minHeightString, 16));
-            }
-            String maxWidthString = parser.getAttributeValue(null, "max_width");
-            if (maxWidthString != null) {
-                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
-                        Integer.parseInt(maxWidthString, 16));
-            }
-            String maxHeightString = parser.getAttributeValue(null, "max_height");
-            if (maxHeightString != null) {
-                options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
-                        Integer.parseInt(maxHeightString, 16));
-            }
-            String categoryString = parser.getAttributeValue(null, "host_category");
-            if (categoryString != null) {
-                options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
-                        Integer.parseInt(categoryString, 16));
-            }
-            return options;
-        }
-
         private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
             int pending = 0;
             final int N = updates.size();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 5405fc7..e49c1ed 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -331,7 +331,7 @@
     }
 
     @Override // from SystemService
-    public boolean isSupportedUser(TargetUser user) {
+    public boolean isUserSupported(TargetUser user) {
         return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 5d5af53..7ad5632 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -86,9 +86,9 @@
      */
     public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
             @NonNull InlinePresentation presentation, int width, int height,
-            @Nullable IBinder hostInputToken) {
-        scheduleAsyncRequest(
-                (s) -> s.renderSuggestion(callback, presentation, width, height, hostInputToken));
+            @Nullable IBinder hostInputToken, int displayId) {
+        scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height,
+                hostInputToken, displayId));
     }
 
     @Nullable
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 0d8c89b..fef49d4 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -134,7 +134,7 @@
         if (inlineAuthentication != null) {
             InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(inlineAuthentication,
                     remoteRenderService, onClickFactory, onErrorCallback,
-                    request.getHostInputToken());
+                    request.getHostInputToken(), request.getHostDisplayId());
             inlineSuggestions.add(inlineAuthSuggestion);
 
             return new InlineSuggestionsResponse(inlineSuggestions);
@@ -162,9 +162,10 @@
                 continue;
             }
             InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
-                    fieldIndex, mergedInlinePresentation(request, datasetIndex, inlinePresentation),
+                    datasetIndex,
+                    mergedInlinePresentation(request, datasetIndex, inlinePresentation),
                     onClickFactory, remoteRenderService, onErrorCallback,
-                    request.getHostInputToken());
+                    request.getHostInputToken(), request.getHostDisplayId());
 
             inlineSuggestions.add(inlineSuggestion);
         }
@@ -172,7 +173,8 @@
             for (InlinePresentation inlinePresentation : inlineActions) {
                 final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
                         mergedInlinePresentation(request, 0, inlinePresentation),
-                        remoteRenderService, onErrorCallback, request.getHostInputToken());
+                        remoteRenderService, onErrorCallback, request.getHostInputToken(),
+                        request.getHostDisplayId());
                 inlineSuggestions.add(inlineAction);
             }
         }
@@ -215,7 +217,8 @@
             @NonNull Context context,
             @NonNull InlinePresentation inlinePresentation,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
-            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
+            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
+            int displayId) {
         // TODO(b/146453195): fill in the autofill hint properly.
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
@@ -227,7 +230,7 @@
         };
         return new InlineSuggestion(inlineSuggestionInfo,
                 createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback,
-                        remoteRenderService, hostInputToken));
+                        remoteRenderService, hostInputToken, displayId));
     }
 
     private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
@@ -235,7 +238,8 @@
             @NonNull InlinePresentation inlinePresentation,
             @NonNull BiConsumer<Dataset, Integer> onClickFactory,
             @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
-            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
+            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken,
+            int displayId) {
         // TODO(b/146453195): fill in the autofill hint properly.
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
@@ -246,7 +250,7 @@
         final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
                 createInlineContentProvider(inlinePresentation,
                         () -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback,
-                        remoteRenderService, hostInputToken));
+                        remoteRenderService, hostInputToken, displayId));
 
         return inlineSuggestion;
     }
@@ -255,7 +259,7 @@
             @NonNull InlinePresentation inlinePresentation,
             @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
             @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback,
-            @Nullable IBinder hostInputToken) {
+            @Nullable IBinder hostInputToken, int displayId) {
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
                 InlineSuggestionInfo.SOURCE_AUTOFILL, null, InlineSuggestionInfo.TYPE_SUGGESTION);
@@ -264,7 +268,7 @@
                 createInlineContentProvider(inlinePresentation,
                         () -> onClickFactory.accept(null,
                                 AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
-                        onErrorCallback, remoteRenderService, hostInputToken));
+                        onErrorCallback, remoteRenderService, hostInputToken, displayId));
     }
 
     /**
@@ -291,7 +295,8 @@
             @NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
             @NonNull Runnable onErrorCallback,
             @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
-            @Nullable IBinder hostInputToken) {
+            @Nullable IBinder hostInputToken,
+            int displayId) {
         return new IInlineContentProvider.Stub() {
             @Override
             public void provideContent(int width, int height, IInlineContentCallback callback) {
@@ -305,7 +310,7 @@
                     }
 
                     remoteRenderService.renderSuggestion(uiCallback, inlinePresentation,
-                            width, height, hostInputToken);
+                            width, height, hostInputToken, displayId);
                 });
             }
         };
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 9a33fc9..5d2b9f3 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -201,7 +201,7 @@
     }
 
     @Override // from SystemService
-    public boolean isSupportedUser(TargetUser user) {
+    public boolean isUserSupported(TargetUser user) {
         return user.getUserInfo().isFull() || user.getUserInfo().isManagedProfile();
     }
 
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 1230bd7..1b1e06a 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -458,8 +458,8 @@
             Bundle verificationBundle, int userId);
 
     /**
-     * Grants implicit access based on an interaction between two apps. This grants the target app
-     * access to the calling application's package metadata.
+     * Grants implicit access based on an interaction between two apps. This grants access to the
+     * from one application to the other's package metadata.
      * <p>
      * When an application explicitly tries to interact with another application [via an
      * activity, service or provider that is either declared in the caller's
@@ -468,14 +468,22 @@
      * metadata about the calling app. If the calling application uses an implicit intent [ie
      * action VIEW, category BROWSABLE], it remains hidden from the launched app.
      * <p>
+     * If an interaction is not explicit, the {@code direct} argument should be set to false as
+     * visibility should not be granted in some cases. This method handles that logic.
+     * <p>
      * @param userId the user
      * @param intent the intent that triggered the grant
-     * @param callingUid The uid of the calling application
-     * @param targetAppId The app ID of the target application
+     * @param recipientAppId The app ID of the application that is being given access to {@code
+     *                       visibleUid}
+     * @param visibleUid The uid of the application that is becoming accessible to {@code
+     *                   recipientAppId}
+     * @param direct true if the access is being made due to direct interaction between visibleUid
+     *               and recipientAppId.
      */
     public abstract void grantImplicitAccess(
-            @UserIdInt int userId, Intent intent, int callingUid,
-            @AppIdInt int targetAppId);
+            @UserIdInt int userId, Intent intent,
+            @AppIdInt int recipientAppId, int visibleUid,
+            boolean direct);
 
     public abstract boolean isInstantAppInstallerComponent(ComponentName component);
     /**
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 93859b3..3148a62 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -345,7 +345,7 @@
                 @Override
                 public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
                 }
-            }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system");
+            }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, null);
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to register uid observer", e);
         }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d86b223..95bfbd7 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -279,7 +279,7 @@
         }
 
         @Override
-        public void onStartUser(TargetUser user) {
+        public void onUserStarting(TargetUser user) {
             mStorageManagerService.snapshotAndMonitorLegacyStorageAppOp(user.getUserHandle());
         }
     }
@@ -4458,8 +4458,11 @@
                     mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid);
                 } catch (IOException e) {
                     Log.e(TAG, "Failed to get canonical path for " + packageName);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to fixup app dir for " + packageName);
+                } catch (RemoteException | ServiceSpecificException e) {
+                    // TODO(b/149975102) there is a known case where this fails, when a new
+                    // user is setup and we try to fixup app dirs for some existing apps.
+                    // For now catch the exception and don't crash.
+                    Log.e(TAG, "Failed to fixup app dir for " + packageName, e);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index aabe98b..e5c0540 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -238,12 +238,12 @@
      * <p>By default returns {@code true}, but subclasses should extend for optimization, if they
      * don't support some types (like headless system user).
      */
-    public boolean isSupportedUser(@NonNull TargetUser user) {
+    public boolean isUserSupported(@NonNull TargetUser user) {
         return true;
     }
 
     /**
-     * Helper method used to dump which users are {@link #onStartUser(TargetUser) supported}.
+     * Helper method used to dump which users are {@link #onUserStarting(TargetUser) supported}.
      *
      * @hide
      */
@@ -264,7 +264,7 @@
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserStarting(TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -273,7 +273,7 @@
     public void onStartUser(@UserIdInt int userId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onStartUser(TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserStarting(TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -287,17 +287,17 @@
      * Called when a new user is starting, for system services to initialize any per-user
      * state they maintain for running users.
      *
-     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * this user.
      *
      * @param user target user
      */
-    public void onStartUser(@NonNull TargetUser user) {
+    public void onUserStarting(@NonNull TargetUser user) {
         onStartUser(user.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by
+     * @deprecated subclasses should extend {@link #onUserUnlocking(TargetUser)} instead (which by
      * default calls this method).
      *
      * @hide
@@ -306,7 +306,7 @@
     public void onUnlockUser(@UserIdInt int userId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onUnlockUser(TargetUser)} instead (which by
+     * @deprecated subclasses should extend {@link #onUserUnlocking(TargetUser)} instead (which by
      * default calls this method).
      *
      * @hide
@@ -326,19 +326,30 @@
      * the user will transition into the {@code STATE_RUNNING_UNLOCKED} state.
      * Code written inside system services should use
      * {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
-     * these states.
+     * these states, or use {@link #onUserUnlocked(TargetUser)} instead.
      * <p>
-     * This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * this user.
      *
      * @param user target user
      */
-    public void onUnlockUser(@NonNull TargetUser user) {
+    public void onUserUnlocking(@NonNull TargetUser user) {
         onUnlockUser(user.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead
+     * Called after an existing user is unlocked.
+     *
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
+     * this user.
+     *
+     * @param user target user
+     */
+    public void onUserUnlocked(@NonNull TargetUser user) {
+    }
+
+    /**
+     * @deprecated subclasses should extend {@link #onUserSwitching(TargetUser, TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -347,7 +358,7 @@
     public void onSwitchUser(@UserIdInt int toUserId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onSwitchUser(TargetUser, TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserSwitching(TargetUser, TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -362,7 +373,7 @@
      * special behavior for whichever user is currently in the foreground.  This is called
      * before any application processes are aware of the new user.
      *
-     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * either of the users ({@code from} or {@code to}).
      *
      * <b>NOTE: </b> both {@code from} and {@code to} are "live" objects
@@ -371,12 +382,12 @@
      * @param from the user switching from
      * @param to the user switching to
      */
-    public void onSwitchUser(@Nullable TargetUser from, @NonNull TargetUser to) {
+    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
         onSwitchUser((from == null ? null : from.getUserInfo()), to.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserStopping(TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -385,7 +396,7 @@
     public void onStopUser(@UserIdInt int userId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onStopUser(TargetUser)} instead
+     * @deprecated subclasses should extend {@link #onUserStopping(TargetUser)} instead
      * (which by default calls this method).
      *
      * @hide
@@ -402,19 +413,19 @@
      * broadcast to the user; it is a good place to stop making use of any resources of that
      * user (such as binding to a service running in the user).
      *
-     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * this user.
      *
      * <p>NOTE: This is the last callback where the callee may access the target user's CE storage.
      *
      * @param user target user
      */
-    public void onStopUser(@NonNull TargetUser user) {
+    public void onUserStopping(@NonNull TargetUser user) {
         onStopUser(user.getUserInfo());
     }
 
     /**
-     * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by
+     * @deprecated subclasses should extend {@link #onUserStopped(TargetUser)} instead (which by
      * default calls this method).
      *
      * @hide
@@ -423,7 +434,7 @@
     public void onCleanupUser(@UserIdInt int userId) {}
 
     /**
-     * @deprecated subclasses should extend {@link #onCleanupUser(TargetUser)} instead (which by
+     * @deprecated subclasses should extend {@link #onUserStopped(TargetUser)} instead (which by
      * default calls this method).
      *
      * @hide
@@ -434,20 +445,16 @@
     }
 
     /**
-     * Called when an existing user is stopping, for system services to finalize any per-user
-     * state they maintain for running users.  This is called after all application process
-     * teardown of the user is complete.
+     * Called after an existing user is stopped.
      *
-     * <p>This method is only called when the service {@link #isSupportedUser(TargetUser) supports}
+     * <p>This is called after all application process teardown of the user is complete.
+     *
+     * <p>This method is only called when the service {@link #isUserSupported(TargetUser) supports}
      * this user.
      *
-     * <p>NOTE: When this callback is called, the CE storage for the target user may not be
-     * accessible already.  Use {@link #onStopUser(TargetUser)} instead if you need to access the CE
-     * storage.
-     *
      * @param user target user
      */
-    public void onCleanupUser(@NonNull TargetUser user) {
+    public void onUserStopped(@NonNull TargetUser user) {
         onCleanupUser(user.getUserInfo());
     }
 
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index e7f7846..f16f6ef 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -51,7 +51,8 @@
 
     // Constants used on onUser(...)
     private static final String START = "Start";
-    private static final String UNLOCK = "Unlock";
+    private static final String UNLOCKING = "Unlocking";
+    private static final String UNLOCKED = "Unlocked";
     private static final String SWITCH = "Switch";
     private static final String STOP = "Stop";
     private static final String CLEANUP = "Cleanup";
@@ -260,7 +261,14 @@
      * Unlocks the given user.
      */
     public void unlockUser(final @UserIdInt int userHandle) {
-        onUser(UNLOCK, userHandle);
+        onUser(UNLOCKING, userHandle);
+    }
+
+    /**
+     * Called after the user was unlocked.
+     */
+    public void onUserUnlocked(final @UserIdInt int userHandle) {
+        onUser(UNLOCKED, userHandle);
     }
 
     /**
@@ -304,12 +312,12 @@
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
             final String serviceName = service.getClass().getName();
-            boolean supported = service.isSupportedUser(curUser);
+            boolean supported = service.isUserSupported(curUser);
 
             // Must check if either curUser or prevUser is supported (for example, if switching from
             // unsupported to supported, we still need to notify the services)
             if (!supported && prevUser != null) {
-                supported = service.isSupportedUser(prevUser);
+                supported = service.isUserSupported(prevUser);
             }
 
             if (!supported) {
@@ -328,19 +336,22 @@
             try {
                 switch (onWhat) {
                     case SWITCH:
-                        service.onSwitchUser(prevUser, curUser);
+                        service.onUserSwitching(prevUser, curUser);
                         break;
                     case START:
-                        service.onStartUser(curUser);
+                        service.onUserStarting(curUser);
                         break;
-                    case UNLOCK:
-                        service.onUnlockUser(curUser);
+                    case UNLOCKING:
+                        service.onUserUnlocking(curUser);
+                        break;
+                    case UNLOCKED:
+                        service.onUserUnlocked(curUser);
                         break;
                     case STOP:
-                        service.onStopUser(curUser);
+                        service.onUserStopping(curUser);
                         break;
                     case CLEANUP:
-                        service.onCleanupUser(curUser);
+                        service.onUserStopped(curUser);
                         break;
                     default:
                         throw new IllegalArgumentException(onWhat + " what?");
diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java
index 74f113f..9a9374c 100644
--- a/services/core/java/com/android/server/UserspaceRebootLogger.java
+++ b/services/core/java/com/android/server/UserspaceRebootLogger.java
@@ -26,6 +26,7 @@
 
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.util.FrameworkStatsLog;
@@ -45,7 +46,7 @@
             "sys.userspace_reboot.log.last_started";
     private static final String USERSPACE_REBOOT_LAST_FINISHED_PROPERTY =
             "sys.userspace_reboot.log.last_finished";
-    private static final String BOOT_REASON_PROPERTY = "sys.boot.reason";
+    private static final String LAST_BOOT_REASON_PROPERTY = "sys.boot.reason.last";
 
     private UserspaceRebootLogger() {}
 
@@ -111,26 +112,28 @@
         if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) {
             return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
         }
-        String reason = SystemProperties.get(BOOT_REASON_PROPERTY, "");
+        String reason = TextUtils.emptyIfNull(SystemProperties.get(LAST_BOOT_REASON_PROPERTY, ""));
         if (reason.startsWith("reboot,")) {
             reason = reason.substring("reboot".length());
         }
-        switch (reason) {
-            case "userspace_failed,watchdog_fork":
-                // Since fork happens before shutdown sequence, attribute it to
-                // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED.
-            case "userspace_failed,shutdown_aborted":
-                return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
-            case "userspace_failed,init_user0_failed":
-                // init_user0 will fail if userdata wasn't remounted correctly, attribute to
-                // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT.
-            case "mount_userdata_failed":
-                return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
-            case "userspace_failed,watchdog_triggered":
-                return
-                    USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
-            default:
-                return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
+        if (reason.startsWith("userspace_failed,watchdog_fork")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
         }
+        if (reason.startsWith("userspace_failed,shutdown_aborted")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
+        }
+        if (reason.startsWith("mount_userdata_failed")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+        }
+        if (reason.startsWith("userspace_failed,init_user0")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+        }
+        if (reason.startsWith("userspace_failed,enablefilecrypto")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+        }
+        if (reason.startsWith("userspace_failed,watchdog_triggered")) {
+            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
+        }
+        return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
     }
 }
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index f16e3ce..e49357b 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -20,19 +20,36 @@
 
 import android.annotation.TestApi;
 import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.debug.AdbManager;
 import android.debug.AdbProtoEnums;
 import android.debug.AdbTransportType;
+import android.debug.PairDevice;
+import android.net.ConnectivityManager;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
+import android.net.NetworkInfo;
 import android.net.Uri;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -51,6 +68,8 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.XmlUtils;
@@ -71,6 +90,8 @@
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -79,6 +100,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keysi
@@ -87,6 +109,7 @@
 public class AdbDebuggingManager {
     private static final String TAG = "AdbDebuggingManager";
     private static final boolean DEBUG = false;
+    private static final boolean MDNS_DEBUG = false;
 
     private static final String ADBD_SOCKET = "adbd";
     private static final String ADB_DIRECTORY = "misc/adb";
@@ -98,19 +121,39 @@
     private static final int BUFFER_SIZE = 65536;
 
     private final Context mContext;
+    private final ContentResolver mContentResolver;
     private final Handler mHandler;
     private AdbDebuggingThread mThread;
-    private boolean mAdbEnabled = false;
+    private boolean mAdbUsbEnabled = false;
+    private boolean mAdbWifiEnabled = false;
     private String mFingerprints;
-    private final List<String> mConnectedKeys;
+    // A key can be used more than once (e.g. USB, wifi), so need to keep a refcount
+    private final Map<String, Integer> mConnectedKeys;
     private String mConfirmComponent;
     private final File mTestUserKeyFile;
 
+    private static final String WIFI_PERSISTENT_CONFIG_PROPERTY =
+            "persist.adb.tls_server.enable";
+    private static final String WIFI_PERSISTENT_GUID =
+            "persist.adb.wifi.guid";
+    private static final int PAIRING_CODE_LENGTH = 6;
+    private PairingThread mPairingThread = null;
+    // A list of keys connected via wifi
+    private final Set<String> mWifiConnectedKeys;
+    // The current info of the adbwifi connection.
+    private AdbConnectionInfo mAdbConnectionInfo;
+    // Polls for a tls port property when adb wifi is enabled
+    private AdbConnectionPortPoller mConnectionPortPoller;
+    private final PortListenerImpl mPortListener = new PortListenerImpl();
+
     public AdbDebuggingManager(Context context) {
         mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
         mContext = context;
+        mContentResolver = mContext.getContentResolver();
         mTestUserKeyFile = null;
-        mConnectedKeys = new ArrayList<>(1);
+        mConnectedKeys = new HashMap<String, Integer>();
+        mWifiConnectedKeys = new HashSet<String>();
+        mAdbConnectionInfo = new AdbConnectionInfo();
     }
 
     /**
@@ -121,9 +164,178 @@
     protected AdbDebuggingManager(Context context, String confirmComponent, File testUserKeyFile) {
         mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
         mContext = context;
+        mContentResolver = mContext.getContentResolver();
         mConfirmComponent = confirmComponent;
         mTestUserKeyFile = testUserKeyFile;
-        mConnectedKeys = new ArrayList<>();
+        mConnectedKeys = new HashMap<String, Integer>();
+        mWifiConnectedKeys = new HashSet<String>();
+        mAdbConnectionInfo = new AdbConnectionInfo();
+    }
+
+    class PairingThread extends Thread implements NsdManager.RegistrationListener {
+        private NsdManager mNsdManager;
+        private String mPublicKey;
+        private String mPairingCode;
+        private String mGuid;
+        private String mServiceName;
+        private final String mServiceType = "_adb_secure_pairing._tcp.";
+        private int mPort;
+
+        private native int native_pairing_start(String guid, String password);
+        private native void native_pairing_cancel();
+        private native boolean native_pairing_wait();
+
+        PairingThread(String pairingCode, String serviceName) {
+            super(TAG);
+            mPairingCode = pairingCode;
+            mGuid = SystemProperties.get(WIFI_PERSISTENT_GUID);
+            mServiceName = serviceName;
+            if (serviceName == null || serviceName.isEmpty()) {
+                mServiceName = mGuid;
+            }
+            mPort = -1;
+            mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
+        }
+
+        @Override
+        public void run() {
+            if (mGuid.isEmpty()) {
+                Slog.e(TAG, "adbwifi guid was not set");
+                return;
+            }
+            mPort = native_pairing_start(mGuid, mPairingCode);
+            if (mPort <= 0 || mPort > 65535) {
+                Slog.e(TAG, "Unable to start pairing server");
+                return;
+            }
+
+            // Register the mdns service
+            NsdServiceInfo serviceInfo = new NsdServiceInfo();
+            serviceInfo.setServiceName(mServiceName);
+            serviceInfo.setServiceType(mServiceType);
+            serviceInfo.setPort(mPort);
+            mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, this);
+
+            // Send pairing port to UI
+            Message msg = mHandler.obtainMessage(
+                    AdbDebuggingHandler.MSG_RESPONSE_PAIRING_PORT);
+            msg.obj = mPort;
+            mHandler.sendMessage(msg);
+
+            boolean paired = native_pairing_wait();
+            if (DEBUG) {
+                if (mPublicKey != null) {
+                    Slog.i(TAG, "Pairing succeeded key=" + mPublicKey);
+                } else {
+                    Slog.i(TAG, "Pairing failed");
+                }
+            }
+
+            mNsdManager.unregisterService(this);
+
+            Bundle bundle = new Bundle();
+            bundle.putString("publicKey", paired ? mPublicKey : null);
+            Message message = Message.obtain(mHandler,
+                                             AdbDebuggingHandler.MSG_RESPONSE_PAIRING_RESULT,
+                                             bundle);
+            mHandler.sendMessage(message);
+        }
+
+        public void cancelPairing() {
+            native_pairing_cancel();
+        }
+
+        @Override
+        public void onServiceRegistered(NsdServiceInfo serviceInfo) {
+            if (MDNS_DEBUG) Slog.i(TAG, "Registered pairing service: " + serviceInfo);
+        }
+
+        @Override
+        public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            Slog.e(TAG, "Failed to register pairing service(err=" + errorCode
+                    + "): " + serviceInfo);
+            cancelPairing();
+        }
+
+        @Override
+        public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
+            if (MDNS_DEBUG) Slog.i(TAG, "Unregistered pairing service: " + serviceInfo);
+        }
+
+        @Override
+        public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+            Slog.w(TAG, "Failed to unregister pairing service(err=" + errorCode
+                    + "): " + serviceInfo);
+        }
+    }
+
+    interface AdbConnectionPortListener {
+        void onPortReceived(int port);
+    }
+
+    /**
+     * This class will poll for a period of time for adbd to write the port
+     * it connected to.
+     *
+     * TODO(joshuaduong): The port is being sent via system property because the adbd socket
+     * (AdbDebuggingManager) is not created when ro.adb.secure=0. Thus, we must communicate the
+     * port through different means. A better fix would be to always start AdbDebuggingManager, but
+     * it needs to adjust accordingly on whether ro.adb.secure is set.
+     */
+    static class AdbConnectionPortPoller extends Thread {
+        private final String mAdbPortProp = "service.adb.tls.port";
+        private AdbConnectionPortListener mListener;
+        private final int mDurationSecs = 10;
+        private AtomicBoolean mCanceled = new AtomicBoolean(false);
+
+        AdbConnectionPortPoller(AdbConnectionPortListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Slog.d(TAG, "Starting adb port property poller");
+            // Once adbwifi is enabled, we poll the service.adb.tls.port
+            // system property until we get the port, or -1 on failure.
+            // Let's also limit the polling to 10 seconds, just in case
+            // something went wrong.
+            for (int i = 0; i < mDurationSecs; ++i) {
+                if (mCanceled.get()) {
+                    return;
+                }
+
+                // If the property is set to -1, then that means adbd has failed
+                // to start the server. Otherwise we should have a valid port.
+                int port = SystemProperties.getInt(mAdbPortProp, Integer.MAX_VALUE);
+                if (port == -1 || (port > 0 && port <= 65535)) {
+                    mListener.onPortReceived(port);
+                    return;
+                }
+                SystemClock.sleep(1000);
+            }
+            Slog.w(TAG, "Failed to receive adb connection port");
+            mListener.onPortReceived(-1);
+        }
+
+        public void cancelAndWait() {
+            mCanceled.set(true);
+            if (this.isAlive()) {
+                try {
+                    this.join();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
+    class PortListenerImpl implements AdbConnectionPortListener {
+        public void onPortReceived(int port) {
+            Message msg = mHandler.obtainMessage(port > 0
+                     ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
+                     : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
+            msg.obj = port;
+            mHandler.sendMessage(msg);
+        }
     }
 
     class AdbDebuggingThread extends Thread {
@@ -213,6 +425,46 @@
                                 AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
                         msg.obj = key;
                         mHandler.sendMessage(msg);
+                    } else if (buffer[0] == 'W' && buffer[1] == 'E') {
+                        // adbd_auth.h and AdbTransportType.aidl need to be kept in
+                        // sync.
+                        byte transportType = buffer[2];
+                        String key = new String(Arrays.copyOfRange(buffer, 3, count));
+                        if (transportType == AdbTransportType.USB) {
+                            Slog.d(TAG, "Received USB TLS connected key message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else if (transportType == AdbTransportType.WIFI) {
+                            Slog.d(TAG, "Received WIFI TLS connected key message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MSG_WIFI_DEVICE_CONNECTED);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else {
+                            Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
+                                    + ")");
+                        }
+                    } else if (buffer[0] == 'W' && buffer[1] == 'F') {
+                        byte transportType = buffer[2];
+                        String key = new String(Arrays.copyOfRange(buffer, 3, count));
+                        if (transportType == AdbTransportType.USB) {
+                            Slog.d(TAG, "Received USB TLS disconnect message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else if (transportType == AdbTransportType.WIFI) {
+                            Slog.d(TAG, "Received WIFI TLS disconnect key message: " + key);
+                            Message msg = mHandler.obtainMessage(
+                                    AdbDebuggingHandler.MSG_WIFI_DEVICE_DISCONNECTED);
+                            msg.obj = key;
+                            mHandler.sendMessage(msg);
+                        } else {
+                            Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
+                                    + ")");
+                        }
                     } else {
                         Slog.e(TAG, "Wrong message: "
                                 + (new String(Arrays.copyOfRange(buffer, 0, 2))));
@@ -268,7 +520,156 @@
         }
     }
 
+    class AdbConnectionInfo {
+        private String mBssid;
+        private String mSsid;
+        private int mPort;
+
+        AdbConnectionInfo() {
+            mBssid = "";
+            mSsid = "";
+            mPort = -1;
+        }
+
+        AdbConnectionInfo(String bssid, String ssid) {
+            mBssid = bssid;
+            mSsid = ssid;
+        }
+
+        AdbConnectionInfo(AdbConnectionInfo other) {
+            mBssid = other.mBssid;
+            mSsid = other.mSsid;
+            mPort = other.mPort;
+        }
+
+        public String getBSSID() {
+            return mBssid;
+        }
+
+        public String getSSID() {
+            return mSsid;
+        }
+
+        public int getPort() {
+            return mPort;
+        }
+
+        public void setPort(int port) {
+            mPort = port;
+        }
+
+        public void clear() {
+            mBssid = "";
+            mSsid = "";
+            mPort = -1;
+        }
+    }
+
+    private void setAdbConnectionInfo(AdbConnectionInfo info) {
+        synchronized (mAdbConnectionInfo) {
+            if (info == null) {
+                mAdbConnectionInfo.clear();
+                return;
+            }
+            mAdbConnectionInfo = info;
+        }
+    }
+
+    private AdbConnectionInfo getAdbConnectionInfo() {
+        synchronized (mAdbConnectionInfo) {
+            return new AdbConnectionInfo(mAdbConnectionInfo);
+        }
+    }
+
     class AdbDebuggingHandler extends Handler {
+        private NotificationManager mNotificationManager;
+        private boolean mAdbNotificationShown;
+
+        private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                // We only care about when wifi is disabled, and when there is a wifi network
+                // change.
+                if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+                    int state = intent.getIntExtra(
+                            WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+                    if (state == WifiManager.WIFI_STATE_DISABLED) {
+                        Slog.i(TAG, "Wifi disabled. Disabling adbwifi.");
+                        Settings.Global.putInt(mContentResolver,
+                                Settings.Global.ADB_WIFI_ENABLED, 0);
+                    }
+                } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+                    // We only care about wifi type connections
+                    NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+                            WifiManager.EXTRA_NETWORK_INFO);
+                    if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+                        // Check for network disconnect
+                        if (!networkInfo.isConnected()) {
+                            Slog.i(TAG, "Network disconnected. Disabling adbwifi.");
+                            Settings.Global.putInt(mContentResolver,
+                                    Settings.Global.ADB_WIFI_ENABLED, 0);
+                            return;
+                        }
+
+                        WifiManager wifiManager =
+                                (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+                        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+                        if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+                            Slog.i(TAG, "Not connected to any wireless network."
+                                    + " Not enabling adbwifi.");
+                            Settings.Global.putInt(mContentResolver,
+                                    Settings.Global.ADB_WIFI_ENABLED, 0);
+                        }
+
+                        // Check for network change
+                        String bssid = wifiInfo.getBSSID();
+                        if (bssid == null || bssid.isEmpty()) {
+                            Slog.e(TAG, "Unable to get the wifi ap's BSSID. Disabling adbwifi.");
+                            Settings.Global.putInt(mContentResolver,
+                                    Settings.Global.ADB_WIFI_ENABLED, 0);
+                        }
+                        synchronized (mAdbConnectionInfo) {
+                            if (!bssid.equals(mAdbConnectionInfo.getBSSID())) {
+                                Slog.i(TAG, "Detected wifi network change. Disabling adbwifi.");
+                                Settings.Global.putInt(mContentResolver,
+                                        Settings.Global.ADB_WIFI_ENABLED, 0);
+                            }
+                        }
+                    }
+                }
+            }
+        };
+
+        private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
+
+        private boolean isTv() {
+            return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        }
+
+        private void setupNotifications() {
+            if (mNotificationManager != null) {
+                return;
+            }
+            mNotificationManager = (NotificationManager)
+                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+            if (mNotificationManager == null) {
+                Slog.e(TAG, "Unable to setup notifications for wireless debugging");
+                return;
+            }
+
+            // Ensure that the notification channels are set up
+            if (isTv()) {
+                // TV-specific notification channel
+                mNotificationManager.createNotificationChannel(
+                        new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
+                                mContext.getString(
+                                        com.android.internal.R.string
+                                                .adb_debugging_notification_channel_tv),
+                                NotificationManager.IMPORTANCE_HIGH));
+            }
+        }
+
         // The default time to schedule the job to keep the keystore updated with a currently
         // connected key as well as to removed expired keys.
         static final long UPDATE_KEYSTORE_JOB_INTERVAL = 86400000;
@@ -288,8 +689,50 @@
         static final int MESSAGE_ADB_UPDATE_KEYSTORE = 9;
         static final int MESSAGE_ADB_CONNECTED_KEY = 10;
 
+        // === Messages from the UI ==============
+        // UI asks adbd to enable adbdwifi
+        static final int MSG_ADBDWIFI_ENABLE = 11;
+        // UI asks adbd to disable adbdwifi
+        static final int MSG_ADBDWIFI_DISABLE = 12;
+        // Cancel pairing
+        static final int MSG_PAIRING_CANCEL = 14;
+        // Enable pairing by pairing code
+        static final int MSG_PAIR_PAIRING_CODE = 15;
+        // Enable pairing by QR code
+        static final int MSG_PAIR_QR_CODE = 16;
+        // UI asks to unpair (forget) a device.
+        static final int MSG_REQ_UNPAIR = 17;
+        // User allows debugging on the current network
+        static final int MSG_ADBWIFI_ALLOW = 18;
+        // User denies debugging on the current network
+        static final int MSG_ADBWIFI_DENY = 19;
+
+        // === Messages from the PairingThread ===========
+        // Result of the pairing
+        static final int MSG_RESPONSE_PAIRING_RESULT = 20;
+        // The port opened for pairing
+        static final int MSG_RESPONSE_PAIRING_PORT = 21;
+
+        // === Messages from adbd ================
+        // Notifies us a wifi device connected.
+        static final int MSG_WIFI_DEVICE_CONNECTED = 22;
+        // Notifies us a wifi device disconnected.
+        static final int MSG_WIFI_DEVICE_DISCONNECTED = 23;
+        // Notifies us the TLS server is connected and listening
+        static final int MSG_SERVER_CONNECTED = 24;
+        // Notifies us the TLS server is disconnected
+        static final int MSG_SERVER_DISCONNECTED = 25;
+
+        // === Messages we can send to adbd ===========
+        static final String MSG_DISCONNECT_DEVICE = "DD";
+        static final String MSG_DISABLE_ADBDWIFI = "DA";
+
         private AdbKeyStore mAdbKeyStore;
 
+        // Usb, Wi-Fi transports can be enabled together or separately, so don't break the framework
+        // connection unless all transport types are disconnected.
+        private int mAdbEnabledRefCount = 0;
+
         private ContentObserver mAuthTimeObserver = new ContentObserver(this) {
             @Override
             public void onChange(boolean selfChange, Uri uri) {
@@ -314,44 +757,111 @@
             mAdbKeyStore = adbKeyStore;
         }
 
+        // Show when at least one device is connected.
+        public void showAdbConnectedNotification(boolean show) {
+            final int id = SystemMessage.NOTE_ADB_WIFI_ACTIVE;
+            final int titleRes = com.android.internal.R.string.adbwifi_active_notification_title;
+            if (show == mAdbNotificationShown) {
+                return;
+            }
+            setupNotifications();
+            if (!mAdbNotificationShown) {
+                Resources r = mContext.getResources();
+                CharSequence title = r.getText(titleRes);
+                CharSequence message = r.getText(
+                        com.android.internal.R.string.adbwifi_active_notification_message);
+
+                Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
+                        intent, 0, null, UserHandle.CURRENT);
+
+                Notification notification =
+                        new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
+                                .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                                .setWhen(0)
+                                .setOngoing(true)
+                                .setTicker(title)
+                                .setDefaults(0)  // please be quiet
+                                .setColor(mContext.getColor(
+                                        com.android.internal.R.color
+                                                .system_notification_accent_color))
+                                .setContentTitle(title)
+                                .setContentText(message)
+                                .setContentIntent(pi)
+                                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                .extend(new Notification.TvExtender()
+                                        .setChannelId(ADB_NOTIFICATION_CHANNEL_ID_TV))
+                                .build();
+                mAdbNotificationShown = true;
+                mNotificationManager.notifyAsUser(null, id, notification,
+                        UserHandle.ALL);
+            } else {
+                mAdbNotificationShown = false;
+                mNotificationManager.cancelAsUser(null, id, UserHandle.ALL);
+            }
+        }
+
+        private void startAdbDebuggingThread() {
+            ++mAdbEnabledRefCount;
+            if (DEBUG) Slog.i(TAG, "startAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+            if (mAdbEnabledRefCount > 1) {
+                return;
+            }
+
+            registerForAuthTimeChanges();
+            mThread = new AdbDebuggingThread();
+            mThread.start();
+
+            mAdbKeyStore.updateKeyStore();
+            scheduleJobToUpdateAdbKeyStore();
+        }
+
+        private void stopAdbDebuggingThread() {
+            --mAdbEnabledRefCount;
+            if (DEBUG) Slog.i(TAG, "stopAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+            if (mAdbEnabledRefCount > 0) {
+                return;
+            }
+
+            if (mThread != null) {
+                mThread.stopListening();
+                mThread = null;
+            }
+
+            if (!mConnectedKeys.isEmpty()) {
+                for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
+                    mAdbKeyStore.setLastConnectionTime(entry.getKey(),
+                            System.currentTimeMillis());
+                }
+                sendPersistKeyStoreMessage();
+                mConnectedKeys.clear();
+                mWifiConnectedKeys.clear();
+            }
+            scheduleJobToUpdateAdbKeyStore();
+        }
+
         public void handleMessage(Message msg) {
+            if (mAdbKeyStore == null) {
+                mAdbKeyStore = new AdbKeyStore();
+            }
+
             switch (msg.what) {
                 case MESSAGE_ADB_ENABLED:
-                    if (mAdbEnabled) {
+                    if (mAdbUsbEnabled) {
                         break;
                     }
-                    registerForAuthTimeChanges();
-                    mAdbEnabled = true;
-
-                    mThread = new AdbDebuggingThread();
-                    mThread.start();
-
-                    mAdbKeyStore = new AdbKeyStore();
-                    mAdbKeyStore.updateKeyStore();
-                    scheduleJobToUpdateAdbKeyStore();
+                    startAdbDebuggingThread();
+                    mAdbUsbEnabled = true;
                     break;
 
                 case MESSAGE_ADB_DISABLED:
-                    if (!mAdbEnabled) {
+                    if (!mAdbUsbEnabled) {
                         break;
                     }
-
-                    mAdbEnabled = false;
-
-                    if (mThread != null) {
-                        mThread.stopListening();
-                        mThread = null;
-                    }
-
-                    if (!mConnectedKeys.isEmpty()) {
-                        for (String connectedKey : mConnectedKeys) {
-                            mAdbKeyStore.setLastConnectionTime(connectedKey,
-                                    System.currentTimeMillis());
-                        }
-                        sendPersistKeyStoreMessage();
-                        mConnectedKeys.clear();
-                    }
-                    scheduleJobToUpdateAdbKeyStore();
+                    stopAdbDebuggingThread();
+                    mAdbUsbEnabled = false;
                     break;
 
                 case MESSAGE_ADB_ALLOW: {
@@ -367,8 +877,8 @@
                     if (mThread != null) {
                         mThread.sendResponse("OK");
                         if (alwaysAllow) {
-                            if (!mConnectedKeys.contains(key)) {
-                                mConnectedKeys.add(key);
+                            if (!mConnectedKeys.containsKey(key)) {
+                                mConnectedKeys.put(key, 1);
                             }
                             mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
                             sendPersistKeyStoreMessage();
@@ -407,7 +917,7 @@
                     }
                     logAdbConnectionChanged(key, AdbProtoEnums.AWAITING_USER_APPROVAL, false);
                     mFingerprints = fingerprints;
-                    startConfirmation(key, mFingerprints);
+                    startConfirmationForKey(key, mFingerprints);
                     break;
                 }
 
@@ -419,6 +929,7 @@
                     if (mAdbKeyStore == null) {
                         mAdbKeyStore = new AdbKeyStore();
                     }
+                    mWifiConnectedKeys.clear();
                     mAdbKeyStore.deleteKeyStore();
                     cancelJobToUpdateAdbKeyStore();
                     break;
@@ -428,12 +939,17 @@
                     String key = (String) msg.obj;
                     boolean alwaysAllow = false;
                     if (key != null && key.length() > 0) {
-                        if (mConnectedKeys.contains(key)) {
+                        if (mConnectedKeys.containsKey(key)) {
                             alwaysAllow = true;
-                            mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
-                            sendPersistKeyStoreMessage();
-                            scheduleJobToUpdateAdbKeyStore();
-                            mConnectedKeys.remove(key);
+                            int refcount = mConnectedKeys.get(key) - 1;
+                            if (refcount == 0) {
+                                mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
+                                sendPersistKeyStoreMessage();
+                                scheduleJobToUpdateAdbKeyStore();
+                                mConnectedKeys.remove(key);
+                            } else {
+                                mConnectedKeys.put(key, refcount);
+                            }
                         }
                     } else {
                         Slog.w(TAG, "Received a disconnected key message with an empty key");
@@ -451,8 +967,8 @@
 
                 case MESSAGE_ADB_UPDATE_KEYSTORE: {
                     if (!mConnectedKeys.isEmpty()) {
-                        for (String connectedKey : mConnectedKeys) {
-                            mAdbKeyStore.setLastConnectionTime(connectedKey,
+                        for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
+                            mAdbKeyStore.setLastConnectionTime(entry.getKey(),
                                     System.currentTimeMillis());
                         }
                         sendPersistKeyStoreMessage();
@@ -469,8 +985,10 @@
                     if (key == null || key.length() == 0) {
                         Slog.w(TAG, "Received a connected key message with an empty key");
                     } else {
-                        if (!mConnectedKeys.contains(key)) {
-                            mConnectedKeys.add(key);
+                        if (!mConnectedKeys.containsKey(key)) {
+                            mConnectedKeys.put(key, 1);
+                        } else {
+                            mConnectedKeys.put(key, mConnectedKeys.get(key) + 1);
                         }
                         mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
                         sendPersistKeyStoreMessage();
@@ -479,6 +997,199 @@
                     }
                     break;
                 }
+                case MSG_ADBDWIFI_ENABLE: {
+                    if (mAdbWifiEnabled) {
+                        break;
+                    }
+
+                    AdbConnectionInfo currentInfo = getCurrentWifiApInfo();
+                    if (currentInfo == null) {
+                        Settings.Global.putInt(mContentResolver,
+                                Settings.Global.ADB_WIFI_ENABLED, 0);
+                        break;
+                    }
+
+                    if (!verifyWifiNetwork(currentInfo.getBSSID(),
+                            currentInfo.getSSID())) {
+                        // This means that the network is not in the list of trusted networks.
+                        // We'll give user a prompt on whether to allow wireless debugging on
+                        // the current wifi network.
+                        Settings.Global.putInt(mContentResolver,
+                                Settings.Global.ADB_WIFI_ENABLED, 0);
+                        break;
+                    }
+
+                    setAdbConnectionInfo(currentInfo);
+                    IntentFilter intentFilter =
+                            new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+                    intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+                    mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    mConnectionPortPoller =
+                            new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+                    mConnectionPortPoller.start();
+
+                    startAdbDebuggingThread();
+                    mAdbWifiEnabled = true;
+
+                    if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+                    break;
+                }
+                case MSG_ADBDWIFI_DISABLE:
+                    if (!mAdbWifiEnabled) {
+                        break;
+                    }
+                    mAdbWifiEnabled = false;
+                    setAdbConnectionInfo(null);
+                    mContext.unregisterReceiver(mBroadcastReceiver);
+
+                    if (mThread != null) {
+                        mThread.sendResponse(MSG_DISABLE_ADBDWIFI);
+                    }
+                    onAdbdWifiServerDisconnected(-1);
+                    stopAdbDebuggingThread();
+                    break;
+                case MSG_ADBWIFI_ALLOW:
+                    if (mAdbWifiEnabled) {
+                        break;
+                    }
+                    String bssid = (String) msg.obj;
+                    boolean alwaysAllow = msg.arg1 == 1;
+                    if (alwaysAllow) {
+                        mAdbKeyStore.addTrustedNetwork(bssid);
+                    }
+
+                    // Let's check again to make sure we didn't switch networks while verifying
+                    // the wifi bssid.
+                    AdbConnectionInfo newInfo = getCurrentWifiApInfo();
+                    if (newInfo == null || !bssid.equals(newInfo.getBSSID())) {
+                        break;
+                    }
+
+                    setAdbConnectionInfo(newInfo);
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 1);
+                    IntentFilter intentFilter =
+                            new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+                    intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+                    mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    mConnectionPortPoller =
+                            new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+                    mConnectionPortPoller.start();
+
+                    startAdbDebuggingThread();
+                    mAdbWifiEnabled = true;
+
+                    if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+                    break;
+                case MSG_ADBWIFI_DENY:
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 0);
+                    sendServerConnectionState(false, -1);
+                    break;
+                case MSG_REQ_UNPAIR: {
+                    String fingerprint = (String) msg.obj;
+                    // Tell adbd to disconnect the device if connected.
+                    String publicKey = mAdbKeyStore.findKeyFromFingerprint(fingerprint);
+                    if (publicKey == null || publicKey.isEmpty()) {
+                        Slog.e(TAG, "Not a known fingerprint [" + fingerprint + "]");
+                        break;
+                    }
+                    String cmdStr = MSG_DISCONNECT_DEVICE + publicKey;
+                    if (mThread != null) {
+                        mThread.sendResponse(cmdStr);
+                    }
+                    mAdbKeyStore.removeKey(publicKey);
+                    // Send the updated paired devices list to the UI.
+                    sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                    break;
+                }
+                case MSG_RESPONSE_PAIRING_RESULT: {
+                    Bundle bundle = (Bundle) msg.obj;
+                    String publicKey = bundle.getString("publicKey");
+                    onPairingResult(publicKey);
+                    // Send the updated paired devices list to the UI.
+                    sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                    break;
+                }
+                case MSG_RESPONSE_PAIRING_PORT: {
+                    int port = (int) msg.obj;
+                    sendPairingPortToUI(port);
+                    break;
+                }
+                case MSG_PAIR_PAIRING_CODE: {
+                    String pairingCode = createPairingCode(PAIRING_CODE_LENGTH);
+                    updateUIPairCode(pairingCode);
+                    mPairingThread = new PairingThread(pairingCode, null);
+                    mPairingThread.start();
+                    break;
+                }
+                case MSG_PAIR_QR_CODE: {
+                    Bundle bundle = (Bundle) msg.obj;
+                    String serviceName = bundle.getString("serviceName");
+                    String password = bundle.getString("password");
+                    mPairingThread = new PairingThread(password, serviceName);
+                    mPairingThread.start();
+                    break;
+                }
+                case MSG_PAIRING_CANCEL:
+                    if (mPairingThread != null) {
+                        mPairingThread.cancelPairing();
+                        try {
+                            mPairingThread.join();
+                        } catch (InterruptedException e) {
+                            Slog.w(TAG, "Error while waiting for pairing thread to quit.");
+                            e.printStackTrace();
+                        }
+                        mPairingThread = null;
+                    }
+                    break;
+                case MSG_WIFI_DEVICE_CONNECTED: {
+                    String key = (String) msg.obj;
+                    if (mWifiConnectedKeys.add(key)) {
+                        sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                        showAdbConnectedNotification(true);
+                    }
+                    break;
+                }
+                case MSG_WIFI_DEVICE_DISCONNECTED: {
+                    String key = (String) msg.obj;
+                    if (mWifiConnectedKeys.remove(key)) {
+                        sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+                        if (mWifiConnectedKeys.isEmpty()) {
+                            showAdbConnectedNotification(false);
+                        }
+                    }
+                    break;
+                }
+                case MSG_SERVER_CONNECTED: {
+                    int port = (int) msg.obj;
+                    onAdbdWifiServerConnected(port);
+                    synchronized (mAdbConnectionInfo) {
+                        mAdbConnectionInfo.setPort(port);
+                    }
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 1);
+                    break;
+                }
+                case MSG_SERVER_DISCONNECTED: {
+                    if (!mAdbWifiEnabled) {
+                        break;
+                    }
+                    int port = (int) msg.obj;
+                    onAdbdWifiServerDisconnected(port);
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 0);
+                    stopAdbDebuggingThread();
+                    if (mConnectionPortPoller != null) {
+                        mConnectionPortPoller.cancelAndWait();
+                        mConnectionPortPoller = null;
+                    }
+                    break;
+                }
             }
         }
 
@@ -540,6 +1251,142 @@
         private void cancelJobToUpdateAdbKeyStore() {
             removeMessages(AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEYSTORE);
         }
+
+        // Generates a random string of digits with size |size|.
+        private String createPairingCode(int size) {
+            String res = "";
+            SecureRandom rand = new SecureRandom();
+            for (int i = 0; i < size; ++i) {
+                res += rand.nextInt(10);
+            }
+
+            return res;
+        }
+
+        private void sendServerConnectionState(boolean connected, int port) {
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+            intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, connected
+                    ? AdbManager.WIRELESS_STATUS_CONNECTED
+                    : AdbManager.WIRELESS_STATUS_DISCONNECTED);
+            intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
+        private void onAdbdWifiServerConnected(int port) {
+            // Send the paired devices list to the UI
+            sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+            sendServerConnectionState(true, port);
+        }
+
+        private void onAdbdWifiServerDisconnected(int port) {
+            // The TLS server disconnected while we had wireless debugging enabled.
+            // Let's disable it.
+            mWifiConnectedKeys.clear();
+            showAdbConnectedNotification(false);
+            sendServerConnectionState(false, port);
+        }
+
+        /**
+         * Returns the [bssid, ssid] of the current access point.
+         */
+        private AdbConnectionInfo getCurrentWifiApInfo() {
+            WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+            if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+                Slog.i(TAG, "Not connected to any wireless network. Not enabling adbwifi.");
+                return null;
+            }
+
+            String ssid = null;
+            if (wifiInfo.isPasspointAp() || wifiInfo.isOsuAp()) {
+                ssid = wifiInfo.getPasspointProviderFriendlyName();
+            } else {
+                ssid = wifiInfo.getSSID();
+                if (ssid == null || WifiManager.UNKNOWN_SSID.equals(ssid)) {
+                    // OK, it's not in the connectionInfo; we have to go hunting for it
+                    List<WifiConfiguration> networks = wifiManager.getConfiguredNetworks();
+                    int length = networks.size();
+                    for (int i = 0; i < length; i++) {
+                        if (networks.get(i).networkId == wifiInfo.getNetworkId()) {
+                            ssid = networks.get(i).SSID;
+                        }
+                    }
+                    if (ssid == null) {
+                        Slog.e(TAG, "Unable to get ssid of the wifi AP.");
+                        return null;
+                    }
+                }
+            }
+
+            String bssid = wifiInfo.getBSSID();
+            if (bssid == null || bssid.isEmpty()) {
+                Slog.e(TAG, "Unable to get the wifi ap's BSSID.");
+                return null;
+            }
+            return new AdbConnectionInfo(bssid, ssid);
+        }
+
+        private boolean verifyWifiNetwork(String bssid, String ssid) {
+            // Check against a list of user-trusted networks.
+            if (mAdbKeyStore.isTrustedNetwork(bssid)) {
+                return true;
+            }
+
+            // Ask user to confirm using wireless debugging on this network.
+            startConfirmationForNetwork(ssid, bssid);
+            return false;
+        }
+
+        private void onPairingResult(String publicKey) {
+            if (publicKey == null) {
+                Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+                intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+                intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_SUCCESS);
+                String fingerprints = getFingerprints(publicKey);
+                String hostname = "nouser@nohostname";
+                String[] args = publicKey.split("\\s+");
+                if (args.length > 1) {
+                    hostname = args[1];
+                }
+                PairDevice device = new PairDevice(fingerprints, hostname, false);
+                intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                // Add the key into the keystore
+                mAdbKeyStore.setLastConnectionTime(publicKey,
+                        System.currentTimeMillis());
+                sendPersistKeyStoreMessage();
+                scheduleJobToUpdateAdbKeyStore();
+            }
+        }
+
+        private void sendPairingPortToUI(int port) {
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+            intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                    AdbManager.WIRELESS_STATUS_CONNECTED);
+            intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
+        private void sendPairedDevicesToUI(Map<String, PairDevice> devices) {
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+            // Map is not serializable, so need to downcast
+            intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
+
+        private void updateUIPairCode(String code) {
+            if (DEBUG) Slog.i(TAG, "updateUIPairCode: " + code);
+
+            Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+            intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code);
+            intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                    AdbManager.WIRELESS_STATUS_PAIRING_CODE);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        }
     }
 
     private String getFingerprints(String key) {
@@ -576,7 +1423,34 @@
         return sb.toString();
     }
 
-    private void startConfirmation(String key, String fingerprints) {
+    private void startConfirmationForNetwork(String ssid, String bssid) {
+        List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
+        extras.add(new AbstractMap.SimpleEntry<String, String>("ssid", ssid));
+        extras.add(new AbstractMap.SimpleEntry<String, String>("bssid", bssid));
+        int currentUserId = ActivityManager.getCurrentUser();
+        UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
+        String componentString;
+        if (userInfo.isAdmin()) {
+            componentString = Resources.getSystem().getString(
+                    com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
+        } else {
+            componentString = Resources.getSystem().getString(
+                    com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
+        }
+        ComponentName componentName = ComponentName.unflattenFromString(componentString);
+        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
+                || startConfirmationService(componentName, userInfo.getUserHandle(),
+                        extras)) {
+            return;
+        }
+        Slog.e(TAG, "Unable to start customAdbWifiNetworkConfirmation[SecondaryUser]Component "
+                + componentString + " as an Activity or a Service");
+    }
+
+    private void startConfirmationForKey(String key, String fingerprints) {
+        List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
+        extras.add(new AbstractMap.SimpleEntry<String, String>("key", key));
+        extras.add(new AbstractMap.SimpleEntry<String, String>("fingerprints", fingerprints));
         int currentUserId = ActivityManager.getCurrentUser();
         UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
         String componentString;
@@ -591,9 +1465,9 @@
                     R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent);
         }
         ComponentName componentName = ComponentName.unflattenFromString(componentString);
-        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), key, fingerprints)
+        if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
                 || startConfirmationService(componentName, userInfo.getUserHandle(),
-                        key, fingerprints)) {
+                        extras)) {
             return;
         }
         Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component "
@@ -604,9 +1478,9 @@
      * @return true if the componentName led to an Activity that was started.
      */
     private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle,
-            String key, String fingerprints) {
+            List<Map.Entry<String, String>> extras) {
         PackageManager packageManager = mContext.getPackageManager();
-        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+        Intent intent = createConfirmationIntent(componentName, extras);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
             try {
@@ -623,8 +1497,8 @@
      * @return true if the componentName led to a Service that was started.
      */
     private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle,
-            String key, String fingerprints) {
-        Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+            List<Map.Entry<String, String>> extras) {
+        Intent intent = createConfirmationIntent(componentName, extras);
         try {
             if (mContext.startServiceAsUser(intent, userHandle) != null) {
                 return true;
@@ -635,12 +1509,13 @@
         return false;
     }
 
-    private Intent createConfirmationIntent(ComponentName componentName, String key,
-            String fingerprints) {
+    private Intent createConfirmationIntent(ComponentName componentName,
+            List<Map.Entry<String, String>> extras) {
         Intent intent = new Intent();
         intent.setClassName(componentName.getPackageName(), componentName.getClassName());
-        intent.putExtra("key", key);
-        intent.putExtra("fingerprints", fingerprints);
+        for (Map.Entry<String, String> entry : extras) {
+            intent.putExtra(entry.getKey(), entry.getValue());
+        }
         return intent;
     }
 
@@ -733,7 +1608,8 @@
             mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
                                               : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
         } else if (transportType == AdbTransportType.WIFI) {
-            // TODO(joshuaduong): Not implemented
+            mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MSG_ADBDWIFI_ENABLE
+                                              : AdbDebuggingHandler.MSG_ADBDWIFI_DISABLE);
         } else {
             throw new IllegalArgumentException(
                     "setAdbEnabled called with unimplemented transport type=" + transportType);
@@ -767,6 +1643,87 @@
     }
 
     /**
+     * Allows wireless debugging on the network identified by {@code bssid} either once
+     * or always if {@code alwaysAllow} is {@code true}.
+     */
+    public void allowWirelessDebugging(boolean alwaysAllow, String bssid) {
+        Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MSG_ADBWIFI_ALLOW);
+        msg.arg1 = alwaysAllow ? 1 : 0;
+        msg.obj = bssid;
+        mHandler.sendMessage(msg);
+    }
+
+    /**
+     * Denies wireless debugging connection on the last requested network.
+     */
+    public void denyWirelessDebugging() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBWIFI_DENY);
+    }
+
+    /**
+     * Returns the port adbwifi is currently opened on.
+     */
+    public int getAdbWirelessPort() {
+        AdbConnectionInfo info = getAdbConnectionInfo();
+        if (info == null) {
+            return 0;
+        }
+        return info.getPort();
+    }
+
+    /**
+     * Returns the list of paired devices.
+     */
+    public Map<String, PairDevice> getPairedDevices() {
+        AdbKeyStore keystore = new AdbKeyStore();
+        return keystore.getPairedDevices();
+    }
+
+    /**
+     * Unpair with device
+     */
+    public void unpairDevice(String fingerprint) {
+        Message message = Message.obtain(mHandler,
+                                         AdbDebuggingHandler.MSG_REQ_UNPAIR,
+                                         fingerprint);
+        mHandler.sendMessage(message);
+    }
+
+    /**
+     * Enable pairing by pairing code
+     */
+    public void enablePairingByPairingCode() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIR_PAIRING_CODE);
+    }
+
+    /**
+     * Enable pairing by pairing code
+     */
+    public void enablePairingByQrCode(String serviceName, String password) {
+        Bundle bundle = new Bundle();
+        bundle.putString("serviceName", serviceName);
+        bundle.putString("password", password);
+        Message message = Message.obtain(mHandler,
+                                         AdbDebuggingHandler.MSG_PAIR_QR_CODE,
+                                         bundle);
+        mHandler.sendMessage(message);
+    }
+
+    /**
+     * Disables pairing
+     */
+    public void disablePairing() {
+        mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIRING_CANCEL);
+    }
+
+    /**
+     * Status enabled/disabled check
+     */
+    public boolean isAdbWifiEnabled() {
+        return mAdbWifiEnabled;
+    }
+
+    /**
      * Sends a message to the handler to persist the keystore.
      */
     private void sendPersistKeyStoreMessage() {
@@ -819,9 +1776,19 @@
         private File mKeyFile;
         private AtomicFile mAtomicKeyFile;
 
+        private List<String> mTrustedNetworks;
+        private static final int KEYSTORE_VERSION = 1;
+        private static final int MAX_SUPPORTED_KEYSTORE_VERSION = 1;
+        private static final String XML_KEYSTORE_START_TAG = "keyStore";
+        private static final String XML_ATTRIBUTE_VERSION = "version";
         private static final String XML_TAG_ADB_KEY = "adbKey";
         private static final String XML_ATTRIBUTE_KEY = "key";
         private static final String XML_ATTRIBUTE_LAST_CONNECTION = "lastConnection";
+        // A list of trusted networks a device can always wirelessly debug on (always allow).
+        // TODO: Move trusted networks list into a different file?
+        private static final String XML_TAG_WIFI_ACCESS_POINT = "wifiAP";
+        private static final String XML_ATTRIBUTE_WIFI_BSSID = "bssid";
+
         private static final String SYSTEM_KEY_FILE = "/adb_keys";
 
         /**
@@ -848,10 +1815,49 @@
         private void init() {
             initKeyFile();
             mKeyMap = getKeyMap();
+            mTrustedNetworks = getTrustedNetworks();
             mSystemKeys = getSystemKeysFromFile(SYSTEM_KEY_FILE);
             addUserKeysToKeyStore();
         }
 
+        public void addTrustedNetwork(String bssid) {
+            mTrustedNetworks.add(bssid);
+            sendPersistKeyStoreMessage();
+        }
+
+        public Map<String, PairDevice> getPairedDevices() {
+            Map<String, PairDevice> pairedDevices = new HashMap<String, PairDevice>();
+            for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
+                String fingerprints = getFingerprints(keyEntry.getKey());
+                String hostname = "nouser@nohostname";
+                String[] args = keyEntry.getKey().split("\\s+");
+                if (args.length > 1) {
+                    hostname = args[1];
+                }
+                pairedDevices.put(keyEntry.getKey(), new PairDevice(
+                        hostname, fingerprints, mWifiConnectedKeys.contains(keyEntry.getKey())));
+            }
+            return pairedDevices;
+        }
+
+        public String findKeyFromFingerprint(String fingerprint) {
+            for (Map.Entry<String, Long> entry : mKeyMap.entrySet()) {
+                String f = getFingerprints(entry.getKey());
+                if (fingerprint.equals(f)) {
+                    return entry.getKey();
+                }
+            }
+            return null;
+        }
+
+        public void removeKey(String key) {
+            if (mKeyMap.containsKey(key)) {
+                mKeyMap.remove(key);
+                writeKeys(mKeyMap.keySet());
+                sendPersistKeyStoreMessage();
+            }
+        }
+
         /**
          * Initializes the key file that will be used to persist the adb grants.
          */
@@ -921,6 +1927,78 @@
             try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
                 XmlPullParser parser = Xml.newPullParser();
                 parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+                // Check for supported keystore version.
+                XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
+                if (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
+                        Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+                                + tagName);
+                        return keyMap;
+                    }
+                    int keystoreVersion = Integer.parseInt(
+                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
+                        Slog.e(TAG, "Keystore version=" + keystoreVersion
+                                + " not supported (max_supported="
+                                + MAX_SUPPORTED_KEYSTORE_VERSION + ")");
+                        return keyMap;
+                    }
+                }
+                while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null) {
+                        break;
+                    } else if (!tagName.equals(XML_TAG_ADB_KEY)) {
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
+                    long connectionTime;
+                    try {
+                        connectionTime = Long.valueOf(
+                                parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION));
+                    } catch (NumberFormatException e) {
+                        Slog.e(TAG,
+                                "Caught a NumberFormatException parsing the last connection time: "
+                                        + e);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    keyMap.put(key, connectionTime);
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "Caught an IOException parsing the XML key file: ", e);
+            } catch (XmlPullParserException e) {
+                Slog.w(TAG, "Caught XmlPullParserException parsing the XML key file: ", e);
+                // The file could be written in a format prior to introducing keystore tag.
+                return getKeyMapBeforeKeystoreVersion();
+            }
+            return keyMap;
+        }
+
+
+        /**
+         * Returns the key map with the keys and last connection times from the key file.
+         * This implementation was prior to adding the XML_KEYSTORE_START_TAG.
+         */
+        private Map<String, Long> getKeyMapBeforeKeystoreVersion() {
+            Map<String, Long> keyMap = new HashMap<String, Long>();
+            // if the AtomicFile could not be instantiated before attempt again; if it still fails
+            // return an empty key map.
+            if (mAtomicKeyFile == null) {
+                initKeyFile();
+                if (mAtomicKeyFile == null) {
+                    Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
+                    return keyMap;
+                }
+            }
+            if (!mAtomicKeyFile.exists()) {
+                return keyMap;
+            }
+            try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(keyStream, StandardCharsets.UTF_8.name());
                 XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY);
                 while (parser.next() != XmlPullParser.END_DOCUMENT) {
                     String tagName = parser.getName();
@@ -951,6 +2029,63 @@
         }
 
         /**
+         * Returns the map of trusted networks from the keystore file.
+         *
+         * This was implemented in keystore version 1.
+         */
+        private List<String> getTrustedNetworks() {
+            List<String> trustedNetworks = new ArrayList<String>();
+            // if the AtomicFile could not be instantiated before attempt again; if it still fails
+            // return an empty key map.
+            if (mAtomicKeyFile == null) {
+                initKeyFile();
+                if (mAtomicKeyFile == null) {
+                    Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
+                    return trustedNetworks;
+                }
+            }
+            if (!mAtomicKeyFile.exists()) {
+                return trustedNetworks;
+            }
+            try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+                // Check for supported keystore version.
+                XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
+                if (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
+                        Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+                                + tagName);
+                        return trustedNetworks;
+                    }
+                    int keystoreVersion = Integer.parseInt(
+                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
+                        Slog.e(TAG, "Keystore version=" + keystoreVersion
+                                + " not supported (max_supported="
+                                + MAX_SUPPORTED_KEYSTORE_VERSION);
+                        return trustedNetworks;
+                    }
+                }
+                while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                    String tagName = parser.getName();
+                    if (tagName == null) {
+                        break;
+                    } else if (!tagName.equals(XML_TAG_WIFI_ACCESS_POINT)) {
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String bssid = parser.getAttributeValue(null, XML_ATTRIBUTE_WIFI_BSSID);
+                    trustedNetworks.add(bssid);
+                }
+            } catch (IOException | XmlPullParserException | NumberFormatException e) {
+                Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
+            }
+            return trustedNetworks;
+        }
+
+        /**
          * Updates the keystore with keys that were previously set to be always allowed before the
          * connection time of keys was tracked.
          */
@@ -986,7 +2121,7 @@
             // if there is nothing in the key map then ensure any keys left in the keystore files
             // are deleted as well.
             filterOutOldKeys();
-            if (mKeyMap.isEmpty()) {
+            if (mKeyMap.isEmpty() && mTrustedNetworks.isEmpty()) {
                 deleteKeyStore();
                 return;
             }
@@ -1004,6 +2139,8 @@
                 serializer.setOutput(keyStream, StandardCharsets.UTF_8.name());
                 serializer.startDocument(null, true);
 
+                serializer.startTag(null, XML_KEYSTORE_START_TAG);
+                serializer.attribute(null, XML_ATTRIBUTE_VERSION, String.valueOf(KEYSTORE_VERSION));
                 for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
                     serializer.startTag(null, XML_TAG_ADB_KEY);
                     serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
@@ -1011,7 +2148,12 @@
                             String.valueOf(keyEntry.getValue()));
                     serializer.endTag(null, XML_TAG_ADB_KEY);
                 }
-
+                for (String bssid : mTrustedNetworks) {
+                    serializer.startTag(null, XML_TAG_WIFI_ACCESS_POINT);
+                    serializer.attribute(null, XML_ATTRIBUTE_WIFI_BSSID, bssid);
+                    serializer.endTag(null, XML_TAG_WIFI_ACCESS_POINT);
+                }
+                serializer.endTag(null, XML_KEYSTORE_START_TAG);
                 serializer.endDocument();
                 mAtomicKeyFile.finishWrite(keyStream);
             } catch (IOException e) {
@@ -1072,6 +2214,7 @@
          */
         public void deleteKeyStore() {
             mKeyMap.clear();
+            mTrustedNetworks.clear();
             deleteKeyFile();
             if (mAtomicKeyFile == null) {
                 return;
@@ -1153,5 +2296,14 @@
                 return false;
             }
         }
+
+        /**
+         * Returns whether the specified bssid is in the list of trusted networks. This requires
+         * that the user previously allowed wireless debugging on this network and selected the
+         * option to 'Always allow'.
+         */
+        public boolean isTrustedNetwork(String bssid) {
+            return mTrustedNetworks.contains(bssid);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 0d161b9..7ccb284 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -21,8 +21,10 @@
 import android.annotation.UserIdInt;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
+import android.debug.AdbManager;
 import android.debug.AdbManagerInternal;
 import android.debug.AdbTransportType;
 import android.debug.IAdbManager;
@@ -34,6 +36,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.adb.AdbServiceDumpProto;
 import android.sysprop.AdbProperties;
@@ -44,6 +47,7 @@
 
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.dump.DualDumpOutputStream;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -54,6 +58,7 @@
 import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization
@@ -61,6 +66,27 @@
  */
 public class AdbService extends IAdbManager.Stub {
     /**
+     * Adb native daemon.
+     */
+    static final String ADBD = "adbd";
+
+    /**
+     * Command to start native service.
+     */
+    static final String CTL_START = "ctl.start";
+
+    /**
+     * Command to start native service.
+     */
+    static final String CTL_STOP = "ctl.stop";
+
+    // The tcp port adb is currently using
+    AtomicInteger mConnectionPort = new AtomicInteger(-1);
+
+    private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener();
+    private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller;
+
+    /**
      * Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
      */
     public static class Lifecycle extends SystemService {
@@ -129,9 +155,8 @@
             mIsAdbUsbEnabled = containsFunction(
                     SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
                     UsbManager.USB_FUNCTION_ADB);
-            // TODO(joshuaduong): Read the adb wifi state from a persistent system
-            // property (persist.sys.adb.wifi).
-            mIsAdbWifiEnabled = false;
+            mIsAdbWifiEnabled = "1".equals(
+                    SystemProperties.get(WIFI_PERSISTENT_CONFIG_PROPERTY, "0"));
 
             // register observer to listen for settings changes
             mObserver = new AdbSettingsObserver();
@@ -189,6 +214,7 @@
      * May also contain vendor-specific default functions for testing purposes.
      */
     private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
+    private static final String WIFI_PERSISTENT_CONFIG_PROPERTY = "persist.adb.tls_server.enable";
 
     private final Context mContext;
     private final ContentResolver mContentResolver;
@@ -245,8 +271,9 @@
     }
 
     @Override
-    public void allowDebugging(boolean alwaysAllow, String publicKey) {
+    public void allowDebugging(boolean alwaysAllow, @NonNull String publicKey) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+        Preconditions.checkStringNotEmpty(publicKey);
         if (mDebuggingManager != null) {
             mDebuggingManager.allowDebugging(alwaysAllow, publicKey);
         }
@@ -296,53 +323,118 @@
     }
 
     @Override
-    public void allowWirelessDebugging(boolean alwaysAllow, String bssid) {
+    public void allowWirelessDebugging(boolean alwaysAllow, @NonNull String bssid) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        Preconditions.checkStringNotEmpty(bssid);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.allowWirelessDebugging(alwaysAllow, bssid);
+        }
     }
 
     @Override
     public void denyWirelessDebugging() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        if (mDebuggingManager != null) {
+            mDebuggingManager.denyWirelessDebugging();
+        }
     }
 
     @Override
     public Map<String, PairDevice> getPairedDevices() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        if (mDebuggingManager != null) {
+            return mDebuggingManager.getPairedDevices();
+        }
         return null;
     }
 
     @Override
-    public void unpairDevice(String fingerprint) {
+    public void unpairDevice(@NonNull String fingerprint) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        Preconditions.checkStringNotEmpty(fingerprint);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.unpairDevice(fingerprint);
+        }
     }
 
     @Override
     public void enablePairingByPairingCode() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        if (mDebuggingManager != null) {
+            mDebuggingManager.enablePairingByPairingCode();
+        }
     }
 
     @Override
-    public void enablePairingByQrCode(String serviceName, String password) {
+    public void enablePairingByQrCode(@NonNull String serviceName, @NonNull String password) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        Preconditions.checkStringNotEmpty(serviceName);
+        Preconditions.checkStringNotEmpty(password);
+        if (mDebuggingManager != null) {
+            mDebuggingManager.enablePairingByQrCode(serviceName, password);
+        }
     }
 
     @Override
     public void disablePairing() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
+        if (mDebuggingManager != null) {
+            mDebuggingManager.disablePairing();
+        }
     }
 
     @Override
     public int getAdbWirelessPort() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
-        // TODO(joshuaduong): NOT IMPLEMENTED
-        return 0;
+        if (mDebuggingManager != null) {
+            return mDebuggingManager.getAdbWirelessPort();
+        }
+        // If ro.adb.secure=0
+        return mConnectionPort.get();
+    }
+
+    /**
+     * This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will
+     * do this.
+     */
+    class AdbConnectionPortListener implements AdbDebuggingManager.AdbConnectionPortListener {
+        public void onPortReceived(int port) {
+            if (port > 0 && port <= 65535) {
+                mConnectionPort.set(port);
+            } else {
+                mConnectionPort.set(-1);
+                // Turn off wifi debugging, since the server did not start.
+                try {
+                    Settings.Global.putInt(mContentResolver,
+                            Settings.Global.ADB_WIFI_ENABLED, 0);
+                } catch (SecurityException e) {
+                    // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
+                    // be changed.
+                    Slog.d(TAG, "ADB_ENABLED is restricted.");
+                }
+            }
+            broadcastPortInfo(mConnectionPort.get());
+        }
+    }
+
+    private void broadcastPortInfo(int port) {
+        Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+        intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, (port >= 0)
+                ? AdbManager.WIRELESS_STATUS_CONNECTED
+                : AdbManager.WIRELESS_STATUS_DISCONNECTED);
+        intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        Slog.i(TAG, "sent port broadcast port=" + port);
+    }
+
+    private void startAdbd() {
+        SystemProperties.set(CTL_START, ADBD);
+    }
+
+    private void stopAdbd() {
+        if (!mIsAdbUsbEnabled && !mIsAdbWifiEnabled) {
+            SystemProperties.set(CTL_STOP, ADBD);
+        }
     }
 
     private void setAdbEnabled(boolean enable, byte transportType) {
@@ -356,11 +448,33 @@
             mIsAdbUsbEnabled = enable;
         } else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
             mIsAdbWifiEnabled = enable;
+            if (mIsAdbWifiEnabled) {
+                if (!AdbProperties.secure().orElse(false) && mDebuggingManager == null) {
+                    // Start adbd. If this is secure adb, then we defer enabling adb over WiFi.
+                    SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+                    mConnectionPortPoller =
+                            new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+                    mConnectionPortPoller.start();
+                }
+            } else {
+                // Stop adb over WiFi.
+                SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0");
+                if (mConnectionPortPoller != null) {
+                    mConnectionPortPoller.cancelAndWait();
+                    mConnectionPortPoller = null;
+                }
+            }
         } else {
             // No change
             return;
         }
 
+        if (enable) {
+            startAdbd();
+        } else {
+            stopAdbd();
+        }
+
         for (IAdbTransport transport : mTransports.values()) {
             try {
                 transport.onAdbEnabled(enable, transportType);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7f98c7f..8ebbce3 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -36,6 +36,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -88,11 +89,13 @@
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.webkit.WebViewZygote;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.ServiceState;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -177,6 +180,10 @@
     /** Temporary list for holding the results of calls to {@link #collectPackageServicesLocked} */
     private ArrayList<ServiceRecord> mTmpCollectionResults = null;
 
+    /** Mapping from uid to their foreground service AppOpCallbacks (if they have one). */
+    @GuardedBy("mAm")
+    private final SparseArray<AppOpCallback> mFgsAppOpCallbacks = new SparseArray<>();
+
     /**
      * For keeping ActiveForegroundApps retaining state while the screen is off.
      */
@@ -1455,7 +1462,9 @@
                                 null, true, false, "");
                         FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                                 r.appInfo.uid, r.shortInstanceName,
-                                FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
+                                FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
+                                r.mAllowWhileInUsePermissionInFgs);
+                        registerAppOpCallbackLocked(r);
                         mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
                     }
                     r.postNotification();
@@ -1504,9 +1513,11 @@
                 mAm.mAppOpsService.finishOperation(
                         AppOpsManager.getToken(mAm.mAppOpsService),
                         AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
+                unregisterAppOpCallbackLocked(r);
                 FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                         r.appInfo.uid, r.shortInstanceName,
-                        FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
+                        FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
+                        r.mAllowWhileInUsePermissionInFgs);
                 mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
                 if (r.app != null) {
                     mAm.updateLruProcessLocked(r.app, false, null);
@@ -1527,6 +1538,207 @@
         }
     }
 
+    /** Registers an AppOpCallback for monitoring special AppOps for this foreground service. */
+    private void registerAppOpCallbackLocked(@NonNull ServiceRecord r) {
+        if (r.app == null) {
+            return;
+        }
+        final int uid = r.appInfo.uid;
+        AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
+        if (callback == null) {
+            callback = new AppOpCallback(r.app, mAm.getAppOpsManager());
+            mFgsAppOpCallbacks.put(uid, callback);
+        }
+        callback.registerLocked();
+    }
+
+    /** Unregisters a foreground service's AppOpCallback. */
+    private void unregisterAppOpCallbackLocked(@NonNull ServiceRecord r) {
+        final int uid = r.appInfo.uid;
+        final AppOpCallback callback = mFgsAppOpCallbacks.get(uid);
+        if (callback != null) {
+            callback.unregisterLocked();
+            if (callback.isObsoleteLocked()) {
+                mFgsAppOpCallbacks.remove(uid);
+            }
+        }
+    }
+
+    /**
+     * For monitoring when {@link #LOGGED_AP_OPS} AppOps occur by an app while it is holding
+     * at least one foreground service and is not also in the TOP state.
+     * Once the uid no longer holds any foreground services, this callback becomes stale
+     * (marked by {@link #isObsoleteLocked()}) and must no longer be used.
+     *
+     * Methods that end in Locked should only be called while the mAm lock is held.
+     */
+    private static final class AppOpCallback {
+        /** AppOps that should be logged if they occur during a foreground service. */
+        private static final int[] LOGGED_AP_OPS = new int[] {
+                AppOpsManager.OP_COARSE_LOCATION,
+                AppOpsManager.OP_FINE_LOCATION,
+                AppOpsManager.OP_RECORD_AUDIO,
+                AppOpsManager.OP_CAMERA
+        };
+
+        private final ProcessRecord mProcessRecord;
+
+        /** Count of acceptances per appop (for LOGGED_AP_OPS) during this fgs session. */
+        @GuardedBy("mCounterLock")
+        private final SparseIntArray mAcceptedOps = new SparseIntArray();
+        /** Count of rejections per appop (for LOGGED_AP_OPS) during this fgs session. */
+        @GuardedBy("mCounterLock")
+        private final SparseIntArray mRejectedOps = new SparseIntArray();
+
+        /** Lock for the purposes of mAcceptedOps and mRejectedOps. */
+        private final Object mCounterLock = new Object();
+
+        /**
+         * AppOp Mode (e.g. {@link AppOpsManager#MODE_ALLOWED} per op.
+         * This currently cannot change without the process being killed, so they are constants.
+         */
+        private final SparseIntArray mAppOpModes = new SparseIntArray();
+
+        /**
+         * Number of foreground services currently associated with this AppOpCallback (i.e.
+         * currently held for this uid).
+         */
+        @GuardedBy("mAm")
+        private int mNumFgs = 0;
+
+        /**
+         * Indicates that this Object is stale and must not be used.
+         * Specifically, when mNumFgs decreases down to 0, the callbacks will be unregistered and
+         * this AppOpCallback is unusable.
+         */
+        @GuardedBy("mAm")
+        private boolean mDestroyed = false;
+
+        private final AppOpsManager mAppOpsManager;
+
+        AppOpCallback(@NonNull ProcessRecord r, @NonNull AppOpsManager appOpsManager) {
+            mProcessRecord = r;
+            mAppOpsManager = appOpsManager;
+            for (int op : LOGGED_AP_OPS) {
+                int mode = appOpsManager.unsafeCheckOpRawNoThrow(op, r.uid, r.info.packageName);
+                mAppOpModes.put(op, mode);
+            }
+        }
+
+        private final AppOpsManager.OnOpNotedListener mOpNotedCallback =
+                new AppOpsManager.OnOpNotedListener() {
+                    @Override
+                    public void onOpNoted(int op, int uid, String pkgName, int result) {
+                        if (uid == mProcessRecord.uid && isNotTop()) {
+                            incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED);
+                        }
+                    }
+        };
+
+        private final AppOpsManager.OnOpActiveChangedInternalListener mOpActiveCallback =
+                new AppOpsManager.OnOpActiveChangedInternalListener() {
+                    @Override
+                    public void onOpActiveChanged(int op, int uid, String pkgName, boolean active) {
+                        if (uid == mProcessRecord.uid && active && isNotTop()) {
+                            incrementOpCount(op, true);
+                        }
+                    }
+        };
+
+        private boolean isNotTop() {
+            return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP;
+        }
+
+        private void incrementOpCount(int op, boolean allowed) {
+            synchronized (mCounterLock) {
+                final SparseIntArray counter = allowed ? mAcceptedOps : mRejectedOps;
+                final int index = counter.indexOfKey(op);
+                if (index < 0) {
+                    counter.put(op, 1);
+                } else {
+                    counter.setValueAt(index, counter.valueAt(index) + 1);
+                }
+            }
+        }
+
+        void registerLocked() {
+            if (isObsoleteLocked()) {
+                Slog.wtf(TAG, "Trying to register on a stale AppOpCallback.");
+                return;
+            }
+            mNumFgs++;
+            if (mNumFgs == 1) {
+                mAppOpsManager.startWatchingNoted(LOGGED_AP_OPS, mOpNotedCallback);
+                mAppOpsManager.startWatchingActive(LOGGED_AP_OPS, mOpActiveCallback);
+            }
+        }
+
+        void unregisterLocked() {
+            mNumFgs--;
+            if (mNumFgs <= 0) {
+                mDestroyed = true;
+                logFinalValues();
+                mAppOpsManager.stopWatchingNoted(mOpNotedCallback);
+                mAppOpsManager.stopWatchingActive(mOpActiveCallback);
+            }
+        }
+
+        /**
+         * Indicates that all foreground services for this uid are now over and the callback is
+         * stale and must never be used again.
+         */
+        boolean isObsoleteLocked() {
+            return mDestroyed;
+        }
+
+        private void logFinalValues() {
+            synchronized (mCounterLock) {
+                for (int op : LOGGED_AP_OPS) {
+                    final int acceptances = mAcceptedOps.get(op);
+                    final int rejections = mRejectedOps.get(op);
+                    if (acceptances > 0 ||  rejections > 0) {
+                        FrameworkStatsLog.write(
+                                FrameworkStatsLog.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED,
+                                mProcessRecord.uid, opToEnum(op),
+                                modeToEnum(mAppOpModes.get(op)),
+                                acceptances, rejections
+                        );
+                    }
+                }
+            }
+        }
+
+        /** Maps AppOp mode to atoms.proto enum. */
+        private static int modeToEnum(int mode) {
+            switch (mode) {
+                case AppOpsManager.MODE_ALLOWED: return FrameworkStatsLog
+                        .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_ALLOWED;
+                case AppOpsManager.MODE_IGNORED: return FrameworkStatsLog
+                        .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_IGNORED;
+                case AppOpsManager.MODE_FOREGROUND: return FrameworkStatsLog
+                        .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_FOREGROUND;
+                default: return FrameworkStatsLog
+                        .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_MODE__MODE_UNKNOWN;
+            }
+        }
+    }
+
+    /** Maps AppOp op value to atoms.proto enum. */
+    private static int opToEnum(int op) {
+        switch (op) {
+            case AppOpsManager.OP_COARSE_LOCATION: return FrameworkStatsLog
+                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_COARSE_LOCATION;
+            case AppOpsManager.OP_FINE_LOCATION: return FrameworkStatsLog
+                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_FINE_LOCATION;
+            case AppOpsManager.OP_CAMERA: return FrameworkStatsLog
+                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_CAMERA;
+            case AppOpsManager.OP_RECORD_AUDIO: return FrameworkStatsLog
+                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_RECORD_AUDIO;
+            default: return FrameworkStatsLog
+                    .FOREGROUND_SERVICE_APP_OP_SESSION_ENDED__APP_OP_NAME__OP_NONE;
+        }
+    }
+
     private void cancelForegroundNotificationLocked(ServiceRecord r) {
         if (r.foregroundId != 0) {
             // First check to see if this app has any other active foreground services
@@ -3136,9 +3348,11 @@
             mAm.mAppOpsService.finishOperation(
                     AppOpsManager.getToken(mAm.mAppOpsService),
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
+            unregisterAppOpCallbackLocked(r);
             FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                     r.appInfo.uid, r.shortInstanceName,
-                    FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
+                    FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
+                    r.mAllowWhileInUsePermissionInFgs);
             mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ca0b03df..cea3bb8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -218,6 +218,7 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.ProcessInfo;
 import android.content.pm.ProviderInfo;
+import android.content.pm.ProviderInfoList;
 import android.content.pm.ResolveInfo;
 import android.content.pm.SELinuxUtil;
 import android.content.pm.ServiceInfo;
@@ -5151,12 +5152,13 @@
             if (mPlatformCompat != null) {
                 mPlatformCompat.resetReporting(app.info);
             }
+            final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
             if (app.isolatedEntryPoint != null) {
                 // This is an isolated process which should just call an entry point instead of
                 // being bound to an application.
                 thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
             } else if (instr2 != null) {
-                thread.bindApplication(processName, appInfo, providers,
+                thread.bindApplication(processName, appInfo, providerList,
                         instr2.mClass,
                         profilerInfo, instr2.mArguments,
                         instr2.mWatcher,
@@ -5169,7 +5171,7 @@
                         buildSerial, autofillOptions, contentCaptureOptions,
                         app.mDisabledCompatChanges);
             } else {
-                thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
+                thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
                         null, null, null, testMode,
                         mBinderTransactionTrackingEnabled, enableTrackAllocation,
                         isRestrictedBackupMode || !normalMode, app.isPersistent(),
@@ -6285,9 +6287,9 @@
     }
 
     @VisibleForTesting
-    public void grantImplicitAccess(int userId, Intent intent, int callingUid, int targetAppId) {
+    public void grantImplicitAccess(int userId, Intent intent, int visibleUid, int recipientAppId) {
         getPackageManagerInternalLocked().
-                grantImplicitAccess(userId, intent, callingUid, targetAppId);
+                grantImplicitAccess(userId, intent, recipientAppId, visibleUid, true /*direct*/);
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index c7f5f63..bf79729 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2570,8 +2570,6 @@
         switch (op) {
             case "move-task":
                 return runStackMoveTask(pw);
-            case "resize-animated":
-                return runStackResizeAnimated(pw);
             case "resize-docked-stack":
                 return runStackResizeDocked(pw);
             case "positiontask":
@@ -2648,23 +2646,6 @@
         return 0;
     }
 
-    int runStackResizeAnimated(PrintWriter pw) throws RemoteException {
-        String stackIdStr = getNextArgRequired();
-        int stackId = Integer.parseInt(stackIdStr);
-        final Rect bounds;
-        if ("null".equals(peekNextArg())) {
-            bounds = null;
-        } else {
-            bounds = getBounds();
-            if (bounds == null) {
-                getErrPrintWriter().println("Error: invalid input bounds");
-                return -1;
-            }
-        }
-        mTaskInterface.animateResizePinnedStack(stackId, bounds, -1);
-        return 0;
-    }
-
     int runStackResizeDocked(PrintWriter pw) throws RemoteException {
         final Rect bounds = getBounds();
         final Rect taskBounds = getBounds();
@@ -3285,8 +3266,6 @@
             pw.println("       move-task <TASK_ID> <STACK_ID> [true|false]");
             pw.println("           Move <TASK_ID> from its current stack to the top (true) or");
             pw.println("           bottom (false) of <STACK_ID>.");
-            pw.println("       resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>");
-            pw.println("           Same as resize, but allow animation.");
             pw.println("       resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]");
             pw.println("           Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>");
             pw.println("           and supplying temporary different task bounds indicated by");
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index fb48db4..a7125b4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -146,6 +146,7 @@
     static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80;
     static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90;
     static final int USER_UNLOCK_MSG = 100;
+    static final int USER_UNLOCKED_MSG = 105;
     static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110;
     static final int START_USER_SWITCH_FG_MSG = 120;
 
@@ -625,6 +626,9 @@
                     FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_BOOT_COMPLETED,
                     elapsedTimeMs);
         }
+
+        mHandler.obtainMessage(USER_UNLOCKED_MSG, userId, 0).sendToTarget();
+
         final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
         bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -2366,6 +2370,9 @@
                 });
                 finishUserUnlocked((UserState) msg.obj);
                 break;
+            case USER_UNLOCKED_MSG:
+                mInjector.getSystemServiceManager().onUserUnlocked(msg.arg1);
+                break;
             case USER_CURRENT_MSG:
                 mInjector.batteryStatsServiceNoteEvent(
                         BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH,
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index fbad8de..5320453 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -16,9 +16,6 @@
 
 package com.android.server.dreams;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -27,10 +24,10 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
 import android.os.IRemoteCallback;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.IBinder.DeathRecipient;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -38,15 +35,14 @@
 import android.service.dreams.IDreamService;
 import android.util.Slog;
 import android.view.IWindowManager;
-import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
 import java.io.PrintWriter;
 import java.util.NoSuchElementException;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-
 /**
  * Internal controller for starting and stopping the current dream and managing related state.
  *
@@ -86,12 +82,9 @@
         }
     };
 
-    private final Runnable mStopStubbornDreamRunnable = new Runnable() {
-        @Override
-        public void run() {
-            Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted");
-            stopDream(true /*immediate*/);
-        }
+    private final Runnable mStopStubbornDreamRunnable = () -> {
+        Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted");
+        stopDream(true /*immediate*/);
     };
 
     public DreamController(Context context, Handler handler, Listener listener) {
@@ -140,14 +133,6 @@
             MetricsLogger.visible(mContext,
                     mCurrentDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING);
 
-            try {
-                mIWindowManager.addWindowToken(token, TYPE_DREAM, DEFAULT_DISPLAY);
-            } catch (RemoteException ex) {
-                Slog.e(TAG, "Unable to add window token for dream.", ex);
-                stopDream(true /*immediate*/);
-                return;
-            }
-
             Intent intent = new Intent(DreamService.SERVICE_INTERFACE);
             intent.setComponent(name);
             intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
@@ -216,9 +201,6 @@
             }
 
             if (oldDream.mService != null) {
-                // Tell the dream that it's being stopped so that
-                // it can shut down nicely before we yank its window token out from
-                // under it.
                 try {
                     oldDream.mService.detach();
                 } catch (RemoteException ex) {
@@ -238,18 +220,7 @@
             }
             oldDream.releaseWakeLockIfNeeded();
 
-            try {
-                mIWindowManager.removeWindowToken(oldDream.mToken, DEFAULT_DISPLAY);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Error removing window token for dream.", ex);
-            }
-
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mListener.onDreamStopped(oldDream.mToken);
-                }
-            });
+            mHandler.post(() -> mListener.onDreamStopped(oldDream.mToken));
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -313,13 +284,10 @@
         // May be called on any thread.
         @Override
         public void binderDied() {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mService = null;
-                    if (mCurrentDream == DreamRecord.this) {
-                        stopDream(true /*immediate*/);
-                    }
+            mHandler.post(() -> {
+                mService = null;
+                if (mCurrentDream == DreamRecord.this) {
+                    stopDream(true /*immediate*/);
                 }
             });
         }
@@ -327,16 +295,13 @@
         // May be called on any thread.
         @Override
         public void onServiceConnected(ComponentName name, final IBinder service) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mConnected = true;
-                    if (mCurrentDream == DreamRecord.this && mService == null) {
-                        attach(IDreamService.Stub.asInterface(service));
-                        // Wake lock will be released once dreaming starts.
-                    } else {
-                        releaseWakeLockIfNeeded();
-                    }
+            mHandler.post(() -> {
+                mConnected = true;
+                if (mCurrentDream == DreamRecord.this && mService == null) {
+                    attach(IDreamService.Stub.asInterface(service));
+                    // Wake lock will be released once dreaming starts.
+                } else {
+                    releaseWakeLockIfNeeded();
                 }
             });
         }
@@ -344,13 +309,10 @@
         // May be called on any thread.
         @Override
         public void onServiceDisconnected(ComponentName name) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mService = null;
-                    if (mCurrentDream == DreamRecord.this) {
-                        stopDream(true /*immediate*/);
-                    }
+            mHandler.post(() -> {
+                mService = null;
+                if (mCurrentDream == DreamRecord.this) {
+                    stopDream(true /*immediate*/);
                 }
             });
         }
@@ -373,4 +335,4 @@
             }
         };
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 3052e3c..eb0257e 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -268,6 +268,10 @@
         }
     }
 
+    private ComponentName getActiveDreamComponentInternal(boolean doze) {
+        return chooseDreamForUser(doze, ActivityManager.getCurrentUser());
+    }
+
     private ComponentName chooseDreamForUser(boolean doze, int userId) {
         if (doze) {
             ComponentName dozeComponent = getDozeComponent(userId);
@@ -501,12 +505,18 @@
 
         @Override // Binder call
         public ComponentName[] getDreamComponents() {
-            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+            return getDreamComponentsForUser(UserHandle.getCallingUserId());
+        }
 
-            final int userId = UserHandle.getCallingUserId();
+        @Override // Binder call
+        public ComponentName[] getDreamComponentsForUser(int userId) {
+            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, false, true, "getDreamComponents", null);
+
             final long ident = Binder.clearCallingIdentity();
             try {
-                return getDreamComponentsForUser(userId);
+                return DreamManagerService.this.getDreamComponentsForUser(userId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -526,13 +536,28 @@
         }
 
         @Override // Binder call
-        public ComponentName getDefaultDreamComponent() {
-            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+        public void setDreamComponentsForUser(int userId, ComponentName[] componentNames) {
+            checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, false, true, "setDreamComponents", null);
 
-            final int userId = UserHandle.getCallingUserId();
             final long ident = Binder.clearCallingIdentity();
             try {
-                return getDefaultDreamComponentForUser(userId);
+                DreamManagerService.this.setDreamComponentsForUser(userId, componentNames);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
+        public ComponentName getDefaultDreamComponentForUser(int userId) {
+            checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, false, true, "getDefaultDreamComponent", null);
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return DreamManagerService.this.getDefaultDreamComponentForUser(userId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -563,24 +588,25 @@
         }
 
         @Override // Binder call
-        public void testDream(ComponentName dream) {
+        public void testDream(int userId, ComponentName dream) {
             if (dream == null) {
                 throw new IllegalArgumentException("dream must not be null");
             }
             checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+            userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, false, true, "testDream", null);
 
-            final int callingUserId = UserHandle.getCallingUserId();
             final int currentUserId = ActivityManager.getCurrentUser();
-            if (callingUserId != currentUserId) {
+            if (userId != currentUserId) {
                 // This check is inherently prone to races but at least it's something.
                 Slog.w(TAG, "Aborted attempt to start a test dream while a different "
-                        + " user is active: callingUserId=" + callingUserId
+                        + " user is active: userId=" + userId
                         + ", currentUserId=" + currentUserId);
                 return;
             }
             final long ident = Binder.clearCallingIdentity();
             try {
-                testDreamInternal(dream, callingUserId);
+                testDreamInternal(dream, userId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -671,6 +697,11 @@
         public boolean isDreaming() {
             return isDreamingInternal();
         }
+
+        @Override
+        public ComponentName getActiveDreamComponent(boolean doze) {
+            return getActiveDreamComponentInternal(doze);
+        }
     }
 
     private final Runnable mSystemPropertiesChanged = new Runnable() {
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 8206fef..2672f84 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -713,7 +713,7 @@
 
     /**
      * Gets a list of all supported users (i.e., those that pass the
-     * {@link #isSupportedUser(TargetUser)}check).
+     * {@link #isUserSupported(TargetUser)}check).
      */
     @NonNull
     protected List<UserInfo> getSupportedUsers() {
@@ -722,7 +722,7 @@
         final List<UserInfo> supportedUsers = new ArrayList<>(size);
         for (int i = 0; i < size; i++) {
             final UserInfo userInfo = allUsers[i];
-            if (isSupportedUser(new TargetUser(userInfo))) {
+            if (isUserSupported(new TargetUser(userInfo))) {
                 supportedUsers.add(userInfo);
             }
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index eac2d24..f24699a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -21,6 +21,7 @@
 import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InputMethodInfo;
 
+import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
 import com.android.internal.view.InlineSuggestionsRequestInfo;
 import com.android.server.LocalServices;
@@ -51,7 +52,7 @@
     /**
      * Hides the current input method, if visible.
      */
-    public abstract void hideCurrentInputMethod();
+    public abstract void hideCurrentInputMethod(@SoftInputShowHideReason int reason);
 
     /**
      * Returns the list of installed input methods for the specified user.
@@ -106,7 +107,7 @@
                 }
 
                 @Override
-                public void hideCurrentInputMethod() {
+                public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
                 }
 
                 @Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 87262a8..e3c545c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -131,6 +131,7 @@
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodDebug;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.inputmethod.StartInputFlags;
 import com.android.internal.inputmethod.StartInputReason;
 import com.android.internal.inputmethod.UnbindReason;
@@ -767,6 +768,75 @@
     @GuardedBy("mMethodMap")
     private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
 
+    private static final class SoftInputShowHideHistory {
+        private Entry[] mEntries = new Entry[16];
+        private int mNextIndex = 0;
+        private static final AtomicInteger sSequenceNumber = new AtomicInteger(0);
+
+        // TODO(b/141738570): add requestWindowToken to track who request show / hide softInput.
+        private static final class Entry {
+            ClientState mClientState;
+            String mFocusedWindowString;
+            @SoftInputModeFlags
+            int mFocusedWindowSoftInputMode;
+            @SoftInputShowHideReason
+            int mReason;
+            boolean mRequestShowKeyboard;
+            // The timing of handling MSG_SHOW_SOFT_INPUT or MSG_HIDE_SOFT_INPUT.
+            long mTimestamp;
+            long mWallTime;
+            int mTargetDisplayId;
+
+            Entry(ClientState client, String focusedWindow, @SoftInputModeFlags int softInputMode,
+                    @SoftInputShowHideReason int reason, boolean show) {
+                mClientState = client;
+                mFocusedWindowString = focusedWindow;
+                mFocusedWindowSoftInputMode = softInputMode;
+                mReason = reason;
+                mRequestShowKeyboard = show;
+                mTimestamp = SystemClock.uptimeMillis();
+                mWallTime = System.currentTimeMillis();
+            }
+        }
+
+        void addEntry(@NonNull Entry entry) {
+            final int index = mNextIndex;
+            mEntries[index] = entry;
+            mNextIndex = (mNextIndex + 1) % mEntries.length;
+        }
+
+        void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+            final SimpleDateFormat dataFormat =
+                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
+
+            for (int i = 0; i < mEntries.length; ++i) {
+                final Entry entry = mEntries[(i + mNextIndex) % mEntries.length];
+                if (entry == null) {
+                    continue;
+                }
+                pw.print(prefix);
+                pw.println("SoftInputShowHideHistory #" + sSequenceNumber.getAndIncrement() + ":");
+
+                pw.print(prefix);
+                pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime))
+                        + " (timestamp=" + entry.mTimestamp + ")");
+
+                pw.print(prefix);
+                pw.print(" requestShowKeyboard=" + entry.mRequestShowKeyboard);
+                pw.print(" targetDisplayId=" + entry.mTargetDisplayId);
+                pw.println(" reason=" + entry.mReason);
+
+                pw.print(prefix);
+                pw.print(" requestClient=" + entry.mClientState);
+                pw.println(" focusedWindow=" + entry.mFocusedWindowString);
+
+                pw.print(prefix);
+                pw.println(" focusedWindowSoftInputMode=" + InputMethodDebug.softInputModeToString(
+                        entry.mFocusedWindowSoftInputMode));
+            }
+        }
+    }
+
     /**
      * Map of generated token to windowToken that is requesting
      * {@link InputMethodManager#showSoftInput(View, int)}.
@@ -933,6 +1003,11 @@
     @NonNull
     private final StartInputHistory mStartInputHistory = new StartInputHistory();
 
+    @GuardedBy("mMethodMap")
+    @NonNull
+    private final SoftInputShowHideHistory mSoftInputShowHideHistory =
+            new SoftInputShowHideHistory();
+
     class SettingsObserver extends ContentObserver {
         int mUserId;
         boolean mRegistered = false;
@@ -989,11 +1064,13 @@
                                     == AccessibilityService.SHOW_MODE_HIDDEN;
                     if (mAccessibilityRequestingNoSoftKeyboard) {
                         final boolean showRequested = mShowRequested;
-                        hideCurrentInputLocked(0, null);
+                        hideCurrentInputLocked(0, null,
+                                SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE);
                         mShowRequested = showRequested;
                     } else if (mShowRequested) {
-                        showCurrentInputLocked(
-                                mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT, null);
+                        showCurrentInputLocked(mCurFocusedWindow,
+                                InputMethodManager.SHOW_IMPLICIT, null,
+                                SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE);
                     }
                 } else {
                     boolean enabledChanged = false;
@@ -1618,7 +1695,7 @@
 
         // TODO: Is it really possible that switchUserLocked() happens before system ready?
         if (mSystemReady) {
-            hideCurrentInputLocked(0, null);
+            hideCurrentInputLocked(0, null, SoftInputShowHideReason.HIDE_SWITCH_USER);
             resetCurrentMethodAndClient(UnbindReason.SWITCH_USER);
             buildInputMethodListLocked(initialUserSwitch);
             if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
@@ -1882,7 +1959,7 @@
                 executeOrSendMessage(mCurMethod,
                         mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
                                 requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
-                                        imi.getPackageName())));
+                                        imi.getPackageName(), mCurTokenDisplayId)));
             } else {
                 callback.onInlineSuggestionsUnsupported();
             }
@@ -1902,11 +1979,14 @@
         @NonNull
         private final String mImePackageName;
 
+        private final int mImeDisplayId;
+
         InlineSuggestionsRequestCallbackDecorator(
                 @NonNull IInlineSuggestionsRequestCallback callback,
-                @NonNull String imePackageName) {
+                @NonNull String imePackageName, int displayId) {
             mCallback = callback;
             mImePackageName = imePackageName;
+            mImeDisplayId = displayId;
         }
 
         @Override
@@ -1923,6 +2003,7 @@
                                 + "] doesn't match the IME package name=[" + mImePackageName
                                 + "].");
             }
+            request.setHostDisplayId(mImeDisplayId);
             mCallback.onInlineSuggestionsRequest(request, callback);
         }
     }
@@ -2154,7 +2235,8 @@
                 startInputToken, session, mCurInputContext, mCurAttribute));
         if (mShowRequested) {
             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
-            showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null);
+            showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null,
+                    SoftInputShowHideReason.ATTACH_NEW_INPUT);
         }
         return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
                 session.session, (session.channel != null ? session.channel.dup() : null),
@@ -2893,7 +2975,8 @@
                     }
                 }
                 if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
-                return showCurrentInputLocked(windowToken, flags, resultReceiver);
+                return showCurrentInputLocked(windowToken, flags, resultReceiver,
+                        SoftInputShowHideReason.SHOW_SOFT_INPUT);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2901,7 +2984,8 @@
     }
 
     @GuardedBy("mMethodMap")
-    boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver) {
+    boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
         mShowRequested = true;
         if (mAccessibilityRequestingNoSoftKeyboard) {
             return false;
@@ -2924,9 +3008,9 @@
             // create a dummy token for IMS so that IMS cannot inject windows into client app.
             Binder showInputToken = new Binder();
             mShowRequestWindowMap.put(showInputToken, windowToken);
-            executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO(
-                    MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod,
-                    resultReceiver, showInputToken));
+            executeOrSendMessage(mCurMethod, mCaller.obtainMessageIIOOO(
+                    MSG_SHOW_SOFT_INPUT, getImeShowFlags(), reason, mCurMethod, resultReceiver,
+                    showInputToken));
             mInputShown = true;
             if (mHaveConnection && !mVisibleBound) {
                 bindCurrentInputMethodServiceLocked(
@@ -2984,14 +3068,16 @@
                 }
 
                 if (DEBUG) Slog.v(TAG, "Client requesting input be hidden");
-                return hideCurrentInputLocked(flags, resultReceiver);
+                return hideCurrentInputLocked(flags, resultReceiver,
+                        SoftInputShowHideReason.HIDE_SOFT_INPUT);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
     }
 
-    boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
+    boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
         if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                 && (mShowExplicitlyRequested || mShowForced)) {
             if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
@@ -3018,8 +3104,8 @@
             // delivered to the IME process as an IPC.  Hence the inconsistency between
             // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
             // the final state.
-            executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
-                    MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));
+            executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(MSG_HIDE_SOFT_INPUT,
+                    reason, mCurMethod, resultReceiver));
             res = true;
         } else {
             res = false;
@@ -3156,7 +3242,7 @@
             Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
                     + " a background user, use EditorInfo.targetInputMethodUser with"
                     + " INTERACT_ACROSS_USERS_FULL permission.");
-            hideCurrentInputLocked(0, null);
+            hideCurrentInputLocked(0, null, SoftInputShowHideReason.HIDE_INVALID_USER);
             return InputBindResult.INVALID_USER;
         }
 
@@ -3219,7 +3305,8 @@
                         // be behind any soft input window, so hide the
                         // soft input window if it is shown.
                         if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
-                        hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null);
+                        hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null,
+                                SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
 
                         // If focused display changed, we should unbind current method
                         // to make app window in previous display relayout after Ime
@@ -3245,7 +3332,8 @@
                                 attribute, startInputFlags, startInputReason);
                         didStart = true;
                     }
-                    showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null);
+                    showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+                            SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV);
                 }
                 break;
             case LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
@@ -3254,12 +3342,14 @@
             case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
                 if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
                     if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
-                    hideCurrentInputLocked(0, null);
+                    hideCurrentInputLocked(0, null,
+                            SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV);
                 }
                 break;
             case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
                 if (DEBUG) Slog.v(TAG, "Window asks to hide input");
-                hideCurrentInputLocked(0, null);
+                hideCurrentInputLocked(0, null,
+                        SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
                 break;
             case LayoutParams.SOFT_INPUT_STATE_VISIBLE:
                 if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
@@ -3271,7 +3361,8 @@
                                     attribute, startInputFlags, startInputReason);
                             didStart = true;
                         }
-                        showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null);
+                        showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+                                SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV);
                     } else {
                         Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because"
                                 + " there is no focused view that also returns true from"
@@ -3288,7 +3379,8 @@
                                 attribute, startInputFlags, startInputReason);
                         didStart = true;
                     }
-                    showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null);
+                    showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
+                            SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE);
                 } else {
                     Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because"
                             + " there is no focused view that also returns true from"
@@ -3780,7 +3872,7 @@
             }
             long ident = Binder.clearCallingIdentity();
             try {
-                hideCurrentInputLocked(flags, null);
+                hideCurrentInputLocked(flags, null, SoftInputShowHideReason.HIDE_MY_SOFT_INPUT);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3795,7 +3887,8 @@
             }
             long ident = Binder.clearCallingIdentity();
             try {
-                showCurrentInputLocked(mLastImeTargetWindow, flags, null);
+                showCurrentInputLocked(mLastImeTargetWindow, flags, null,
+                        SoftInputShowHideReason.SHOW_MY_SOFT_INPUT);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3878,10 +3971,16 @@
             case MSG_SHOW_SOFT_INPUT:
                 args = (SomeArgs)msg.obj;
                 try {
+                    final @SoftInputShowHideReason int reason = msg.arg2;
                     if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
-                            + msg.arg1 + ", " + args.arg2 + ")");
+                            + msg.arg1 + ", " + args.arg2 + ") for reason: "
+                            + InputMethodDebug.softInputDisplayReasonToString(reason));
                     ((IInputMethod) args.arg1).showSoftInput(
                             (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2);
+                    mSoftInputShowHideHistory.addEntry(
+                            new SoftInputShowHideHistory.Entry(mCurClient,
+                                    InputMethodDebug.objToString(mCurFocusedWindow),
+                                    mCurFocusedWindowSoftInputMode, reason, true /* show */));
                 } catch (RemoteException e) {
                 }
                 args.recycle();
@@ -3889,16 +3988,23 @@
             case MSG_HIDE_SOFT_INPUT:
                 args = (SomeArgs)msg.obj;
                 try {
+                    final @SoftInputShowHideReason int reason = msg.arg1;
                     if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
-                            + args.arg2 + ")");
+                            + args.arg2 + ") for reason: "
+                            + InputMethodDebug.softInputDisplayReasonToString(reason));
                     ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2);
+                    mSoftInputShowHideHistory.addEntry(
+                            new SoftInputShowHideHistory.Entry(mCurClient,
+                                    InputMethodDebug.objToString(mCurFocusedWindow),
+                                    mCurFocusedWindowSoftInputMode, reason, false /* show */));
                 } catch (RemoteException e) {
                 }
                 args.recycle();
                 return true;
             case MSG_HIDE_CURRENT_INPUT_METHOD:
                 synchronized (mMethodMap) {
-                    hideCurrentInputLocked(0, null);
+                    final @SoftInputShowHideReason int reason = (int) msg.obj;
+                    hideCurrentInputLocked(0, null, reason);
                 }
                 return true;
             case MSG_INITIALIZE_IME:
@@ -4682,9 +4788,9 @@
         }
 
         @Override
-        public void hideCurrentInputMethod() {
+        public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
             mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
-            mService.mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
+            mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
         }
 
         @Override
@@ -4841,6 +4947,9 @@
 
             p.println("  mStartInputHistory:");
             mStartInputHistory.dump(pw, "   ");
+
+            p.println("  mSoftInputShowHideHistory:");
+            mSoftInputShowHideHistory.dump(pw, "   ");
         }
 
         p.println(" ");
@@ -5300,7 +5409,8 @@
                 final String nextIme;
                 final List<InputMethodInfo> nextEnabledImes;
                 if (userId == mSettings.getCurrentUserId()) {
-                    hideCurrentInputLocked(0, null);
+                    hideCurrentInputLocked(0, null,
+                            SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
                     unbindCurrentMethodLocked();
                     // Reset the current IME
                     resetSelectedInputMethodAndSubtypeLocked(null);
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 4904061..1aff23b0 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -73,6 +73,7 @@
 import com.android.internal.inputmethod.IMultiClientInputMethod;
 import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.IMultiClientInputMethodSession;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.inputmethod.StartInputFlags;
 import com.android.internal.inputmethod.StartInputReason;
 import com.android.internal.inputmethod.UnbindReason;
@@ -174,7 +175,7 @@
                         }
 
                         @Override
-                        public void hideCurrentInputMethod() {
+                        public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
                             reportNotSupported();
                         }
 
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 63054cf..fd8e159 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -40,7 +40,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.Signature;
@@ -202,7 +201,7 @@
                                 intent,
                                 /* onFinished= */ null,
                                 /* handler= */ null);
-                    } catch (IntentSender.SendIntentException e) {
+                    } catch (Exception e) {
                         Slog.e(TAG, "Error sending status feedback.", e);
                     }
                 });
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 475f229..c9c6d51 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -380,7 +380,7 @@
     private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
     private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds
 
-    private static final long DELAY_FOR_ASSISTANT_TIME = 100;
+    private static final long DELAY_FOR_ASSISTANT_TIME = 200;
 
     private static final String ACTION_NOTIFICATION_TIMEOUT =
             NotificationManagerService.class.getSimpleName() + ".TIMEOUT";
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index ae6e058..0ad0b23 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -354,14 +354,13 @@
      * Grants access based on an interaction between a calling and target package, granting
      * visibility of the caller from the target.
      *
-     * @param callingUid the uid initiating the interaction
-     * @param targetUid  the uid being interacted with and thus gaining visibility of the
-     *                   initiating uid.
+     * @param recipientUid the uid gaining visibility of the {@code visibleUid}.
+     * @param visibleUid the uid becoming visible to the {@recipientUid}
      */
-    public void grantImplicitAccess(int callingUid, int targetUid) {
-        if (targetUid != callingUid
-                && mImplicitlyQueryable.add(targetUid, callingUid) && DEBUG_LOGGING) {
-            Slog.wtf(TAG, "implicit access granted: " + targetUid + " -> " + callingUid);
+    public void grantImplicitAccess(int recipientUid, int visibleUid) {
+        if (recipientUid != visibleUid
+                && mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) {
+            Slog.wtf(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 85810e3..e86a42c 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -28,6 +28,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.InstantAppResolveInfo;
 import android.content.pm.PackageManager;
@@ -272,6 +273,9 @@
             return null;
         }
         List<ProviderInfo> providerList = null;
+
+        // Map from a package name to the corresponding app info.
+        ArrayMap<String, ApplicationInfo> appInfos = null;
         synchronized (mLock) {
             for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
                 final ParsedProvider p = mProviders.mProviders.valueAt(i);
@@ -300,8 +304,29 @@
                         && (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) {
                     continue;
                 }
+
+                // Make sure we have AppInfo for this provider.
+                final PackageUserState state = ps.readUserState(userId);
+                ApplicationInfo appInfo =
+                        (appInfos == null) ? null : appInfos.get(pkg.getPackageName());
+                if (appInfo == null) {
+                    appInfo = PackageInfoUtils.generateApplicationInfo(
+                            pkg, flags, state, userId, ps);
+                    if (appInfo == null) {
+                        // In this case, we should avoid calling generateApplicationInfo() for
+                        // the same package in subsequent iterations, but appInfo shouldn't be null
+                        // here, so we don't bother.
+                        continue;
+                    }
+                    if (appInfos == null) {
+                        appInfos = new ArrayMap<>(4);
+                    }
+                    appInfos.put(pkg.getPackageName(), appInfo);
+                }
+                // At this point, appInfo != null.
+
                 final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
-                        pkg, p, flags, ps.readUserState(userId), userId, ps);
+                        pkg, p, flags, state, appInfo, userId, ps);
                 if (info == null) {
                     continue;
                 }
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index cf85b0f..0eaac41 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -403,7 +403,7 @@
 
     @GuardedBy("mService.mLock")
     public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
-            int instantAppId, int targetAppId) {
+            int recipientUid, int instantAppId) {
         if (mInstalledInstantAppUids == null) {
             return;     // no instant apps installed; no need to grant
         }
@@ -411,7 +411,7 @@
         if (instantAppList == null || !instantAppList.get(instantAppId)) {
             return;     // instant app id isn't installed; no need to grant
         }
-        if (instantAppList.get(targetAppId)) {
+        if (instantAppList.get(recipientUid)) {
             return;     // target app id is an instant app; no need to grant
         }
         if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
@@ -428,10 +428,10 @@
             targetAppList = new SparseArray<>();
             mInstantGrants.put(userId, targetAppList);
         }
-        SparseBooleanArray instantGrantList = targetAppList.get(targetAppId);
+        SparseBooleanArray instantGrantList = targetAppList.get(recipientUid);
         if (instantGrantList == null) {
             instantGrantList = new SparseBooleanArray();
-            targetAppList.put(targetAppId, instantGrantList);
+            targetAppList.put(recipientUid, instantGrantList);
         }
         instantGrantList.put(instantAppId, true /*granted*/);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 077fc6f..4eac79c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -10969,10 +10969,10 @@
             // to null here, only to reset them at a later point.
             Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
                     destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(),
-                    AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
-                    AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
-                    PackageInfoWithoutStateUtils.appInfoFlags(parsedPackage),
-                    PackageInfoWithoutStateUtils.appInfoPrivateFlags(parsedPackage),
+                    AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
+                    AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
+                    PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
+                    PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
                     UserManagerService.getInstance(),
                     usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
                     parsedPackage.getMimeGroups());
@@ -11164,6 +11164,8 @@
         // TODO(b/135203078): Remove, move to constructor
         pkgSetting.pkg = parsedPackage;
         pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
+        pkgSetting.pkgPrivateFlags =
+                PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
         if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
             pkgSetting.versionCode = parsedPackage.getLongVersionCode();
         }
@@ -16343,7 +16345,27 @@
                         REASON_INSTALL,
                         DexoptOptions.DEXOPT_BOOT_COMPLETE
                                 | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
-                mPackageDexOptimizer.performDexOpt(pkg, reconciledPkg.pkgSetting,
+                ScanResult result = reconciledPkg.scanResult;
+
+                // This mirrors logic from commitReconciledScanResultLocked, where the library files
+                // needed for dexopt are assigned.
+                // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous
+                //  setting needs to be passed to have a comparison, hide it behind an immutable
+                //  interface. There's no good reason to have 3 different ways to access the real
+                //  PackageSetting object, only one of which is actually correct.
+                PackageSetting realPkgSetting = result.existingSettingCopied
+                        ? result.request.pkgSetting : result.pkgSetting;
+                if (realPkgSetting == null) {
+                    realPkgSetting = reconciledPkg.pkgSetting;
+                }
+
+                // Unfortunately, the updated system app flag is only tracked on this PackageSetting
+                boolean isUpdatedSystemApp = reconciledPkg.pkgSetting.getPkgState()
+                        .isUpdatedSystemApp();
+
+                realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
+
+                mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
                         null /* instructionSets */,
                         getOrCreateCompilerPackageStats(pkg),
                         mDexManager.getPackageUseInfoOrDefault(packageName),
@@ -16954,6 +16976,7 @@
                     final boolean vendor = oldPackage.isVendor();
                     final boolean product = oldPackage.isProduct();
                     final boolean odm = oldPackage.isOdm();
+                    final boolean systemExt = oldPackage.isSystemExt();
                     final @ParseFlags int systemParseFlags = parseFlags;
                     final @ScanFlags int systemScanFlags = scanFlags
                             | SCAN_AS_SYSTEM
@@ -16961,14 +16984,14 @@
                             | (oem ? SCAN_AS_OEM : 0)
                             | (vendor ? SCAN_AS_VENDOR : 0)
                             | (product ? SCAN_AS_PRODUCT : 0)
-                            | (odm ? SCAN_AS_ODM : 0);
+                            | (odm ? SCAN_AS_ODM : 0)
+                            | (systemExt ? SCAN_AS_SYSTEM_EXT : 0);
 
                     if (DEBUG_INSTALL) {
                         Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
                                 + ", old=" + oldPackage);
                     }
                     res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
-                    ps.getPkgState().setUpdatedSystemApp(true);
                     targetParseFlags = systemParseFlags;
                     targetScanFlags = systemScanFlags;
                 } else { // non system replace
@@ -23566,22 +23589,27 @@
 
         @Override
         public void grantImplicitAccess(int userId, Intent intent,
-                int callingUid, int targetAppId) {
+                int recipientAppId, int visibleUid, boolean direct) {
             synchronized (mLock) {
-                final AndroidPackage callingPackage = getPackage(callingUid);
-                final int targetUid = UserHandle.getUid(userId, targetAppId);
-                final AndroidPackage targetPackage = getPackage(targetUid);
-                if (callingPackage == null || targetPackage == null) {
+                final AndroidPackage visiblePackage = getPackage(visibleUid);
+                final int recipientUid = UserHandle.getUid(userId, recipientAppId);
+                if (visiblePackage == null || getPackage(recipientUid) == null) {
                     return;
                 }
 
-                final boolean instantApp = isInstantAppInternal(callingPackage.getPackageName(),
-                        userId, callingUid);
+                final boolean instantApp =
+                        isInstantAppInternal(visiblePackage.getPackageName(), userId, visibleUid);
                 if (instantApp) {
+                    if (!direct) {
+                        // if the interaction that lead to this granting access to an instant app
+                        // was indirect (i.e.: URI permission grant), do not actually execute the
+                        // grant.
+                        return;
+                    }
                     mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
-                            UserHandle.getAppId(callingUid), targetAppId);
+                            recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
                 } else {
-                    mAppsFilter.grantImplicitAccess(callingUid, targetUid);
+                    mAppsFilter.grantImplicitAccess(recipientUid, visibleUid);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index af5c536..2453318 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -521,6 +521,9 @@
                 p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
                 p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups);
+        if (ret != null) {
+            ret.getPkgState().setUpdatedSystemApp(false);
+        }
         mDisabledSysPackages.remove(name);
         return ret;
     }
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index fe99229..7dd2e55 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -1246,7 +1246,7 @@
             try {
                 IStorageManager storageManager = PackageHelper.getStorageManager();
                 if (storageManager.supportsCheckpoint()) {
-                    storageManager.startCheckpoint(1);
+                    storageManager.startCheckpoint(2);
                 }
             } catch (Exception e) {
                 // Failed to get hold of StorageManager
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 23bdf5f..f5ce080 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -312,8 +312,14 @@
 
     /**
      * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+     *
+     * @deprecated use {@link #generateProviderInfo(
+     * AndroidPackage, ParsedProvider, int, PackageUserState, ApplicationInfo, int, PackageSetting)}
+     * instead and pass {@link ApplicationInfo} explicitly to avoid generating duplicate instances
+     * of it.
      */
     @Nullable
+    @Deprecated
     public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
             @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
             @Nullable PackageSetting pkgSetting) {
@@ -324,7 +330,7 @@
      * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
      */
     @Nullable
-    private static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
+    public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
             @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
             @Nullable ApplicationInfo applicationInfo, int userId,
             @Nullable PackageSetting pkgSetting) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 840c865d..f647b6a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2794,7 +2794,7 @@
             }
 
             if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
-                    !ps.isSystem() || !ps.getPkgState().isUpdatedSystemApp()) {
+                    !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
                 // This is the first that we have heard about this package, so the
                 // permissions we have now selected are fixed until explicitly
                 // changed.
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index c008d93..e27bf48 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -35,6 +35,9 @@
  *
  * It is assumed that anything inside the package was not cached or written to disk, so none of
  * these fields are either. They must be set on every boot from other state on the device.
+ *
+ * These fields are also not copied into any cloned PackageSetting, to preserve the old behavior
+ * where they would be lost implicitly by re-generating the package object.
  */
 @DataClass(genSetters = true, genConstructor = false, genBuilder = false)
 public class PackageStateUnserialized {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 19e6062..1b5cc6a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -186,6 +186,7 @@
 
 import com.android.internal.R;
 import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.os.RoSystemProperties;
@@ -482,6 +483,7 @@
     MetricsLogger mLogger;
     boolean mWakeOnDpadKeyPress;
     boolean mWakeOnAssistKeyPress;
+    boolean mWakeOnBackKeyPress;
 
     private boolean mHandleVolumeKeysInWM;
 
@@ -1105,7 +1107,8 @@
                                     LocalServices.getService(InputMethodManagerInternal.class);
                         }
                         if (mInputMethodManagerInternal != null) {
-                            mInputMethodManagerInternal.hideCurrentInputMethod();
+                            mInputMethodManagerInternal.hideCurrentInputMethod(
+                                    SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME);
                         }
                     } else {
                         shortPressPowerGoHome();
@@ -1736,6 +1739,8 @@
                 res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress);
         mWakeOnAssistKeyPress =
                 res.getBoolean(com.android.internal.R.bool.config_wakeOnAssistKeyPress);
+        mWakeOnBackKeyPress =
+                res.getBoolean(com.android.internal.R.bool.config_wakeOnBackKeyPress);
 
         // Init display burn-in protection
         boolean burnInProtectionEnabled = context.getResources().getBoolean(
@@ -3085,7 +3090,7 @@
                         event.getAction(), fallbackAction.keyCode,
                         event.getRepeatCount(), fallbackAction.metaState,
                         event.getDeviceId(), event.getScanCode(),
-                        flags, event.getSource(), event.getDisplayId(), null /* hmac */, null);
+                        flags, event.getSource(), event.getDisplayId(), null);
 
                 if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
                     fallbackEvent.recycle();
@@ -4107,6 +4112,9 @@
 
             case KeyEvent.KEYCODE_ASSIST:
                 return mWakeOnAssistKeyPress;
+
+            case KeyEvent.KEYCODE_BACK:
+                return mWakeOnBackKeyPress;
         }
 
         return true;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index 233417d..059861b 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -16,8 +16,13 @@
 package com.android.server.power.batterysaver;
 
 import android.annotation.IntDef;
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.BatterySaverPolicyConfig;
@@ -183,18 +188,15 @@
     private String mEventLogKeys;
 
     /**
-     * Whether vibration should *really* be disabled -- i.e. {@link Policy#disableVibration}
-     * is true *and* {@link #mAccessibilityEnabled} is false.
-     */
-    @GuardedBy("mLock")
-    private boolean mDisableVibrationEffective;
-
-    /**
      * Whether accessibility is currently enabled or not.
      */
     @GuardedBy("mLock")
     private boolean mAccessibilityEnabled;
 
+    /** Whether the phone is projecting in car mode or not. */
+    @GuardedBy("mLock")
+    private boolean mCarModeEnabled;
+
     /** The current default adaptive policy. */
     @GuardedBy("mLock")
     private Policy mDefaultAdaptivePolicy = DEFAULT_ADAPTIVE_POLICY;
@@ -207,6 +209,13 @@
     @GuardedBy("mLock")
     private Policy mFullPolicy = DEFAULT_FULL_POLICY;
 
+    /**
+     * The current effective policy. This is based on the current policy level's policy, with any
+     * required adjustments.
+     */
+    @GuardedBy("mLock")
+    private Policy mEffectivePolicy = OFF_POLICY;
+
     @IntDef(prefix = {"POLICY_LEVEL_"}, value = {
             POLICY_LEVEL_OFF,
             POLICY_LEVEL_ADAPTIVE,
@@ -230,6 +239,20 @@
     private final ContentResolver mContentResolver;
     private final BatterySavingStats mBatterySavingStats;
 
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED:
+                    setCarModeEnabled(true);
+                    break;
+                case UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED:
+                    setCarModeEnabled(false);
+                    break;
+            }
+        }
+    };
+
     @GuardedBy("mLock")
     private final List<BatterySaverPolicyListener> mListeners = new ArrayList<>();
 
@@ -263,16 +286,25 @@
 
         final AccessibilityManager acm = mContext.getSystemService(AccessibilityManager.class);
 
-        acm.addAccessibilityStateChangeListener((enabled) -> {
-            synchronized (mLock) {
-                mAccessibilityEnabled = enabled;
-            }
-            refreshSettings();
-        });
-        final boolean enabled = acm.isEnabled();
+        acm.addAccessibilityStateChangeListener((enabled) -> setAccessibilityEnabled(enabled));
+        final boolean accessibilityEnabled = acm.isEnabled();
         synchronized (mLock) {
-            mAccessibilityEnabled = enabled;
+            mAccessibilityEnabled = accessibilityEnabled;
         }
+
+        final IntentFilter filter = new IntentFilter(
+                UiModeManager.ACTION_ENTER_CAR_MODE_PRIORITIZED);
+        filter.addAction(UiModeManager.ACTION_EXIT_CAR_MODE_PRIORITIZED);
+        // The ENTER/EXIT_CAR_MODE_PRIORITIZED intents are sent to UserHandle.ALL, so no need to
+        // register as all users here.
+        mContext.registerReceiver(mBroadcastReceiver, filter);
+        final boolean carModeEnabled =
+                mContext.getSystemService(UiModeManager.class).getCurrentModeType()
+                        == Configuration.UI_MODE_TYPE_CAR;
+        synchronized (mLock) {
+            mCarModeEnabled = carModeEnabled;
+        }
+
         onChange(true, null);
     }
 
@@ -299,13 +331,34 @@
         PowerManager.invalidatePowerSaveModeCaches();
     }
 
+    /**
+     * Notifies listeners of a policy change on the handler thread only if the current policy level
+     * is not {@link POLICY_LEVEL_OFF}.
+     */
+    private void maybeNotifyListenersOfPolicyChange() {
+        final BatterySaverPolicyListener[] listeners;
+        synchronized (mLock) {
+            if (getPolicyLevelLocked() == POLICY_LEVEL_OFF) {
+                // Current policy is OFF, so there's no change to notify listeners of.
+                return;
+            }
+            // Don't call out to listeners with the lock held.
+            listeners = mListeners.toArray(new BatterySaverPolicyListener[mListeners.size()]);
+        }
+
+        mHandler.post(() -> {
+            for (BatterySaverPolicyListener listener : listeners) {
+                listener.onBatterySaverPolicyChanged(this);
+            }
+        });
+    }
+
     @Override
     public void onChange(boolean selfChange, Uri uri) {
         refreshSettings();
     }
 
     private void refreshSettings() {
-        final BatterySaverPolicyListener[] listeners;
         synchronized (mLock) {
             // Load the non-device-specific setting.
             final String setting = getGlobalSetting(Settings.Global.BATTERY_SAVER_CONSTANTS);
@@ -334,16 +387,9 @@
                 // Nothing of note changed.
                 return;
             }
-
-            listeners = mListeners.toArray(new BatterySaverPolicyListener[0]);
         }
 
-        // Notify the listeners.
-        mHandler.post(() -> {
-            for (BatterySaverPolicyListener listener : listeners) {
-                listener.onBatterySaverPolicyChanged(this);
-            }
-        });
+        maybeNotifyListenersOfPolicyChange();
     }
 
     @GuardedBy("mLock")
@@ -404,31 +450,63 @@
 
     @GuardedBy("mLock")
     private void updatePolicyDependenciesLocked() {
-        final Policy currPolicy = getCurrentPolicyLocked();
-        // Update the effective vibration policy.
-        mDisableVibrationEffective = currPolicy.disableVibration
-                && !mAccessibilityEnabled; // Don't disable vibration when accessibility is on.
+        final Policy rawPolicy = getCurrentRawPolicyLocked();
+
+        final int locationMode;
+        if (mCarModeEnabled
+                && rawPolicy.locationMode != PowerManager.LOCATION_MODE_NO_CHANGE
+                && rawPolicy.locationMode != PowerManager.LOCATION_MODE_FOREGROUND_ONLY) {
+            // If car projection is enabled, ensure that navigation works.
+            locationMode = PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+        } else {
+            locationMode = rawPolicy.locationMode;
+        }
+        mEffectivePolicy = new Policy(
+                rawPolicy.adjustBrightnessFactor,
+                rawPolicy.advertiseIsEnabled,
+                rawPolicy.deferFullBackup,
+                rawPolicy.deferKeyValueBackup,
+                rawPolicy.disableAnimation,
+                rawPolicy.disableAod,
+                rawPolicy.disableLaunchBoost,
+                rawPolicy.disableOptionalSensors,
+                rawPolicy.disableSoundTrigger,
+                // Don't disable vibration when accessibility is on.
+                rawPolicy.disableVibration && !mAccessibilityEnabled,
+                rawPolicy.enableAdjustBrightness,
+                rawPolicy.enableDataSaver,
+                rawPolicy.enableFirewall,
+                // Don't force night mode when car projection is enabled.
+                rawPolicy.enableNightMode && !mCarModeEnabled,
+                rawPolicy.enableQuickDoze,
+                rawPolicy.filesForInteractive,
+                rawPolicy.filesForNoninteractive,
+                rawPolicy.forceAllAppsStandby,
+                rawPolicy.forceBackgroundCheck,
+                locationMode
+        );
+
 
         final StringBuilder sb = new StringBuilder();
 
-        if (currPolicy.forceAllAppsStandby) sb.append("A");
-        if (currPolicy.forceBackgroundCheck) sb.append("B");
+        if (mEffectivePolicy.forceAllAppsStandby) sb.append("A");
+        if (mEffectivePolicy.forceBackgroundCheck) sb.append("B");
 
-        if (mDisableVibrationEffective) sb.append("v");
-        if (currPolicy.disableAnimation) sb.append("a");
-        if (currPolicy.disableSoundTrigger) sb.append("s");
-        if (currPolicy.deferFullBackup) sb.append("F");
-        if (currPolicy.deferKeyValueBackup) sb.append("K");
-        if (currPolicy.enableFirewall) sb.append("f");
-        if (currPolicy.enableDataSaver) sb.append("d");
-        if (currPolicy.enableAdjustBrightness) sb.append("b");
+        if (mEffectivePolicy.disableVibration) sb.append("v");
+        if (mEffectivePolicy.disableAnimation) sb.append("a");
+        if (mEffectivePolicy.disableSoundTrigger) sb.append("s");
+        if (mEffectivePolicy.deferFullBackup) sb.append("F");
+        if (mEffectivePolicy.deferKeyValueBackup) sb.append("K");
+        if (mEffectivePolicy.enableFirewall) sb.append("f");
+        if (mEffectivePolicy.enableDataSaver) sb.append("d");
+        if (mEffectivePolicy.enableAdjustBrightness) sb.append("b");
 
-        if (currPolicy.disableLaunchBoost) sb.append("l");
-        if (currPolicy.disableOptionalSensors) sb.append("S");
-        if (currPolicy.disableAod) sb.append("o");
-        if (currPolicy.enableQuickDoze) sb.append("q");
+        if (mEffectivePolicy.disableLaunchBoost) sb.append("l");
+        if (mEffectivePolicy.disableOptionalSensors) sb.append("S");
+        if (mEffectivePolicy.disableAod) sb.append("o");
+        if (mEffectivePolicy.enableQuickDoze) sb.append("q");
 
-        sb.append(currPolicy.locationMode);
+        sb.append(mEffectivePolicy.locationMode);
 
         mEventLogKeys = sb.toString();
     }
@@ -857,7 +935,7 @@
                     return builder.setBatterySaverEnabled(currPolicy.disableSoundTrigger)
                             .build();
                 case ServiceType.VIBRATION:
-                    return builder.setBatterySaverEnabled(mDisableVibrationEffective)
+                    return builder.setBatterySaverEnabled(currPolicy.disableVibration)
                             .build();
                 case ServiceType.FORCE_ALL_APPS_STANDBY:
                     return builder.setBatterySaverEnabled(currPolicy.forceAllAppsStandby)
@@ -933,6 +1011,10 @@
     }
 
     private Policy getCurrentPolicyLocked() {
+        return mEffectivePolicy;
+    }
+
+    private Policy getCurrentRawPolicyLocked() {
         switch (getPolicyLevelLocked()) {
             case POLICY_LEVEL_FULL:
                 return mFullPolicy;
@@ -994,11 +1076,13 @@
             pw.println("    value: " + mAdaptiveDeviceSpecificSettings);
 
             pw.println("  mAccessibilityEnabled=" + mAccessibilityEnabled);
+            pw.println("  mCarModeEnabled=" + mCarModeEnabled);
             pw.println("  mPolicyLevel=" + getPolicyLevelLocked());
 
             dumpPolicyLocked(pw, "  ", "full", mFullPolicy);
             dumpPolicyLocked(pw, "  ", "default adaptive", mDefaultAdaptivePolicy);
             dumpPolicyLocked(pw, "  ", "current adaptive", mAdaptivePolicy);
+            dumpPolicyLocked(pw, "  ", "effective", mEffectivePolicy);
         }
     }
 
@@ -1009,11 +1093,7 @@
         pw.print(indent);
         pw.println("  " + KEY_ADVERTISE_IS_ENABLED + "=" + p.advertiseIsEnabled);
         pw.print(indent);
-        pw.println("  " + KEY_VIBRATION_DISABLED + ":config=" + p.disableVibration);
-        // mDisableVibrationEffective is based on the currently selected policy
-        pw.print(indent);
-        pw.println("  " + KEY_VIBRATION_DISABLED + ":effective=" + (p.disableVibration
-                && !mAccessibilityEnabled));
+        pw.println("  " + KEY_VIBRATION_DISABLED + "=" + p.disableVibration);
         pw.print(indent);
         pw.println("  " + KEY_ANIMATION_DISABLED + "=" + p.disableAnimation);
         pw.print(indent);
@@ -1070,10 +1150,24 @@
     }
 
     @VisibleForTesting
-    public void setAccessibilityEnabledForTest(boolean enabled) {
+    void setAccessibilityEnabled(boolean enabled) {
         synchronized (mLock) {
-            mAccessibilityEnabled = enabled;
-            updatePolicyDependenciesLocked();
+            if (mAccessibilityEnabled != enabled) {
+                mAccessibilityEnabled = enabled;
+                updatePolicyDependenciesLocked();
+                maybeNotifyListenersOfPolicyChange();
+            }
+        }
+    }
+
+    @VisibleForTesting
+    void setCarModeEnabled(boolean enabled) {
+        synchronized (mLock) {
+            if (mCarModeEnabled != enabled) {
+                mCarModeEnabled = enabled;
+                updatePolicyDependenciesLocked();
+                maybeNotifyListenersOfPolicyChange();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 8d090f1..801d75b 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -218,11 +218,7 @@
                         packageInstaller.getSessionInfo(sessionId);
                 if (sessionInfo.isStagedSessionReady() && markStagedSessionHandled(rollbackId)) {
                     mContext.unregisterReceiver(listener);
-                    if (logPackage != null) {
-                        // We save the rollback id so that after reboot, we can log if rollback was
-                        // successful or not. If logPackage is null, then there is nothing to log.
-                        saveStagedRollbackId(rollbackId);
-                    }
+                    saveStagedRollbackId(rollbackId);
                     WatchdogRollbackLogger.logEvent(logPackage,
                             FrameworkStatsLog
                             .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED,
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 1be6f22..659de00 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -159,6 +159,12 @@
             return;
         }
 
+        // If no logging packages are found, use a null package to ensure the rollback status
+        // is still logged.
+        if (oldLoggingPackages.isEmpty()) {
+            oldLoggingPackages.add(null);
+        }
+
         for (VersionedPackage oldLoggingPackage : oldLoggingPackages) {
             if (sessionInfo.isStagedSessionApplied()) {
                 logEvent(oldLoggingPackage,
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index aed2d9b..3f8f6bf 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -188,6 +188,7 @@
     private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
     private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
     private static final int OP_FLAGS_PULLED = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
+    private static final String COMMON_PERMISSION_PREFIX = "android.permission.";
 
     private final Object mNetworkStatsLock = new Object();
     @GuardedBy("mNetworkStatsLock")
@@ -395,6 +396,8 @@
                     case FrameworkStatsLog.BATTERY_VOLTAGE:
                     case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
                         return pullHealthHal(atomTag, data);
+                    case FrameworkStatsLog.APP_FEATURES_OPS:
+                        return pullAppFeaturesOps(atomTag, data);
                     default:
                         throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
                 }
@@ -550,6 +553,7 @@
         registerAppsOnExternalStorageInfo();
         registerFaceSettings();
         registerAppOps();
+        registerAppFeaturesOps();
         registerRuntimeAppOpAccessMessage();
         registerNotificationRemoteViews();
         registerDangerousPermissionState();
@@ -2624,6 +2628,10 @@
                             continue;
                         }
 
+                        if (permName.startsWith(COMMON_PERMISSION_PREFIX)) {
+                            permName = permName.substring(COMMON_PERMISSION_PREFIX.length());
+                        }
+
                         StatsEvent.Builder e = StatsEvent.newBuilder();
                         e.setAtomId(atomTag);
                         e.writeString(permName);
@@ -2843,7 +2851,6 @@
                 BackgroundThread.getExecutor(),
                 mStatsCallbackImpl
         );
-
     }
 
     private void registerRuntimeAppOpAccessMessage() {
@@ -2854,7 +2861,6 @@
                 BackgroundThread.getExecutor(),
                 mStatsCallbackImpl
         );
-
     }
 
     int pullAppOps(int atomTag, List<StatsEvent> pulledData) {
@@ -2917,6 +2923,84 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    private void registerAppFeaturesOps() {
+        int tagId = FrameworkStatsLog.APP_FEATURES_OPS;
+        mStatsManager.registerPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mStatsCallbackImpl
+        );
+    }
+
+    int pullAppFeaturesOps(int atomTag, List<StatsEvent> pulledData) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+
+            CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
+            HistoricalOpsRequest histOpsRequest =
+                    new HistoricalOpsRequest.Builder(0, Long.MAX_VALUE).setFlags(
+                            OP_FLAGS_PULLED).build();
+            appOps.getHistoricalOps(histOpsRequest, mContext.getMainExecutor(), ops::complete);
+
+            HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+                    TimeUnit.MILLISECONDS);
+
+            for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
+                final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
+                final int uid = uidOps.getUid();
+                for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
+                    final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
+                    for (int featureIdx = 0; featureIdx < packageOps.getFeatureCount();
+                            featureIdx++) {
+                        final AppOpsManager.HistoricalFeatureOps featureOps =
+                                packageOps.getFeatureOpsAt(featureIdx);
+                        for (int opIdx = 0; opIdx < featureOps.getOpCount(); opIdx++) {
+                            final AppOpsManager.HistoricalOp op = featureOps.getOpAt(opIdx);
+                            StatsEvent.Builder e = StatsEvent.newBuilder();
+                            e.setAtomId(atomTag);
+                            e.writeInt(uid);
+                            e.writeString(packageOps.getPackageName());
+                            e.writeString(featureOps.getFeatureId());
+                            e.writeString(op.getOpName());
+                            e.writeLong(op.getForegroundAccessCount(OP_FLAGS_PULLED));
+                            e.writeLong(op.getBackgroundAccessCount(OP_FLAGS_PULLED));
+                            e.writeLong(op.getForegroundRejectCount(OP_FLAGS_PULLED));
+                            e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED));
+                            e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED));
+                            e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED));
+
+                            String perm = AppOpsManager.opToPermission(op.getOpCode());
+                            if (perm == null) {
+                                e.writeBoolean(false);
+                            } else {
+                                PermissionInfo permInfo;
+                                try {
+                                    permInfo = mContext.getPackageManager().getPermissionInfo(perm,
+                                            0);
+                                    e.writeBoolean(
+                                            permInfo.getProtection() == PROTECTION_DANGEROUS);
+                                } catch (PackageManager.NameNotFoundException exception) {
+                                    e.writeBoolean(false);
+                                }
+                            }
+                            pulledData.add(e.build());
+                        }
+
+                    }
+                }
+            }
+        } catch (Throwable t) {
+            // TODO: catch exceptions at a more granular level
+            Slog.e(TAG, "Could not read appops", t);
+            return StatsManager.PULL_SKIP;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
     int pullRuntimeAppOpAccessMessage(int atomTag, List<StatsEvent> pulledData) {
         final long token = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index bad2b78..4eff954f 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -13,9 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.server.tv.tunerresourcemanager;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
   * A client profile object used by the Tuner Resource Manager to record the registered clients'
   * information.
@@ -23,12 +25,14 @@
   * @hide
   */
 public final class ClientProfile {
+
     public static final int INVALID_GROUP_ID = -1;
+
     /**
      * Client id sent to the client when registering with
      * {@link #registerClientProfile(ResourceClientProfile, TunerResourceManagerCallback, int[])}
      */
-    private final int mClientId;
+    private final int mId;
 
     /**
      * see {@link ResourceClientProfile}
@@ -41,7 +45,7 @@
     private final int mUseCase;
 
     /**
-     * Process id queried from {@link TvInputManager#}
+     * Process id queried from {@link TvInputManager#getPid(String)}.
      */
     private final int mProcessId;
 
@@ -59,6 +63,11 @@
     private int mNiceValue;
 
     /**
+     * List of the frontend ids that are used by the current client.
+     */
+    private List<Integer> mUsingFrontendIds = new ArrayList<>();
+
+    /**
      * Optional arbitrary priority value given by the client.
      *
      * <p>This value can override the default priorotiy calculated from
@@ -66,18 +75,15 @@
      */
     private int mPriority;
 
-    private ClientProfile(ClientProfileBuilder builder) {
-        this.mClientId = builder.mClientId;
+    private ClientProfile(Builder builder) {
+        this.mId = builder.mId;
         this.mTvInputSessionId = builder.mTvInputSessionId;
         this.mUseCase = builder.mUseCase;
         this.mProcessId = builder.mProcessId;
-        this.mGroupId = builder.mGroupId;
-        this.mNiceValue = builder.mNiceValue;
-        this.mPriority = builder.mPriority;
     }
 
-    public int getClientId() {
-        return mClientId;
+    public int getId() {
+        return mId;
     }
 
     public String getTvInputSessionId() {
@@ -116,26 +122,47 @@
         mNiceValue = niceValue;
     }
 
+    /**
+     * Set when the client starts to use a frontend.
+     *
+     * @param frontendId being used.
+     */
+    public void useFrontend(int frontendId) {
+        mUsingFrontendIds.add(frontendId);
+    }
+
+    public List<Integer> getInUseFrontendIds() {
+        return mUsingFrontendIds;
+    }
+
+    /**
+     * Called when the client released a frontend.
+     *
+     * <p>This could happen when client resource reclaimed.
+     *
+     * @param frontendId being released.
+     */
+    public void releaseFrontend(int frontendId) {
+        mUsingFrontendIds.remove(frontendId);
+    }
+
     @Override
     public String toString() {
-        return "ClientProfile: " + this.mClientId + ", " + this.mTvInputSessionId + ", "
-                + this.mUseCase + ", " + this.mProcessId;
+        return "ClientProfile[id=" + this.mId + ", tvInputSessionId=" + this.mTvInputSessionId
+                + ", useCase=" + this.mUseCase + ", processId=" + this.mProcessId + "]";
     }
 
     /**
     * Builder class for {@link ClientProfile}.
     */
-    public static class ClientProfileBuilder {
-        private final int mClientId;
+    public static class Builder {
+        private final int mId;
         private String mTvInputSessionId;
         private int mUseCase;
         private int mProcessId;
-        private int mGroupId;
-        private int mNiceValue;
-        private int mPriority;
 
-        ClientProfileBuilder(int clientId) {
-            this.mClientId = clientId;
+        Builder(int id) {
+            this.mId = id;
         }
 
         /**
@@ -143,7 +170,7 @@
           *
           * @param useCase the useCase of the client.
           */
-        public ClientProfileBuilder useCase(int useCase) {
+        public Builder useCase(int useCase) {
             this.mUseCase = useCase;
             return this;
         }
@@ -153,7 +180,7 @@
           *
           * @param tvInputSessionId the id of the tv input session.
           */
-        public ClientProfileBuilder tvInputSessionId(String tvInputSessionId) {
+        public Builder tvInputSessionId(String tvInputSessionId) {
             this.mTvInputSessionId = tvInputSessionId;
             return this;
         }
@@ -163,42 +190,11 @@
           *
           * @param processId the id of process.
           */
-        public ClientProfileBuilder processId(int processId) {
+        public Builder processId(int processId) {
             this.mProcessId = processId;
             return this;
         }
 
-
-        /**
-          * Builder for {@link ClientProfile}.
-          *
-          * @param groupId the id of the group that shares the same resource.
-          */
-        public ClientProfileBuilder groupId(int groupId) {
-            this.mGroupId = groupId;
-            return this;
-        }
-
-        /**
-          * Builder for {@link ClientProfile}.
-          *
-          * @param niceValue the nice value of the client.
-          */
-        public ClientProfileBuilder niceValue(int niceValue) {
-            this.mNiceValue = niceValue;
-            return this;
-        }
-
-        /**
-          * Builder for {@link ClientProfile}.
-          *
-          * @param priority the priority value of the client.
-          */
-        public ClientProfileBuilder priority(int priority) {
-            this.mPriority = priority;
-            return this;
-        }
-
         /**
           * Build a {@link ClientProfile}.
           *
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
new file mode 100644
index 0000000..a109265
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tv.tunerresourcemanager;
+
+import android.annotation.Nullable;
+import android.media.tv.tuner.frontend.FrontendSettings.Type;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A frontend resource object used by the Tuner Resource Manager to record the tuner frontend
+ * information.
+ *
+ * @hide
+ */
+public final class FrontendResource {
+    public static final int INVALID_OWNER_ID = -1;
+
+    /**
+     * Id of the current frontend. Should not be changed and should be aligned with the driver level
+     * implementation.
+     */
+    private final int mId;
+
+    /**
+     * see {@link android.media.tv.tuner.frontend.FrontendSettings.Type}
+     */
+    @Type private final int mType;
+
+    /**
+     * The exclusive group id of the FE. FEs under the same id can't be used at the same time.
+     */
+    private final int mExclusiveGroupId;
+
+    /**
+     * An array to save all the FE ids under the same exclisive group.
+     */
+    private List<Integer> mExclusiveGroupMemberFeIds = new ArrayList<>();
+
+    /**
+     * If the current resource is in use. Once resources under the same exclusive group id is in use
+     * all other resources in the same group would be considered in use.
+     */
+    private boolean mIsInUse;
+
+    /**
+     * The owner client's id if this resource is occupied. Owner of the resource under the same
+     * exclusive group id would be considered as the whole group's owner.
+     */
+    private int mOwnerClientId = INVALID_OWNER_ID;
+
+    private FrontendResource(Builder builder) {
+        this.mId = builder.mId;
+        this.mType = builder.mType;
+        this.mExclusiveGroupId = builder.mExclusiveGroupId;
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    public int getExclusiveGroupId() {
+        return mExclusiveGroupId;
+    }
+
+    public List<Integer> getExclusiveGroupMemberFeIds() {
+        return mExclusiveGroupMemberFeIds;
+    }
+
+    /**
+     * Add one id into the exclusive group member id list.
+     *
+     * @param id the id to be added.
+     */
+    public void addExclusiveGroupMemberFeId(int id) {
+        mExclusiveGroupMemberFeIds.add(id);
+    }
+
+    /**
+     * Add one id list to the exclusive group member id list.
+     *
+     * @param ids the id list to be added.
+     */
+    public void addExclusiveGroupMemberFeId(List<Integer> ids) {
+        mExclusiveGroupMemberFeIds.addAll(ids);
+    }
+
+    /**
+     * Remove one id from the exclusive group member id list.
+     *
+     * @param id the id to be removed.
+     */
+    public void removeExclusiveGroupMemberFeId(int id) {
+        mExclusiveGroupMemberFeIds.remove(new Integer(id));
+    }
+
+    public boolean isInUse() {
+        return mIsInUse;
+    }
+
+    public int getOwnerClientId() {
+        return mOwnerClientId;
+    }
+
+    /**
+     * Set an owner client on the resource.
+     *
+     * @param ownerClientId the id of the owner client.
+     */
+    public void setOwner(int ownerClientId) {
+        mIsInUse = true;
+        mOwnerClientId = ownerClientId;
+    }
+
+    /**
+     * Remove an owner client from the resource.
+     */
+    public void removeOwner() {
+        mIsInUse = false;
+        mOwnerClientId = INVALID_OWNER_ID;
+    }
+
+    @Override
+    public String toString() {
+        return "FrontendResource[id=" + this.mId + ", type=" + this.mType
+                + ", exclusiveGId=" + this.mExclusiveGroupId + ", exclusiveGMemeberIds="
+                + Arrays.toString(this.mExclusiveGroupMemberFeIds.toArray())
+                + ", isInUse=" + this.mIsInUse + ", ownerClientId=" + this.mOwnerClientId + "]";
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o instanceof FrontendResource) {
+            FrontendResource fe = (FrontendResource) o;
+            return mId == fe.getId() && mType == fe.getType()
+                    && mExclusiveGroupId == fe.getExclusiveGroupId()
+                    && mExclusiveGroupMemberFeIds.equals(fe.getExclusiveGroupMemberFeIds())
+                    && mIsInUse == fe.isInUse() && mOwnerClientId == fe.getOwnerClientId();
+        }
+        return false;
+    }
+
+    /**
+     * Builder class for {@link FrontendResource}.
+     */
+    public static class Builder {
+        private final int mId;
+        @Type private int mType;
+        private int mExclusiveGroupId;
+
+        Builder(int id) {
+            this.mId = id;
+        }
+
+        /**
+         * Builder for {@link FrontendResource}.
+         *
+         * @param type the type of the frontend. See {@link Type}
+         */
+        public Builder type(@Type int type) {
+            this.mType = type;
+            return this;
+        }
+
+        /**
+         * Builder for {@link FrontendResource}.
+         *
+         * @param exclusiveGroupId the id of exclusive group.
+         */
+        public Builder exclusiveGroupId(int exclusiveGroupId) {
+            this.mExclusiveGroupId = exclusiveGroupId;
+            return this;
+        }
+
+        /**
+         * Build a {@link FrontendResource}.
+         *
+         * @return {@link FrontendResource}.
+         */
+        public FrontendResource build() {
+            FrontendResource frontendResource = new FrontendResource(this);
+            return frontendResource;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 49a7045..cb31a50 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -28,32 +28,48 @@
 import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
 import android.media.tv.tunerresourcemanager.TunerLnbRequest;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
 import java.util.List;
 
 /**
-  * This class provides a system service that manages the TV tuner resources.
-  *
-  * @hide
-  */
+ * This class provides a system service that manages the TV tuner resources.
+ *
+ * @hide
+ */
 public class TunerResourceManagerService extends SystemService {
     private static final String TAG = "TunerResourceManagerService";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private SparseArray<ClientProfile> mClientProfiles = new SparseArray<>();
-    private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>();
-    private int mNextUnusedFrontendId = 0;
-    private List<Integer> mReleasedClientId = new ArrayList<Integer>();
+    public static final int INVALID_CLIENT_ID = -1;
+    private static final int MAX_CLIENT_PRIORITY = 1000;
+
+    // Array of the registered client profiles
+    @VisibleForTesting private SparseArray<ClientProfile> mClientProfiles = new SparseArray<>();
+    private int mNextUnusedClientId = 0;
+    private List<Integer> mRegisteredClientIds = new ArrayList<Integer>();
+
+    // Array of the current available frontend resources
+    @VisibleForTesting
+    private SparseArray<FrontendResource> mFrontendResources = new SparseArray<>();
+    // Array of the current available frontend ids
     private List<Integer> mAvailableFrontendIds = new ArrayList<Integer>();
 
+    private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>();
+
     private TvInputManager mManager;
+    private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints();
+
+    // Used to synchronize the access to the service.
+    private final Object mLock = new Object();
 
     public TunerResourceManagerService(@Nullable Context context) {
         super(context);
@@ -61,97 +77,78 @@
 
     @Override
     public void onStart() {
-        publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService());
-        mManager = (TvInputManager) getContext()
-                .getSystemService(Context.TV_INPUT_SERVICE);
+        onStart(false /*isForTesting*/);
+    }
+
+    @VisibleForTesting
+    protected void onStart(boolean isForTesting) {
+        if (!isForTesting) {
+            publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService());
+        }
+        mManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+        mPriorityCongfig.parse();
     }
 
     private final class BinderService extends ITunerResourceManager.Stub {
         @Override
         public void registerClientProfile(@NonNull ResourceClientProfile profile,
-                            @NonNull IResourcesReclaimListener listener,
-                            @NonNull int[] clientId) {
-            if (DEBUG) {
-                Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")");
+                @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)
+                throws RemoteException {
+            enforceAccessPermission();
+            if (profile == null) {
+                throw new RemoteException("ResourceClientProfile can't be null");
             }
 
-            // TODO tell if the client already exists
-            if (mReleasedClientId.isEmpty()) {
-                clientId[0] = mNextUnusedFrontendId++;
-            } else {
-                clientId[0] = mReleasedClientId.get(0);
-                mReleasedClientId.remove(0);
+            if (clientId == null) {
+                throw new RemoteException("clientId can't be null!");
             }
 
-            if (mManager == null) {
-                Slog.e(TAG, "TvInputManager is null. Can't register client profile.");
-                return;
+            if (!mPriorityCongfig.isDefinedUseCase(profile.getUseCase())) {
+                throw new RemoteException("Use undefined client use case:" + profile.getUseCase());
             }
 
-            int callingPid = mManager.getClientPid(profile.getTvInputSessionId());
-
-            ClientProfile clientProfile = new ClientProfile.ClientProfileBuilder(
-                    clientId[0])
-                    .tvInputSessionId(profile.getTvInputSessionId())
-                    .useCase(profile.getUseCase())
-                    .processId(callingPid)
-                    .build();
-            mClientProfiles.append(clientId[0], clientProfile);
-            mListeners.append(clientId[0], listener);
+            synchronized (mLock) {
+                registerClientProfileInternal(profile, listener, clientId);
+            }
         }
 
         @Override
-        public void unregisterClientProfile(int clientId) {
-            if (DEBUG) {
-                Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
+        public void unregisterClientProfile(int clientId) throws RemoteException {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                if (!checkClientExists(clientId)) {
+                    Slog.e(TAG, "Unregistering non exists client:" + clientId);
+                    return;
+                }
+                unregisterClientProfileInternal(clientId);
             }
-
-            mClientProfiles.remove(clientId);
-            mListeners.remove(clientId);
-            mReleasedClientId.add(clientId);
         }
 
         @Override
         public boolean updateClientPriority(int clientId, int priority, int niceValue) {
-            if (DEBUG) {
-                Slog.d(TAG, "updateClientPriority(clientId=" + clientId
-                        + ", priority=" + priority + ", niceValue=" + niceValue + ")");
+            enforceAccessPermission();
+            synchronized (mLock) {
+                return updateClientPriorityInternal(clientId, priority, niceValue);
             }
-
-            ClientProfile profile = mClientProfiles.get(clientId);
-            if (profile == null) {
-                Slog.e(TAG, "Can not find client profile with id " + clientId
-                        + " when trying to update the client priority.");
-                return false;
-            }
-
-            profile.setPriority(priority);
-            profile.setNiceValue(niceValue);
-
-            return true;
         }
 
         @Override
-        public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos)
-                throws RemoteException {
-            if (infos == null || infos.length == 0) {
-                Slog.d(TAG, "Can't update with empty frontend info");
-                return;
+        public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException {
+            enforceAccessPermission();
+            if (infos == null) {
+                throw new RemoteException("TunerFrontendInfo can't be null");
             }
-
-            if (DEBUG) {
-                Slog.d(TAG, "updateFrontendInfo:");
-                for (int i = 0; i < infos.length; i++) {
-                    Slog.d(TAG, infos[i].toString());
-                }
+            synchronized (mLock) {
+                setFrontendInfoListInternal(infos);
             }
         }
 
         @Override
         public void updateCasInfo(int casSystemId, int maxSessionNum) {
             if (DEBUG) {
-                Slog.d(TAG, "updateCasInfo(casSystemId="
-                        + casSystemId + ", maxSessionNum=" + maxSessionNum + ")");
+                Slog.d(TAG,
+                        "updateCasInfo(casSystemId=" + casSystemId
+                                + ", maxSessionNum=" + maxSessionNum + ")");
             }
         }
 
@@ -166,49 +163,30 @@
 
         @Override
         public boolean requestFrontend(@NonNull TunerFrontendRequest request,
-                                       @NonNull int[] frontendId) throws RemoteException {
-            if (DEBUG) {
-                Slog.d(TAG, "requestFrontend(request=" + request + ")");
+                @NonNull int[] frontendId) throws RemoteException {
+            enforceAccessPermission();
+            if (frontendId == null) {
+                throw new RemoteException("frontendId can't be null");
             }
-
-            frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
-
-            if (getContext() == null) {
-                Slog.e(TAG, "Can not find context when requesting frontend");
-                return false;
+            synchronized (mLock) {
+                try {
+                    return requestFrontendInternal(request, frontendId);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
             }
-
-            if (mClientProfiles.get(request.getClientId()) == null) {
-                Slog.e(TAG, "Request from unregistered client. Id: "
-                        + request.getClientId());
-                return false;
-            }
-
-            String sessionId = mClientProfiles.get(request.getClientId())
-                    .getTvInputSessionId();
-
-            if (DEBUG) {
-                Slog.d(TAG, "session Id:" + sessionId + ")");
-            }
-
-            if (DEBUG) {
-                Slog.d(TAG, "No available Frontend found.");
-            }
-
-            return false;
         }
 
         @Override
         public void shareFrontend(int selfClientId, int targetClientId) {
             if (DEBUG) {
-                Slog.d(TAG, "shareFrontend from "
-                        + selfClientId + " with " + targetClientId);
+                Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId);
             }
         }
 
         @Override
-        public boolean requestCasSession(@NonNull CasSessionRequest request,
-                    @NonNull int[] sessionResourceId) {
+        public boolean requestCasSession(
+                @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) {
             if (DEBUG) {
                 Slog.d(TAG, "requestCasSession(request=" + request + ")");
             }
@@ -246,13 +224,284 @@
         }
 
         @Override
-        public boolean isHigherPriority(ResourceClientProfile challengerProfile,
-                ResourceClientProfile holderProfile) {
+        public boolean isHigherPriority(
+                ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) {
             if (DEBUG) {
-                Slog.d(TAG, "isHigherPriority(challengerProfile=" + challengerProfile
-                        + ", holderProfile=" + challengerProfile + ")");
+                Slog.d(TAG,
+                        "isHigherPriority(challengerProfile=" + challengerProfile
+                                + ", holderProfile=" + challengerProfile + ")");
             }
             return true;
         }
     }
+
+    @VisibleForTesting
+    protected void registerClientProfileInternal(ResourceClientProfile profile,
+            IResourcesReclaimListener listener, int[] clientId) {
+        if (DEBUG) {
+            Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")");
+        }
+
+        clientId[0] = INVALID_CLIENT_ID;
+        if (mManager == null) {
+            Slog.e(TAG, "TvInputManager is null. Can't register client profile.");
+            return;
+        }
+        // TODO tell if the client already exists
+        clientId[0] = mNextUnusedClientId++;
+
+        int pid = profile.getTvInputSessionId() == null
+                ? Binder.getCallingPid() /*callingPid*/
+                : mManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/
+
+        ClientProfile clientProfile = new ClientProfile.Builder(clientId[0])
+                                              .tvInputSessionId(profile.getTvInputSessionId())
+                                              .useCase(profile.getUseCase())
+                                              .processId(pid)
+                                              .build();
+        clientProfile.setPriority(getClientPriority(profile.getUseCase(), pid));
+
+        mClientProfiles.append(clientId[0], clientProfile);
+        mListeners.append(clientId[0], listener);
+        mRegisteredClientIds.add(clientId[0]);
+    }
+
+    @VisibleForTesting
+    protected void unregisterClientProfileInternal(int clientId) {
+        if (DEBUG) {
+            Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
+        }
+        for (int id : getClientProfile(clientId).getInUseFrontendIds()) {
+            getFrontendResource(id).removeOwner();
+            for (int groupMemberId : getFrontendResource(id).getExclusiveGroupMemberFeIds()) {
+                getFrontendResource(groupMemberId).removeOwner();
+            }
+        }
+        mClientProfiles.remove(clientId);
+        mListeners.remove(clientId);
+        mRegisteredClientIds.remove(clientId);
+    }
+
+    @VisibleForTesting
+    protected boolean updateClientPriorityInternal(int clientId, int priority, int niceValue) {
+        if (DEBUG) {
+            Slog.d(TAG,
+                    "updateClientPriority(clientId=" + clientId + ", priority=" + priority
+                            + ", niceValue=" + niceValue + ")");
+        }
+
+        ClientProfile profile = getClientProfile(clientId);
+        if (profile == null) {
+            Slog.e(TAG,
+                    "Can not find client profile with id " + clientId
+                            + " when trying to update the client priority.");
+            return false;
+        }
+
+        profile.setPriority(priority);
+        profile.setNiceValue(niceValue);
+
+        return true;
+    }
+
+    @VisibleForTesting
+    protected void setFrontendInfoListInternal(TunerFrontendInfo[] infos) {
+        if (DEBUG) {
+            Slog.d(TAG, "updateFrontendInfo:");
+            for (int i = 0; i < infos.length; i++) {
+                Slog.d(TAG, infos[i].toString());
+            }
+        }
+
+        // An arrayList to record the frontends pending on updating. Ids will be removed
+        // from this list once its updating finished. Any frontend left in this list when all
+        // the updates are done will be removed from mAvailableFrontendIds and
+        // mFrontendResources.
+        List<Integer> updatingFrontendIds = new ArrayList<>(mAvailableFrontendIds);
+
+        // Update frontendResources sparse array and other mappings accordingly
+        for (int i = 0; i < infos.length; i++) {
+            if (getFrontendResource(infos[i].getId()) != null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Frontend id=" + infos[i].getId() + "exists.");
+                }
+                updatingFrontendIds.remove(new Integer(infos[i].getId()));
+            } else {
+                // Add a new fe resource
+                FrontendResource newFe = new FrontendResource.Builder(infos[i].getId())
+                                                 .type(infos[i].getFrontendType())
+                                                 .exclusiveGroupId(infos[i].getExclusiveGroupId())
+                                                 .build();
+                // Update the exclusive group member list in all the existing Frontend resource
+                for (Integer feId : mAvailableFrontendIds) {
+                    FrontendResource fe = getFrontendResource(feId.intValue());
+                    if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) {
+                        newFe.addExclusiveGroupMemberFeId(fe.getId());
+                        newFe.addExclusiveGroupMemberFeId(fe.getExclusiveGroupMemberFeIds());
+                        for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
+                            getFrontendResource(excGroupmemberFeId.intValue())
+                                    .addExclusiveGroupMemberFeId(newFe.getId());
+                        }
+                        fe.addExclusiveGroupMemberFeId(newFe.getId());
+                        break;
+                    }
+                }
+                // Update resource list and available id list
+                mFrontendResources.append(newFe.getId(), newFe);
+                mAvailableFrontendIds.add(newFe.getId());
+            }
+        }
+
+        // TODO check if the removing resource is in use or not. Handle the conflict.
+        for (Integer removingId : updatingFrontendIds) {
+            // update the exclusive group id memver list
+            FrontendResource fe = getFrontendResource(removingId.intValue());
+            fe.removeExclusiveGroupMemberFeId(new Integer(fe.getId()));
+            for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
+                getFrontendResource(excGroupmemberFeId.intValue())
+                        .removeExclusiveGroupMemberFeId(new Integer(fe.getId()));
+            }
+            mFrontendResources.remove(removingId.intValue());
+            mAvailableFrontendIds.remove(removingId);
+        }
+    }
+
+    @VisibleForTesting
+    protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendId)
+            throws RemoteException {
+        if (DEBUG) {
+            Slog.d(TAG, "requestFrontend(request=" + request + ")");
+        }
+
+        frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
+        if (!checkClientExists(request.getClientId())) {
+            Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId());
+            return false;
+        }
+        ClientProfile requestClient = getClientProfile(request.getClientId());
+        int grantingFrontendId = -1;
+        int inUseLowestPriorityFrId = -1;
+        // Priority max value is 1000
+        int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+        for (int id : mAvailableFrontendIds) {
+            FrontendResource fr = getFrontendResource(id);
+            if (fr.getType() == request.getFrontendType()) {
+                if (!fr.isInUse()) {
+                    // Grant unused frontend with no exclusive group members first.
+                    if (fr.getExclusiveGroupMemberFeIds().size() == 0) {
+                        grantingFrontendId = id;
+                        break;
+                    } else if (grantingFrontendId < 0) {
+                        // Grant the unused frontend with lower id first if all the unused
+                        // frontends have exclusive group members.
+                        grantingFrontendId = id;
+                    }
+                } else if (grantingFrontendId < 0) {
+                    // Record the frontend id with the lowest client priority among all the
+                    // in use frontends when no available frontend has been found.
+                    int priority = getOwnerClientPriority(id);
+                    if (currentLowestPriority > priority) {
+                        inUseLowestPriorityFrId = id;
+                        currentLowestPriority = priority;
+                    }
+                }
+            }
+        }
+
+        // Grant frontend when there is unused resource.
+        if (grantingFrontendId > -1) {
+            frontendId[0] = grantingFrontendId;
+            updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+            return true;
+        }
+
+        // When all the resources are occupied, grant the lowest priority resource if the
+        // request client has higher priority.
+        if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
+            frontendId[0] = inUseLowestPriorityFrId;
+            reclaimFrontendResource(getFrontendResource(frontendId[0]).getOwnerClientId());
+            updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+            return true;
+        }
+
+        return false;
+    }
+
+    @VisibleForTesting
+    protected int getClientPriority(int useCase, int pid) {
+        if (DEBUG) {
+            Slog.d(TAG, "getClientPriority useCase=" + useCase
+                    + ", pid=" + pid + ")");
+        }
+
+        if (isForeground(pid)) {
+            return mPriorityCongfig.getForegroundPriority(useCase);
+        }
+        return mPriorityCongfig.getBackgroundPriority(useCase);
+    }
+
+    @VisibleForTesting
+    protected boolean isForeground(int pid) {
+        // TODO: how to get fg/bg information from pid
+        return true;
+    }
+
+    @VisibleForTesting
+    protected void reclaimFrontendResource(int reclaimingId) throws RemoteException {
+        if (mListeners.get(reclaimingId) != null) {
+            try {
+                mListeners.get(reclaimingId).onReclaimResources();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    private void updateFrontendClientMappingOnNewGrant(int grantingId, int ownerClientId) {
+        FrontendResource grantingFrontend = getFrontendResource(grantingId);
+        ClientProfile ownerProfile = getClientProfile(ownerClientId);
+        grantingFrontend.setOwner(ownerClientId);
+        ownerProfile.useFrontend(grantingId);
+        for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeIds()) {
+            getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId);
+            ownerProfile.useFrontend(exclusiveGroupMember);
+        }
+    }
+
+    /**
+     * Get the owner client's priority from the frontend id.
+     *
+     * @param frontendId an in use frontend id.
+     * @return the priority of the owner client of the frontend.
+     */
+    private int getOwnerClientPriority(int frontendId) {
+        return getClientProfile(getFrontendResource(frontendId).getOwnerClientId()).getPriority();
+    }
+
+    private ClientProfile getClientProfile(int clientId) {
+        return mClientProfiles.get(clientId);
+    }
+
+    protected FrontendResource getFrontendResource(int frontendId) {
+        return mFrontendResources.get(frontendId);
+    }
+
+    @VisibleForTesting
+    protected SparseArray<ClientProfile> getClientProfiles() {
+        return mClientProfiles;
+    }
+
+    @VisibleForTesting
+    protected SparseArray<FrontendResource> getFrontendResources() {
+        return mFrontendResources;
+    }
+
+    private boolean checkClientExists(int clientId) {
+        return mRegisteredClientIds.contains(clientId);
+    }
+
+    private void enforceAccessPermission() {
+        getContext().enforceCallingOrSelfPermission(
+                "android.permission.TUNER_RESOURCE_ACCESS", TAG);
+    }
 }
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
new file mode 100644
index 0000000..8c2de47
--- /dev/null
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tv.tunerresourcemanager;
+
+import android.media.tv.TvInputService;
+import android.media.tv.TvInputService.PriorityHintUseCaseType;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the Tuner Resource Manager use case priority hints config info including a
+ * parser that can read the xml config from the vendors.
+ *
+ * @hide
+ */
+public class UseCasePriorityHints {
+    private static final String TAG = "UseCasePriorityHints";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final String PATH_TO_VENDOR_CONFIG_XML =
+            "/vendor/etc/tunerResourceManagerUseCaseConfig.xml";
+    private static final int INVALID_PRIORITY_VALUE = -1;
+    private static final int INVALID_USE_CASE = -1;
+
+    /**
+     * Array of the configured use case priority hints. Key is the use case id. Value is a size 2
+     * int array. The first element carries the priority of the use case on foreground. The second
+     * shows the background priority.
+     */
+    SparseArray<int[]> mPriorityHints = new SparseArray<>();
+
+    List<Integer> mVendorDefinedUseCase = new ArrayList<>();
+
+    private int mDefaultForeground = 150;
+    private int mDefaultBackground = 50;
+
+    int getForegroundPriority(int useCase) {
+        if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) {
+            return mPriorityHints.get(useCase)[0];
+        }
+        return mDefaultForeground;
+    }
+
+    int getBackgroundPriority(int useCase) {
+        if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) {
+            return mPriorityHints.get(useCase)[1];
+        }
+        return mDefaultBackground;
+    }
+
+    boolean isDefinedUseCase(int useCase) {
+        return (mVendorDefinedUseCase.contains(useCase) || isPredefinedUseCase(useCase));
+    }
+
+    /**
+     * To parse the vendor use case config.
+     */
+    public void parse() {
+        // Override the default priority with vendor setting if available.
+        File file = new File(PATH_TO_VENDOR_CONFIG_XML);
+        if (file.exists()) {
+            try {
+                InputStream in = new FileInputStream(file);
+                parseInternal(in);
+                return;
+            } catch (IOException e) {
+                Slog.e(TAG, "Error reading vendor file: " + file, e);
+            } catch (XmlPullParserException e) {
+                Slog.e(TAG, "Unable to parse vendor file: " + file, e);
+            }
+        } else {
+            if (DEBUG) {
+                Slog.i(TAG, "no vendor priority configuration available. Using default priority");
+            }
+            addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND, 180, 100);
+            addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN, 450, 200);
+            addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 480, 300);
+            addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 490, 400);
+            addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 600, 500);
+        }
+    }
+
+    // We don't use namespaces
+    private static final String NS = null;
+
+    @VisibleForTesting
+    protected void parseInternal(InputStream in)
+            throws IOException, XmlPullParserException {
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+            parser.setInput(in, null);
+            parser.nextTag();
+            readUseCase(parser);
+            in.close();
+        } catch (IOException | XmlPullParserException e) {
+            throw e;
+        }
+        for (int i = 0; i < mPriorityHints.size(); i++) {
+            int useCase = mPriorityHints.keyAt(i);
+            int[] priorities = mPriorityHints.get(useCase);
+            if (DEBUG) {
+                Slog.d(TAG, "{defaultFg=" + mDefaultForeground
+                        + ", defaultBg=" + mDefaultBackground + "}");
+                Slog.d(TAG, "{useCase=" + useCase
+                        + ", fg=" + priorities[0]
+                        + ", bg=" + priorities[1]
+                        + "}");
+            }
+        }
+    }
+
+    private void readUseCase(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        parser.require(XmlPullParser.START_TAG, NS, "config");
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                continue;
+            }
+            String name = parser.getName();
+            int useCase;
+            if (name.equals("useCaseDefault")) {
+                mDefaultForeground = readAttributeToInt("fgPriority", parser);
+                mDefaultBackground = readAttributeToInt("bgPriority", parser);
+                parser.nextTag();
+                parser.require(XmlPullParser.END_TAG, NS, name);
+            } else if (name.equals("useCasePreDefined")) {
+                useCase = formatTypeToNum("type", parser);
+                if (useCase == INVALID_USE_CASE) {
+                    Slog.e(TAG, "Wrong predefined use case name given in the vendor config.");
+                    continue;
+                }
+                addNewUseCasePriority(useCase,
+                        readAttributeToInt("fgPriority", parser),
+                        readAttributeToInt("bgPriority", parser));
+                parser.nextTag();
+                parser.require(XmlPullParser.END_TAG, NS, name);
+            } else if (name.equals("useCaseVendor")) {
+                useCase = readAttributeToInt("id", parser);
+                addNewUseCasePriority(useCase,
+                        readAttributeToInt("fgPriority", parser),
+                        readAttributeToInt("bgPriority", parser));
+                mVendorDefinedUseCase.add(useCase);
+                parser.nextTag();
+                parser.require(XmlPullParser.END_TAG, NS, name);
+            } else {
+                skip(parser);
+            }
+        }
+    }
+
+    private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+        if (parser.getEventType() != XmlPullParser.START_TAG) {
+            throw new IllegalStateException();
+        }
+        int depth = 1;
+        while (depth != 0) {
+            switch (parser.next()) {
+                case XmlPullParser.END_TAG:
+                    depth--;
+                    break;
+                case XmlPullParser.START_TAG:
+                    depth++;
+                    break;
+            }
+        }
+    }
+
+    private int readAttributeToInt(String attributeName, XmlPullParser parser) {
+        return Integer.valueOf(parser.getAttributeValue(null, attributeName));
+    }
+
+    private void addNewUseCasePriority(int useCase, int fgPriority, int bgPriority) {
+        int[] priorities = {fgPriority, bgPriority};
+        mPriorityHints.append(useCase, priorities);
+    }
+
+    @PriorityHintUseCaseType
+    private static int formatTypeToNum(String attributeName, XmlPullParser parser) {
+        String useCaseName = parser.getAttributeValue(null, attributeName);
+        switch (useCaseName) {
+            case "USE_CASE_BACKGROUND":
+                return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND;
+            case "USE_CASE_SCAN":
+                return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN;
+            case "USE_CASE_PLAYBACK":
+                return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK;
+            case "USE_CASE_LIVE":
+                return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE;
+            case "USE_CASE_RECORD":
+                return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD;
+            default:
+                return INVALID_USE_CASE;
+        }
+    }
+
+    private static boolean isPredefinedUseCase(int useCase) {
+        switch (useCase) {
+            case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND:
+            case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN:
+            case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK:
+            case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE:
+            case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD:
+                return true;
+            default:
+                return false;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index e3b7c0a..fe34e86 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -739,6 +739,8 @@
         final UriPermission perm = findOrCreateUriPermission(
                 pi.packageName, targetPkg, targetUid, grantUri);
         perm.grantModes(modeFlags, owner);
+        getPmInternal().grantImplicitAccess(UserHandle.getUserId(targetUid), null,
+                UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/);
     }
 
     /** Like grantUriPermissionUnchecked, but takes an Intent. */
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 8130546..9bbeb72 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -211,8 +211,9 @@
                     PackageManagerInternal.class);
             final int webviewUid = pmInternal.getPackageUidInternal(
                     webViewPackageName, 0, UserHandle.getUserId(callingUid));
-            pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null, webviewUid,
-                    UserHandle.getAppId(callingUid));
+            pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null,
+                    UserHandle.getAppId(callingUid), webviewUid,
+                    true /*direct*/);
         }
 
         /**
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 190af7a..d715ed4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3845,13 +3845,6 @@
                     mDisplayContent.setLayoutNeeded();
                 }
                 mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
-
-                // Notify the pinned stack upon all windows drawn. If there was an animation in
-                // progress then this signal will resume that animation.
-                final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
-                if (pinnedStack != null) {
-                    pinnedStack.onAllWindowsDrawn();
-                }
             }
         }
     }
@@ -6755,18 +6748,8 @@
         // for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
         final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
         if (pinnedStack == null) return;
-        final Rect stackBounds;
-        if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) {
-            // We are animating the bounds, use the pre-animation bounds to save the snap
-            // fraction
-            stackBounds = pinnedStack.mPreAnimationBounds;
-        } else {
-            // We skip the animation if the fullscreen configuration is not compatible, so
-            // use the current bounds to calculate the saved snap fraction instead
-            // (see PinnedActivityStack.skipResizeAnimation())
-            stackBounds = mTmpRect;
-            pinnedStack.getBounds(stackBounds);
-        }
+        final Rect stackBounds = mTmpRect;
+        pinnedStack.getBounds(stackBounds);
         mDisplayContent.mPinnedStackControllerLocked.saveReentryBounds(
                 mActivityComponent, stackBounds);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2f1cc05..ff890ff 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -98,10 +98,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
-import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.TaskProto.ACTIVITIES;
 import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
@@ -202,7 +198,7 @@
 /**
  * State and management of a single stack of activities.
  */
-class ActivityStack extends Task implements BoundsAnimationTarget {
+class ActivityStack extends Task {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
     static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -327,11 +323,8 @@
     // Set when an animation has been requested but has not yet started from the UI thread. This is
     // cleared when the animation actually starts.
     private boolean mBoundsAnimatingRequested = false;
-    private boolean mBoundsAnimatingToFullscreen = false;
-    private boolean mCancelCurrentBoundsAnimation = false;
     private Rect mBoundsAnimationTarget = new Rect();
     private Rect mBoundsAnimationSourceHintBounds = new Rect();
-    private @BoundsAnimationController.AnimationType int mAnimationType;
 
     Rect mPreAnimationBounds = new Rect();
 
@@ -790,17 +783,6 @@
         setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
                 false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
                 false /* creating */);
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            // This stack will be visible before SystemUI requests PiP animation to start, and if
-            // the selected animation type is fade in, this stack will be close first. If the
-            // animation does not start early, user may see this full screen window appear again
-            // after the closing transition finish, which could cause screen to flicker.
-            // Check if animation should start here in advance.
-            final BoundsAnimationController controller = getDisplay().mBoundsAnimationController;
-            if (controller.isAnimationTypeFadeIn()) {
-                setPinnedStackAlpha(0f);
-            }
-        }
     }
 
     /**
@@ -905,6 +887,9 @@
             likelyResolvedMode = parent != null ? parent.getWindowingMode()
                     : WINDOWING_MODE_FULLSCREEN;
         }
+        if (currentMode == WINDOWING_MODE_PINNED) {
+            mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
+        }
         if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
                 && topActivity != null && !topActivity.noDisplay
                 && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) {
@@ -3480,91 +3465,6 @@
         }
     }
 
-    void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration,
-            boolean fromFullscreen) {
-        if (!inPinnedWindowingMode()) return;
-
-        /**
-         * TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation.
-         * If this PIP Task is controlled by a TaskOrganizer, the animation occurs entirely
-         * on the TaskOrganizer side, so we just hand over the leash without doing any animation.
-         * We have to be careful to not schedule the enter-pip callback as the TaskOrganizer
-         * needs to have flexibility to schedule that at an appropriate point in the animation.
-         */
-        if (isControlledByTaskOrganizer()) return;
-        if (toBounds == null /* toFullscreen */) {
-            final Configuration parentConfig = getParent().getConfiguration();
-            final ActivityRecord top = topRunningNonOverlayTaskActivity();
-            if (top != null && !top.isConfigurationCompatible(parentConfig)) {
-                // The final orientation of this activity will change after moving to full screen.
-                // Start freezing screen here to prevent showing a temporary full screen window.
-                top.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
-                dismissPip();
-                return;
-            }
-        }
-
-        // Get the from-bounds
-        final Rect fromBounds = new Rect();
-        getBounds(fromBounds);
-
-        // Get non-null fullscreen to-bounds for animating if the bounds are null
-        @BoundsAnimationController.SchedulePipModeChangedState int schedulePipModeChangedState =
-                NO_PIP_MODE_CHANGED_CALLBACKS;
-        final boolean toFullscreen = toBounds == null;
-        if (toFullscreen) {
-            if (fromFullscreen) {
-                throw new IllegalArgumentException("Should not defer scheduling PiP mode"
-                        + " change on animation to fullscreen.");
-            }
-            schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
-
-            mWmService.getStackBounds(
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds);
-            if (!mTmpToBounds.isEmpty()) {
-                // If there is a fullscreen bounds, use that
-                toBounds = new Rect(mTmpToBounds);
-            } else {
-                // Otherwise, use the display bounds
-                toBounds = new Rect();
-                getDisplayContent().getBounds(toBounds);
-            }
-        } else if (fromFullscreen) {
-            schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-        }
-
-        setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
-
-        final Rect finalToBounds = toBounds;
-        final @BoundsAnimationController.SchedulePipModeChangedState int
-                finalSchedulePipModeChangedState = schedulePipModeChangedState;
-        final DisplayContent displayContent = getDisplayContent();
-        @BoundsAnimationController.AnimationType int intendedAnimationType =
-                displayContent.mBoundsAnimationController.getAnimationType();
-        if (intendedAnimationType == FADE_IN) {
-            if (fromFullscreen) {
-                setPinnedStackAlpha(0f);
-            }
-            if (toBounds.width() == fromBounds.width()
-                    && toBounds.height() == fromBounds.height()) {
-                intendedAnimationType = BoundsAnimationController.BOUNDS;
-            } else if (!fromFullscreen && !toBounds.equals(fromBounds)) {
-                // intendedAnimationType may have been reset at the end of RecentsAnimation,
-                // force it to BOUNDS type if we know for certain we're animating to
-                // a different bounds, especially for expand and collapse of PiP window.
-                intendedAnimationType = BoundsAnimationController.BOUNDS;
-            }
-        }
-
-        final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType;
-        mCancelCurrentBoundsAnimation = false;
-        displayContent.mBoundsAnimationController.getHandler().post(() -> {
-            displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
-                    finalToBounds, animationDuration, finalSchedulePipModeChangedState,
-                    fromFullscreen, toFullscreen, animationType);
-        });
-    }
-
     void dismissPip() {
         if (!isActivityTypeStandardOrUndefined()) {
             throw new IllegalArgumentException(
@@ -3593,21 +3493,6 @@
         });
     }
 
-    private void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds,
-            boolean forceUpdate) {
-        // It is guaranteed that the activities requiring the update will be in the pinned stack at
-        // this point (either reparented before the animation into PiP, or before reparenting after
-        // the animation out of PiP)
-        if (!isAttached()) {
-            return;
-        }
-        final PooledConsumer c = PooledLambda.obtainConsumer(
-                ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor,
-                PooledLambda.__(Task.class), targetStackBounds, forceUpdate);
-        forAllLeafTasks(c, true /* traverseTopToBottom */);
-        c.recycle();
-    }
-
     void prepareFreezingTaskBounds() {
         forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */);
     }
@@ -3704,34 +3589,6 @@
     }
 
     /**
-     * Sets the bounds animation target bounds ahead of an animation.  This can't currently be done
-     * in onAnimationStart() since that is started on the UiThread.
-     */
-    private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds,
-            boolean toFullscreen) {
-        if (mAnimationType == BoundsAnimationController.BOUNDS) {
-            mBoundsAnimatingRequested = true;
-        }
-        mBoundsAnimatingToFullscreen = toFullscreen;
-        if (destBounds != null) {
-            mBoundsAnimationTarget.set(destBounds);
-        } else {
-            mBoundsAnimationTarget.setEmpty();
-        }
-        if (sourceHintBounds != null) {
-            mBoundsAnimationSourceHintBounds.set(sourceHintBounds);
-        } else if (!mBoundsAnimating) {
-            // If the bounds are already animating, we don't want to reset the source hint. This is
-            // because the source hint is sent when starting the animation from the client that
-            // requested to enter pip. Other requests can adjust the pip bounds during an animation,
-            // but could accidentally reset the source hint bounds.
-            mBoundsAnimationSourceHintBounds.setEmpty();
-        }
-
-        mPreAnimationBounds.set(getRawBounds());
-    }
-
-    /**
      * @return the final bounds for the bounds animation.
      */
     void getFinalAnimationBounds(Rect outBounds) {
@@ -3763,30 +3620,6 @@
     }
 
     /**
-     * Reset the current animation running on {@link #mBoundsAnimationTarget}.
-     *
-     * @param destinationBounds the final destination bounds
-     */
-    void resetCurrentBoundsAnimation(Rect destinationBounds) {
-        boolean animating = (mBoundsAnimatingRequested || mBoundsAnimating)
-                && !mBoundsAnimationTarget.isEmpty();
-
-        // The final boundary is updated while there is an existing boundary animation. Let's
-        // cancel this animation to prevent the obsolete animation overwritten updated bounds.
-        if (animating && !destinationBounds.equals(mBoundsAnimationTarget)) {
-            final BoundsAnimationController controller =
-                    getDisplayContent().mBoundsAnimationController;
-            controller.getHandler().post(() -> controller.cancel(this));
-        }
-        // Once we've set the bounds based on the rotation of the old bounds in the new
-        // orientation, clear the animation target bounds since they are obsolete, and
-        // cancel any currently running animations
-        mBoundsAnimationTarget.setEmpty();
-        mBoundsAnimationSourceHintBounds.setEmpty();
-        mCancelCurrentBoundsAnimation = true;
-    }
-
-    /**
      * Updates the passed-in {@code inOutBounds} based on the current state of the
      * docked controller. This gets run *after* the override configuration is updated, so it's
      * safe to rely on the controller's state in here (though eventually this dependence should
@@ -4590,110 +4423,6 @@
         return task != null;
     }
 
-    public boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds) {
-        // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mWmService.mGlobalLock) {
-            if (mCancelCurrentBoundsAnimation) {
-                return false;
-            }
-            mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
-        }
-
-        return true;
-    }
-
-    void onAllWindowsDrawn() {
-        if (!mBoundsAnimating && !mBoundsAnimatingRequested) {
-            return;
-        }
-
-        getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn();
-    }
-
-    @Override  // AnimatesBounds
-    public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
-            @BoundsAnimationController.AnimationType int animationType) {
-        // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mWmService.mGlobalLock) {
-            if (!isAttached()) {
-                // Don't run the animation if the stack is already detached
-                return false;
-            }
-
-            if (animationType == BoundsAnimationController.BOUNDS) {
-                mBoundsAnimatingRequested = false;
-                mBoundsAnimating = true;
-            }
-            mAnimationType = animationType;
-
-            // If we are changing UI mode, as in the PiP to fullscreen
-            // transition, then we need to wait for the window to draw.
-            if (schedulePipModeChangedCallback) {
-                forAllWindows((w) -> {
-                    w.mWinAnimator.resetDrawState();
-                }, false /* traverseTopToBottom */);
-            }
-        }
-
-        if (inPinnedWindowingMode()) {
-            try {
-                mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
-            } catch (RemoteException e) {
-                // I don't believe you...
-            }
-
-            if ((schedulePipModeChangedCallback || animationType == FADE_IN)) {
-                // We need to schedule the PiP mode change before the animation up. It is possible
-                // in this case for the animation down to not have been completed, so always
-                // force-schedule and update to the client to ensure that it is notified that it
-                // is no longer in picture-in-picture mode
-                updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate);
-            }
-        }
-        return true;
-    }
-
-    @Override  // AnimatesBounds
-    public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
-            boolean moveToFullscreen) {
-        synchronized (mWmService.mGlobalLock) {
-            if (inPinnedWindowingMode()) {
-                // Update to the final bounds if requested. This is done here instead of in the
-                // bounds animator to allow us to coordinate this after we notify the PiP mode
-                // changed
-
-                if (schedulePipModeChangedCallback) {
-                    // We need to schedule the PiP mode change after the animation down, so use the
-                    // final bounds
-                    updatePictureInPictureModeForPinnedStackAnimation(mBoundsAnimationTarget,
-                            false /* forceUpdate */);
-                }
-
-                if (mAnimationType == BoundsAnimationController.FADE_IN) {
-                    setPinnedStackAlpha(1f);
-                    mWmService.mAtmService.notifyPinnedStackAnimationEnded();
-                    return;
-                }
-
-                if (finalStackSize != null && !mCancelCurrentBoundsAnimation) {
-                    setPinnedStackSize(finalStackSize, null);
-                } else {
-                    // We have been canceled, so the final stack size is null, still run the
-                    // animation-end logic
-                    onPipAnimationEndResize();
-                }
-
-                mWmService.mAtmService.notifyPinnedStackAnimationEnded();
-                if (moveToFullscreen) {
-                    ((ActivityStack) this).dismissPip();
-                }
-            } else {
-                // No PiP animation, just run the normal animation-end logic
-                onPipAnimationEndResize();
-            }
-        }
-    }
-
     /**
      * Sets the current picture-in-picture aspect ratio.
      */
@@ -4741,42 +4470,6 @@
         getDisplayContent().getPinnedStackController().setActions(actions);
     }
 
-    /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */
-    void onPipAnimationEndResize() {
-        mBoundsAnimating = false;
-        forAllLeafTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */);
-        mWmService.requestTraversal();
-    }
-
-    @Override
-    public boolean shouldDeferStartOnMoveToFullscreen() {
-        synchronized (mWmService.mGlobalLock) {
-            if (!isAttached()) {
-                // Unnecessary to pause the animation because the stack is detached.
-                return false;
-            }
-
-            // Workaround for the recents animation -- normally we need to wait for the new activity
-            // to show before starting the PiP animation, but because we start and show the home
-            // activity early for the recents animation prior to the PiP animation starting, there
-            // is no subsequent all-drawn signal. In this case, we can skip the pause when the home
-            // stack is already visible and drawn.
-            final ActivityStack homeStack = mDisplayContent.getRootHomeTask();
-            if (homeStack == null) {
-                return true;
-            }
-            final Task homeTask = homeStack.getTopMostTask();
-            if (homeTask == null) {
-                return true;
-            }
-            final ActivityRecord homeApp = homeTask.getTopVisibleActivity();
-            if (!homeTask.isVisible() || homeApp == null) {
-                return true;
-            }
-            return !homeApp.allDrawn;
-        }
-    }
-
     /**
      * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen
      *         bounds and we have a deferred PiP mode changed callback set with the animation.
@@ -4798,25 +4491,6 @@
         return mBoundsAnimating;
     }
 
-    public boolean isAnimatingBounds() {
-        return mBoundsAnimating;
-    }
-
-    public boolean lastAnimatingBoundsWasToFullscreen() {
-        return mBoundsAnimatingToFullscreen;
-    }
-
-    public boolean isAnimatingBoundsToFullscreen() {
-        return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen();
-    }
-
-    public boolean pinnedStackResizeDisallowed() {
-        if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
-            return true;
-        }
-        return false;
-    }
-
     /** Returns true if a removal action is still being deferred. */
     boolean checkCompleteDeferredRemoval() {
         if (isAnimating(TRANSITION | CHILDREN)) {
@@ -4843,21 +4517,6 @@
                 || activityType == ACTIVITY_TYPE_ASSISTANT;
     }
 
-    @Override
-    public boolean setPinnedStackAlpha(float alpha) {
-        // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mWmService.mGlobalLock) {
-            final SurfaceControl sc = getSurfaceControl();
-            if (sc == null || !sc.isValid()) {
-                // If the stack is already removed, don't bother updating any stack animation
-                return false;
-            }
-            getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha);
-            scheduleAnimation();
-            return !mCancelCurrentBoundsAnimation;
-        }
-    }
-
     public DisplayInfo getDisplayInfo() {
         return mDisplayContent.getDisplayInfo();
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 97b6388..b2d2f62 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1696,40 +1696,6 @@
         }
     }
 
-    void resizePinnedStack(Rect displayedBounds, Rect inConfigBounds) {
-        // TODO(multi-display): The display containing the stack should be passed in.
-        final ActivityStack stack =
-                mRootWindowContainer.getDefaultDisplay().getRootPinnedTask();
-        if (stack == null) {
-            Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
-            return;
-        }
-
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
-        mService.deferWindowLayout();
-        try {
-            Rect configBounds = null;
-            if (inConfigBounds != null) {
-                // Use 0,0 as the position for the inset rect because we are headed for fullscreen.
-                configBounds = tempRect;
-                configBounds.top = 0;
-                configBounds.left = 0;
-                configBounds.right = inConfigBounds.width();
-                configBounds.bottom = inConfigBounds.height();
-            }
-            if (displayedBounds != null && inConfigBounds == null) {
-                // We have finished the animation into PiP, and are resizing the tasks to match the
-                // stack bounds, while layouts are deferred, update any task state as a part of
-                // transitioning it from fullscreen into a floating state.
-                stack.onPipAnimationEndResize();
-            }
-            stack.resize(displayedBounds, configBounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
-        } finally {
-            mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
     private void removeStackInSurfaceTransaction(ActivityStack stack) {
         if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
             /**
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 600a125..f52c7f2 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -97,7 +97,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -336,6 +335,7 @@
         int filterCallingUid;
         PendingIntentRecord originatingPendingIntent;
         boolean allowBackgroundActivityStart;
+        boolean isDream;
 
         /**
          * If set to {@code true}, allows this activity start to look into
@@ -387,6 +387,7 @@
             filterCallingUid = UserHandle.USER_NULL;
             originatingPendingIntent = null;
             allowBackgroundActivityStart = false;
+            isDream = false;
         }
 
         /**
@@ -427,6 +428,7 @@
             filterCallingUid = request.filterCallingUid;
             originatingPendingIntent = request.originatingPendingIntent;
             allowBackgroundActivityStart = request.allowBackgroundActivityStart;
+            isDream = request.isDream;
         }
 
         /**
@@ -970,7 +972,7 @@
                 restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
                         callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
                         request.originatingPendingIntent, request.allowBackgroundActivityStart,
-                        intent);
+                        request.isDream, intent);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             }
@@ -1180,13 +1182,18 @@
     boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid,
             final String callingPackage, int realCallingUid, int realCallingPid,
             WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent,
-            boolean allowBackgroundActivityStart, Intent intent) {
+            boolean allowBackgroundActivityStart, boolean isDream, Intent intent) {
         // don't abort for the most important UIDs
         final int callingAppId = UserHandle.getAppId(callingUid);
         if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
                 || callingAppId == Process.NFC_UID) {
             return false;
         }
+
+        // don't abort if this is the dream activity
+        if (isDream) {
+            return false;
+        }
         // don't abort if the callingUid has a visible window or is a persistent system process
         final int callingUidProcState = mService.getUidState(callingUid);
         final boolean callingUidHasAnyVisibleWindow =
@@ -1558,9 +1565,8 @@
                 mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
         mService.getPackageManagerInternalLocked().grantImplicitAccess(
                 mStartActivity.mUserId, mIntent,
-                mCallingUid,
-                UserHandle.getAppId(mStartActivity.info.applicationInfo.uid)
-        );
+                UserHandle.getAppId(mStartActivity.info.applicationInfo.uid), mCallingUid,
+                true /*direct*/);
         if (newTask) {
             EventLogTags.writeWmCreateTask(mStartActivity.mUserId,
                     mStartActivity.getTask().mTaskId);
@@ -2401,7 +2407,6 @@
                 mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                 mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
         addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
-        updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
 
         if (DEBUG_TASKS) {
             Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
@@ -2424,21 +2429,6 @@
         mIntentDelivered = true;
     }
 
-    @VisibleForTesting
-    void updateBounds(Task task, Rect bounds) {
-        if (bounds.isEmpty()) {
-            return;
-        }
-
-        final Task rootTask = task.getRootTask();
-        if (rootTask != null && rootTask.inPinnedWindowingMode()) {
-            mService.animateResizePinnedStack(rootTask.mTaskId, bounds, -1);
-        } else {
-            // TODO: I don't believe it is possible to reach this else condition anymore...
-            task.setBounds(bounds);
-        }
-    }
-
     private void addOrReparentStartingActivity(Task parent, String reason) {
         if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
             parent.addChild(mStartActivity);
@@ -2686,6 +2676,11 @@
         return this;
     }
 
+    ActivityStarter setIsDream(boolean isDream) {
+        mRequest.isDream = isDream;
+        return this;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         prefix = prefix + "  ";
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5392257..693a5e4 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -212,6 +212,8 @@
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
+import android.service.dreams.DreamActivity;
+import android.service.dreams.DreamManagerInternal;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.sysprop.DisplayProperties;
@@ -1231,6 +1233,52 @@
     }
 
     @Override
+    public boolean startDreamActivity(Intent intent) {
+        final WindowProcessController process = mProcessMap.getProcess(Binder.getCallingPid());
+        final long origId = Binder.clearCallingIdentity();
+
+        // The dream activity is only called for non-doze dreams.
+        final ComponentName currentDream = LocalServices.getService(DreamManagerInternal.class)
+                .getActiveDreamComponent(/* doze= */ false);
+
+        if (currentDream == null || currentDream.getPackageName() == null
+                || !currentDream.getPackageName().equals(process.mInfo.packageName)) {
+            Slog.e(TAG, "Calling package is not the current dream package. "
+                    + "Aborting startDreamActivity...");
+            return false;
+        }
+
+        final ActivityInfo a = new ActivityInfo();
+        a.theme = com.android.internal.R.style.Theme_Dream;
+        a.exported = true;
+        a.name = DreamActivity.class.getName();
+
+
+        a.packageName = process.mInfo.packageName;
+        a.applicationInfo = process.mInfo;
+        a.processName = process.mInfo.processName;
+        a.uiOptions = process.mInfo.uiOptions;
+        a.taskAffinity = "android:" + a.packageName + "/dream";
+        a.enabled = true;
+        a.launchMode = ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+
+        a.persistableMode = ActivityInfo.PERSIST_NEVER;
+        a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+        a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+
+        try {
+            getActivityStartController().obtainStarter(intent, "dream")
+                    .setActivityInfo(a)
+                    .setIsDream(true)
+                    .execute();
+            return true;
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
     public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
             String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
             String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
@@ -2403,7 +2451,7 @@
         final ActivityStarter starter = getActivityStartController().obtainStarter(
                 null /* intent */, "moveTaskToFront");
         if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1,
-                -1, callerApp, null, false, null)) {
+                -1, callerApp, null, false, false, null)) {
             if (!isBackgroundActivityStartsEnabled()) {
                 return;
             }
@@ -2645,64 +2693,6 @@
         }
     }
 
-    @Override
-    public void animateResizePinnedStack(int stackId, Rect destBounds, int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "animateResizePinnedStack()");
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityStack stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
-                    return;
-                }
-                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                    throw new IllegalArgumentException("Stack: " + stackId
-                        + " doesn't support animated resize.");
-                }
-                stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */,
-                        animationDuration, false /* fromFullscreen */);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
-    public void offsetPinnedStackBounds(int stackId, Rect compareBounds, int xOffset, int yOffset,
-            int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "offsetPinnedStackBounds()");
-
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                if (xOffset == 0 && yOffset == 0) {
-                    return;
-                }
-                final ActivityStack stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "offsetPinnedStackBounds: stackId " + stackId + " not found.");
-                    return;
-                }
-                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                    throw new IllegalArgumentException("Stack: " + stackId
-                            + " doesn't support animated resize.");
-                }
-                final Rect destBounds = new Rect();
-                stack.getAnimationOrCurrentBounds(destBounds);
-                if (destBounds.isEmpty() || !destBounds.equals(compareBounds)) {
-                    Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete.");
-                    return;
-                }
-                destBounds.offset(xOffset, yOffset);
-                stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */,
-                        animationDuration, false /* fromFullscreen */);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
     /**
      * Moves the specified task to the primary-split-screen stack.
      *
@@ -4016,17 +4006,7 @@
                     throw new IllegalArgumentException("Stack: " + stack
                             + " doesn't support animated resize.");
                 }
-                /**
-                 * TODO(b/146594635): Remove all PIP animation code from WM
-                 * once SysUI handles animation. Don't even try to animate TaskOrganized tasks.
-                 */
-                if (animate && !stack.isControlledByTaskOrganizer()) {
-                    stack.animateResizePinnedStack(null /* destBounds */,
-                            null /* sourceHintBounds */, animationDuration,
-                            false /* fromFullscreen */);
-                } else {
-                    stack.dismissPip();
-                }
+                stack.dismissPip();
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -4121,15 +4101,10 @@
     }
 
     private boolean isInPictureInPictureMode(ActivityRecord r) {
-        if (r == null || r.getRootTask() == null || !r.inPinnedWindowingMode()
-                || r.getRootTask().isInStackLocked(r) == null) {
-            return false;
-        }
-
-        // If we are animating to fullscreen then we have already dispatched the PIP mode
-        // changed, so we should reflect that check here as well.
-        final ActivityStack taskStack = r.getRootTask();
-        return !taskStack.isAnimatingBoundsToFullscreen();
+        return r != null
+                && r.getRootTask() != null
+                && r.inPinnedWindowingMode()
+                && r.getRootTask().isInStackLocked(r) != null;
     }
 
     @Override
@@ -4213,11 +4188,9 @@
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
                     // be used the next time the activity enters PiP
                     final ActivityStack stack = r.getRootTask();
-                    if (!stack.isAnimatingBoundsToFullscreen()) {
-                        stack.setPictureInPictureAspectRatio(
-                                r.pictureInPictureArgs.getAspectRatio());
-                        stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
-                    }
+                    stack.setPictureInPictureAspectRatio(
+                            r.pictureInPictureArgs.getAspectRatio());
+                    stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
                 }
                 logPictureInPictureArgs(params);
             }
@@ -4421,31 +4394,6 @@
                 .supportsLocalVoiceInteraction();
     }
 
-    /** Notifies all listeners when the pinned stack animation starts. */
-    @Override
-    public void notifyPinnedStackAnimationStarted() {
-        mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
-    }
-
-    /** Notifies all listeners when the pinned stack animation ends. */
-    @Override
-    public void notifyPinnedStackAnimationEnded() {
-        mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
-    }
-
-    @Override
-    public void resizePinnedStack(Rect displayedBounds, Rect configBounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     @Override
     public boolean updateConfiguration(Configuration values) {
         mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 8fa8119..4cce212 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -111,7 +111,7 @@
                 final ActivityStarter starter = mService.getActivityStartController().obtainStarter(
                         null /* intent */, "moveToFront");
                 if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid,
-                        callingPackage, -1, -1, callerApp, null, false, null)) {
+                        callingPackage, -1, -1, callerApp, null, false, false, null)) {
                     if (!mService.isBackgroundActivityStartsEnabled()) {
                         return;
                     }
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
deleted file mode 100644
index 5385e2f..0000000
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ /dev/null
@@ -1,584 +0,0 @@
-/*
- * 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.server.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.animation.AnimationHandler;
-import android.animation.Animator;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.view.Choreographer;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Enables animating bounds of objects.
- *
- * In multi-window world bounds of both stack and tasks can change. When we need these bounds to
- * change smoothly and not require the app to relaunch (e.g. because it handles resizes and
- * relaunching it would cause poorer experience), these class provides a way to directly animate
- * the bounds of the resized object.
- *
- * The object that is resized needs to implement {@link BoundsAnimationTarget} interface.
- *
- * NOTE: All calls to methods in this class should be done on the Animation thread
- */
-public class BoundsAnimationController {
-    private static final boolean DEBUG_LOCAL = false;
-    private static final boolean DEBUG = DEBUG_LOCAL || DEBUG_ANIM;
-    private static final String TAG = TAG_WITH_CLASS_NAME || DEBUG_LOCAL
-            ? "BoundsAnimationController" : TAG_WM;
-    private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1;
-
-    private static final int DEFAULT_TRANSITION_DURATION = 425;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START,
-        SCHEDULE_PIP_MODE_CHANGED_ON_END})
-    public @interface SchedulePipModeChangedState {}
-    /** Do not schedule any PiP mode changed callbacks as a part of this animation. */
-    public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0;
-    /** Schedule a PiP mode changed callback when this animation starts. */
-    public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1;
-    /** Schedule a PiP mode changed callback when this animation ends. */
-    public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2;
-
-    public static final int BOUNDS = 0;
-    public static final int FADE_IN = 1;
-
-    @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {}
-
-    private static final int FADE_IN_DURATION = 500;
-
-    // Only accessed on UI thread.
-    private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
-
-    private final class AppTransitionNotifier
-            extends WindowManagerInternal.AppTransitionListener implements Runnable {
-
-        public void onAppTransitionCancelledLocked() {
-            if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:"
-                    + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
-            animationFinished();
-        }
-        public void onAppTransitionFinishedLocked(IBinder token) {
-            if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:"
-                    + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition);
-            animationFinished();
-        }
-        private void animationFinished() {
-            if (mFinishAnimationAfterTransition) {
-                mHandler.removeCallbacks(this);
-                // This might end up calling into activity manager which will be bad since we have
-                // the window manager lock held at this point. Post a message to take care of the
-                // processing so we don't deadlock.
-                mHandler.post(this);
-            }
-        }
-
-        @Override
-        public void run() {
-            for (int i = 0; i < mRunningAnimations.size(); i++) {
-                final BoundsAnimator b = mRunningAnimations.valueAt(i);
-                b.onAnimationEnd(null);
-            }
-        }
-    }
-
-    private final Handler mHandler;
-    private final AppTransition mAppTransition;
-    private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier();
-    private final Interpolator mFastOutSlowInInterpolator;
-    private boolean mFinishAnimationAfterTransition = false;
-    private final AnimationHandler mAnimationHandler;
-    private Choreographer mChoreographer;
-    private @AnimationType int mAnimationType;
-
-    private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
-
-    BoundsAnimationController(Context context, AppTransition transition, Handler handler,
-            AnimationHandler animationHandler) {
-        mHandler = handler;
-        mAppTransition = transition;
-        mAppTransition.registerListenerLocked(mAppTransitionNotifier);
-        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_slow_in);
-        mAnimationHandler = animationHandler;
-        if (animationHandler != null) {
-            // If an animation handler is provided, then ensure that it runs on the sf vsync tick
-            handler.post(() -> {
-                mChoreographer = Choreographer.getSfInstance();
-                animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer));
-            });
-        }
-    }
-
-    @VisibleForTesting
-    final class BoundsAnimator extends ValueAnimator
-            implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
-
-        private final BoundsAnimationTarget mTarget;
-        private final @AnimationType int mAnimationType;
-        private final Rect mFrom = new Rect();
-        private final Rect mTo = new Rect();
-        private final Rect mTmpRect = new Rect();
-        private final Rect mTmpTaskBounds = new Rect();
-
-        // True if this this animation was canceled and will be replaced the another animation from
-        // the same {@link #BoundsAnimationTarget} target.
-        private boolean mSkipFinalResize;
-        // True if this animation was canceled by the user, not as a part of a replacing animation
-        private boolean mSkipAnimationEnd;
-
-        // True if the animation target is animating from the fullscreen. Only one of
-        // {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be true at any time in the
-        // animation.
-        private boolean mMoveFromFullscreen;
-        // True if the animation target should be moved to the fullscreen stack at the end of this
-        // animation. Only one of {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be
-        // true at any time in the animation.
-        private boolean mMoveToFullscreen;
-
-        // Whether to schedule PiP mode changes on animation start/end
-        private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
-        private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState;
-
-        // Depending on whether we are animating from
-        // a smaller to a larger size
-        private int mFrozenTaskWidth;
-        private int mFrozenTaskHeight;
-
-        // Timeout callback to ensure we continue the animation if waiting for resuming or app
-        // windows drawn fails
-        private final Runnable mResumeRunnable = () -> {
-            if (DEBUG) Slog.d(TAG, "pause: timed out waiting for windows drawn");
-            resume();
-        };
-
-        // If this animator is explicitly cancelled when it's in paused state, we should not
-        // attempt to resume the animation. Use this flag to avoid such behavior.
-        private boolean mIsCancelled;
-
-        BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from,
-                Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState,
-                @SchedulePipModeChangedState int prevShedulePipModeChangedState,
-                boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) {
-            super();
-            mTarget = target;
-            mAnimationType = animationType;
-            mFrom.set(from);
-            mTo.set(to);
-            mSchedulePipModeChangedState = schedulePipModeChangedState;
-            mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState;
-            mMoveFromFullscreen = moveFromFullscreen;
-            mMoveToFullscreen = moveToFullscreen;
-            addUpdateListener(this);
-            addListener(this);
-
-            // If we are animating from smaller to larger, we want to change the task bounds
-            // to their final size immediately so we can use scaling to make the window
-            // larger. Likewise if we are going from bigger to smaller, we want to wait until
-            // the end so we don't have to upscale from the smaller finished size.
-            if (mAnimationType == BOUNDS) {
-                if (animatingToLargerSize()) {
-                    mFrozenTaskWidth = mTo.width();
-                    mFrozenTaskHeight = mTo.height();
-                } else {
-                    mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width();
-                    mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height();
-                }
-            }
-        }
-
-        @Override
-        public void onAnimationStart(Animator animation) {
-            if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
-                    + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState
-                    + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
-            mIsCancelled = false;
-            mFinishAnimationAfterTransition = false;
-            mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
-                    mFrom.top + mFrozenTaskHeight);
-
-            // Boost the thread priority of the animation thread while the bounds animation is
-            // running
-            updateBooster();
-
-            // Ensure that we have prepared the target for animation before we trigger any size
-            // changes, so it can swap surfaces in to appropriate modes, or do as it wishes
-            // otherwise.
-            boolean continueAnimation;
-            if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
-                continueAnimation = mTarget.onAnimationStart(
-                        mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START,
-                        false /* forceUpdate */, mAnimationType);
-
-                // When starting an animation from fullscreen, pause here and wait for the
-                // windows-drawn signal before we start the rest of the transition down into PiP.
-                if (continueAnimation && mMoveFromFullscreen
-                        && mTarget.shouldDeferStartOnMoveToFullscreen()) {
-                    pause();
-                }
-            } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END &&
-                    mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                // We are replacing a running animation into PiP, but since it hasn't completed, the
-                // client will not currently receive any picture-in-picture mode change callbacks.
-                // However, we still need to report to them that they are leaving PiP, so this will
-                // force an update via a mode changed callback.
-                continueAnimation = mTarget.onAnimationStart(
-                        true /* schedulePipModeChangedCallback */, true /* forceUpdate */,
-                        mAnimationType);
-            } else {
-                // The animation is already running, but we should check that the TaskStack is still
-                // valid before continuing with the animation
-                continueAnimation = mTarget.isAttached();
-            }
-
-            if (!continueAnimation) {
-                // No point of trying to animate something that isn't attached to the hierarchy
-                // anymore.
-                cancel();
-                return;
-            }
-
-            // Immediately update the task bounds if they have to become larger, but preserve
-            // the starting position so we don't jump at the beginning of the animation.
-            if (animatingToLargerSize()) {
-                mTarget.setPinnedStackSize(mFrom, mTmpRect);
-
-                // We pause the animation until the app has drawn at the new size.
-                // The target will notify us via BoundsAnimationController#resume.
-                // We do this here and pause the animation, rather than just defer starting it
-                // so we can enter the animating state and have WindowStateAnimator apply the
-                // correct logic to make this resize seamless.
-                if (mMoveToFullscreen) {
-                    pause();
-                }
-            }
-        }
-
-        @Override
-        public void pause() {
-            if (DEBUG) Slog.d(TAG, "pause: waiting for windows drawn");
-            super.pause();
-            mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS);
-        }
-
-        @Override
-        public void resume() {
-            if (DEBUG) Slog.d(TAG, "resume:");
-            mHandler.removeCallbacks(mResumeRunnable);
-            if (!mIsCancelled) super.resume();
-        }
-
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            final float value = (Float) animation.getAnimatedValue();
-            if (mAnimationType == FADE_IN) {
-                if (!mTarget.setPinnedStackAlpha(value)) {
-                    cancelAndCallAnimationEnd();
-                }
-                return;
-            }
-
-            final float remains = 1 - value;
-            mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f);
-            mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f);
-            mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f);
-            mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f);
-            if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds="
-                    + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value
-                    + " remains=" + remains);
-
-            mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top,
-                    mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight);
-
-            if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) {
-                // Whoops, the target doesn't feel like animating anymore. Let's immediately finish
-                // any further animation.
-                if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled");
-
-                // If we have already scheduled a PiP mode changed at the start of the animation,
-                // then we need to clean up and schedule one at the end, since we have canceled the
-                // animation to the final state.
-                if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                    mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-                }
-
-                // Since we are cancelling immediately without a replacement animation, send the
-                // animation end to maintain callback parity, but also skip any further resizes
-                cancelAndCallAnimationEnd();
-            }
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
-                    + " mSkipFinalResize=" + mSkipFinalResize
-                    + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition
-                    + " mAppTransitionIsRunning=" + mAppTransition.isRunning()
-                    + " callers=" + Debug.getCallers(2));
-
-            // There could be another animation running. For example in the
-            // move to fullscreen case, recents will also be closing while the
-            // previous task will be taking its place in the fullscreen stack.
-            // we have to ensure this is completed before we finish the animation
-            // and take our place in the fullscreen stack.
-            if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) {
-                mFinishAnimationAfterTransition = true;
-                return;
-            }
-
-            if (!mSkipAnimationEnd) {
-                // If this animation has already scheduled the picture-in-picture mode on start, and
-                // we are not skipping the final resize due to being canceled, then move the PiP to
-                // fullscreen once the animation ends
-                if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
-                        + " moveToFullscreen=" + mMoveToFullscreen);
-                mTarget.onAnimationEnd(mSchedulePipModeChangedState ==
-                        SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null,
-                                mMoveToFullscreen);
-            }
-
-            // Clean up this animation
-            removeListener(this);
-            removeUpdateListener(this);
-            mRunningAnimations.remove(mTarget);
-
-            // Reset the thread priority of the animation thread after the bounds animation is done
-            updateBooster();
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            mIsCancelled = true;
-            // Always skip the final resize when the animation is canceled
-            mSkipFinalResize = true;
-            mMoveToFullscreen = false;
-        }
-
-        private void cancelAndCallAnimationEnd() {
-            if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget);
-            mSkipAnimationEnd = false;
-            super.cancel();
-        }
-
-        @Override
-        public void cancel() {
-            if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
-            mSkipAnimationEnd = true;
-            super.cancel();
-
-            // Reset the thread priority of the animation thread if the bounds animation is canceled
-            updateBooster();
-        }
-
-        /**
-         * @return true if the animation target is the same as the input bounds.
-         */
-        boolean isAnimatingTo(Rect bounds) {
-            return mTo.equals(bounds);
-        }
-
-        /**
-         * @return true if we are animating to a larger surface size
-         */
-        @VisibleForTesting
-        boolean animatingToLargerSize() {
-            // TODO: Fix this check for aspect ratio changes
-            return (mFrom.width() * mFrom.height() < mTo.width() * mTo.height());
-        }
-
-        @Override
-        public void onAnimationRepeat(Animator animation) {
-            // Do nothing
-        }
-
-        @Override
-        public AnimationHandler getAnimationHandler() {
-            if (mAnimationHandler != null) {
-                return mAnimationHandler;
-            }
-            return super.getAnimationHandler();
-        }
-    }
-
-    public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to,
-            int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
-            boolean moveFromFullscreen, boolean moveToFullscreen,
-            @AnimationType int animationType) {
-        animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState,
-                moveFromFullscreen, moveToFullscreen, animationType);
-    }
-
-    /**
-     * Cancel existing animation if the destination was modified.
-     */
-    void cancel(final BoundsAnimationTarget target) {
-        final BoundsAnimator existing = mRunningAnimations.get(target);
-        if (existing != null) {
-            // Cancel animation. Since its already started, send animation end to client.
-            if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target);
-            existing.cancelAndCallAnimationEnd();
-        }
-    }
-
-    @VisibleForTesting
-    BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to,
-            int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
-            boolean moveFromFullscreen, boolean moveToFullscreen,
-            @AnimationType int animationType) {
-        final BoundsAnimator existing = mRunningAnimations.get(target);
-
-        if (isRunningFadeInAnimation(target) && from.width() == to.width()
-                && from.height() == to.height()) {
-            animationType = FADE_IN;
-        }
-        final boolean replacing = existing != null;
-        @SchedulePipModeChangedState int prevSchedulePipModeChangedState =
-                NO_PIP_MODE_CHANGED_CALLBACKS;
-
-        if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
-                + " schedulePipModeChangedState=" + schedulePipModeChangedState
-                + " replacing=" + replacing);
-
-        Rect frozenTask = new Rect();
-        if (replacing) {
-            if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen)
-                    && (!moveFromFullscreen || existing.mMoveFromFullscreen)) {
-                // Just let the current animation complete if it has the same destination as the
-                // one we are trying to start, and, if moveTo/FromFullscreen was requested, already
-                // has that flag set.
-                if (DEBUG) Slog.d(TAG, "animateBounds: same destination and moveTo/From flags as "
-                        + "existing=" + existing + ", ignoring...");
-                return existing;
-            }
-
-            // Save the previous state
-            prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState;
-
-            // Update the PiP callback states if we are replacing the animation
-            if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                    if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep"
-                            + " existing deferred state");
-                } else {
-                    if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback"
-                            + " on start already processed, schedule deferred update on end");
-                    schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-                }
-            } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) {
-                if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
-                    if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled,"
-                            + " callback on start will be processed");
-                } else {
-                    if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep"
-                            + " existing deferred state");
-                    schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
-                }
-            }
-
-            // We need to keep the previous moveTo/FromFullscreen flag, unless the new animation
-            // specifies a direction.
-            if (!moveFromFullscreen && !moveToFullscreen) {
-                moveToFullscreen = existing.mMoveToFullscreen;
-                moveFromFullscreen = existing.mMoveFromFullscreen;
-            }
-
-            // We are in the middle of an existing animation, so that this new animation may
-            // start from an interpolated bounds. We should keep using the existing frozen task
-            // width/height for consistent configurations.
-            frozenTask.set(0, 0, existing.mFrozenTaskWidth, existing.mFrozenTaskHeight);
-
-            // Since we are replacing, we skip both animation start and end callbacks
-            existing.cancel();
-        }
-        if (animationType == FADE_IN) {
-            target.setPinnedStackSize(to, null);
-        }
-
-        final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to,
-                schedulePipModeChangedState, prevSchedulePipModeChangedState,
-                moveFromFullscreen, moveToFullscreen, frozenTask);
-        mRunningAnimations.put(target, animator);
-        animator.setFloatValues(0f, 1f);
-        animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION
-                : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION)
-                        * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
-        animator.setInterpolator(mFastOutSlowInInterpolator);
-        animator.start();
-        return animator;
-    }
-
-    public void setAnimationType(@AnimationType int animationType) {
-        mAnimationType = animationType;
-    }
-
-    /** return the current animation type. */
-    public @AnimationType int getAnimationType() {
-        @AnimationType int animationType = mAnimationType;
-        // Default to BOUNDS.
-        mAnimationType = BOUNDS;
-        return animationType;
-    }
-
-    boolean isAnimationTypeFadeIn() {
-        return mAnimationType == FADE_IN;
-    }
-
-    public Handler getHandler() {
-        return mHandler;
-    }
-
-    public void onAllWindowsDrawn() {
-        if (DEBUG) Slog.d(TAG, "onAllWindowsDrawn:");
-        mHandler.post(this::resume);
-    }
-
-    private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) {
-        final BoundsAnimator existing = mRunningAnimations.get(target);
-        return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted();
-    }
-
-    private void resume() {
-        for (int i = 0; i < mRunningAnimations.size(); i++) {
-            final BoundsAnimator b = mRunningAnimations.valueAt(i);
-            b.resume();
-        }
-    }
-
-    private void updateBooster() {
-        WindowManagerService.sThreadPriorityBooster.setBoundsAnimationRunning(
-                !mRunningAnimations.isEmpty());
-    }
-}
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
deleted file mode 100644
index b1d5359..0000000
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.graphics.Rect;
-
-/**
- * The target for a BoundsAnimation.
- * @see BoundsAnimationController
- */
-interface BoundsAnimationTarget {
-
-    /**
-     * Callback for the target to inform it that the animation has started, so it can do some
-     * necessary preparation.
-     *
-     * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
-     * callbacks
-     * @return whether to continue the animation
-     */
-    boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
-            @BoundsAnimationController.AnimationType int animationType);
-
-    /**
-     * @return Whether the animation should be paused waiting for the windows to draw before
-     *         entering PiP.
-     */
-    boolean shouldDeferStartOnMoveToFullscreen();
-
-    /**
-     * Sets the size of the target (without any intermediate steps, like scheduling animation)
-     * but freezes the bounds of any tasks in the target at taskBounds, to allow for more
-     * flexibility during resizing. Only works for the pinned stack at the moment.  This will
-     * only be called between onAnimationStart() and onAnimationEnd().
-     *
-     * @return Whether the target should continue to be animated and this call was successful.
-     * If false, the animation will be cancelled because the user has determined that the
-     * animation is now invalid and not required. In such a case, the cancel will trigger the
-     * animation end callback as well, but will not send any further size changes.
-     */
-    boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds);
-
-    /** Sets the alpha of the animation target */
-    boolean setPinnedStackAlpha(float alpha);
-
-    /**
-     * Callback for the target to inform it that the animation has ended, so it can do some
-     * necessary cleanup.
-     *
-     * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
-     * callbacks
-     * @param finalStackSize the final stack bounds to set on the target (can be to indicate that
-     * the animation was cancelled and the target does not need to update to the final stack bounds)
-     * @param moveToFullscreen whether or the target should reparent itself to the fullscreen stack
-     * when the animation completes
-     */
-    void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
-            boolean moveToFullscreen);
-
-    /** @return True if the target is attached to the window hierarchy. */
-    default boolean isAttached() {
-        return true;
-    }
-}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0029dc8..e468810 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -150,7 +150,6 @@
 import static com.android.server.wm.utils.RegionUtils.forEachRectReverse;
 import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
 
-import android.animation.AnimationHandler;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -317,7 +316,6 @@
     final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
     final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>();
     final UnknownAppVisibilityController mUnknownAppVisibilityController;
-    BoundsAnimationController mBoundsAnimationController;
 
     private MetricsLogger mMetricsLogger;
 
@@ -977,10 +975,6 @@
         mAppTransitionController = new AppTransitionController(mWmService, this);
         mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
 
-        AnimationHandler animationHandler = new AnimationHandler();
-        mBoundsAnimationController = new BoundsAnimationController(mWmService.mContext,
-                mAppTransition, mWmService.mAnimationHandler, animationHandler);
-
         final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
                 "PointerEventDispatcher" + mDisplayId, mDisplayId);
         mPointerEventDispatcher = new PointerEventDispatcher(inputChannel);
@@ -2718,6 +2712,9 @@
             mRemovingDisplay = false;
         }
 
+        // Apply the pending transaction here since we may not be able to reach the DisplayContent
+        // on the next traversal if it's removed from RootWindowContainer child list.
+        getPendingTransaction().apply();
         mWmService.mWindowPlacerLocked.requestTraversal();
     }
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6431e11..958c8ae 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -53,6 +53,7 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DockedDividerUtils;
 import com.android.server.LocalServices;
@@ -517,7 +518,8 @@
 
                 // Hide the current IME to avoid problems with animations from IME adjustment when
                 // attaching the docked stack.
-                inputMethodManagerInternal.hideCurrentInputMethod();
+                inputMethodManagerInternal.hideCurrentInputMethod(
+                        SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED);
                 mImeHideRequested = true;
             }
 
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 668b609..e14b8ae 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -52,7 +50,7 @@
  * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio
  *    and IME state into account.
  * 2) When rotating the device: the controller calculates the new bounds in the new orientation,
- *    taking the minimized and IME state into account. In this case, we currently ignore the
+ *    taking the IME state into account. In this case, we currently ignore the
  *    SystemUI adjustments (ie. expanded for menu, interaction, etc).
  *
  * Other changes in the system, including adjustment of IME, configuration change, and more are
@@ -72,8 +70,6 @@
 
     private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback();
 
-    // States that affect how the PIP can be manipulated
-    private boolean mIsMinimized;
     private boolean mIsImeShowing;
     private int mImeHeight;
 
@@ -84,9 +80,6 @@
     // Used to calculate stack bounds across rotations
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
 
-    // The size and position information that describes where the pinned stack will go by default.
-    private float mDefaultAspectRatio;
-
     // The aspect ratio bounds of the PIP.
     private float mMinAspectRatio;
     private float mMaxAspectRatio;
@@ -98,43 +91,12 @@
      * The callback object passed to listeners for them to notify the controller of state changes.
      */
     private class PinnedStackControllerCallback extends IPinnedStackController.Stub {
-
-        @Override
-        public void setIsMinimized(final boolean isMinimized) {
-            mHandler.post(() -> {
-                mIsMinimized = isMinimized;
-            });
-        }
-
         @Override
         public int getDisplayRotation() {
             synchronized (mService.mGlobalLock) {
                 return mDisplayInfo.rotation;
             }
         }
-
-        @Override
-        public void startAnimation(Rect destinationBounds, Rect sourceRectHint,
-                int animationDuration) {
-            synchronized (mService.mGlobalLock) {
-                final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask();
-                pinnedStack.animateResizePinnedStack(destinationBounds,
-                        sourceRectHint, animationDuration, true /* fromFullscreen */);
-            }
-        }
-
-        @Override
-        public void resetBoundsAnimation(Rect bounds) {
-            synchronized (mService.mGlobalLock) {
-                if (mDisplayContent.hasPinnedTask()) {
-                    final ActivityStack pinnedStack = mDisplayContent.getTopStackInWindowingMode(
-                            WINDOWING_MODE_PINNED);
-                    if (pinnedStack != null) {
-                        pinnedStack.resetCurrentBoundsAnimation(bounds);
-                    }
-                }
-            }
-        }
     }
 
     /**
@@ -157,10 +119,6 @@
         mDisplayContent = displayContent;
         mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo());
         reloadResources();
-        // Initialize the aspect ratio to the default aspect ratio.  Don't do this in reload
-        // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
-        // triggers a configuration change and the resources to be reloaded.
-        mAspectRatio = mDefaultAspectRatio;
     }
 
     void onConfigurationChanged() {
@@ -172,8 +130,6 @@
      */
     private void reloadResources() {
         final Resources res = mService.mContext.getResources();
-        mDefaultAspectRatio = res.getFloat(
-                com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
         mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics);
         mMinAspectRatio = res.getFloat(
                 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
@@ -191,12 +147,8 @@
             mPinnedStackListener = listener;
             notifyDisplayInfoChanged(mDisplayInfo);
             notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
-            // The movement bounds notification needs to be sent before the minimized state, since
-            // SystemUI may use the bounds to restore the minimized position
-            notifyMovementBoundsChanged(false /* fromImeAdjustment */,
-                    false /* fromShelfAdjustment */);
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
             notifyActionsChanged(mActions);
-            notifyMinimizeChanged(mIsMinimized);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register pinned stack listener", e);
         }
@@ -247,8 +199,7 @@
     void onDisplayInfoChanged(DisplayInfo displayInfo) {
         synchronized (mService.mGlobalLock) {
             setDisplayInfo(displayInfo);
-            notifyMovementBoundsChanged(false /* fromImeAdjustment */,
-                    false /* fromShelfAdjustment */);
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
         }
     }
 
@@ -269,7 +220,7 @@
         mIsImeShowing = imeShowing;
         mImeHeight = imeHeight;
         notifyImeVisibilityChanged(imeShowing, imeHeight);
-        notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */);
+        notifyMovementBoundsChanged(true /* fromImeAdjustment */);
     }
 
     /**
@@ -279,10 +230,7 @@
         if (Float.compare(mAspectRatio, aspectRatio) != 0) {
             mAspectRatio = aspectRatio;
             notifyAspectRatioChanged(aspectRatio);
-            notifyMovementBoundsChanged(false /* fromImeAdjustment */,
-                    false /* fromShelfAdjustment */);
-            notifyPrepareAnimation(null /* sourceHintRect */, aspectRatio,
-                    null /* stackBounds */);
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
         }
     }
 
@@ -304,10 +252,6 @@
         notifyActionsChanged(mActions);
     }
 
-    void prepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
-        notifyPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
-    }
-
     /**
      * Notifies listeners that the PIP needs to be adjusted for the IME.
      */
@@ -331,19 +275,6 @@
     }
 
     /**
-     * Notifies listeners that the PIP minimized state has changed.
-     */
-    private void notifyMinimizeChanged(boolean isMinimized) {
-        if (mPinnedStackListener != null) {
-            try {
-                mPinnedStackListener.onMinimizedStateChanged(isMinimized);
-            } catch (RemoteException e) {
-                Slog.e(TAG_WM, "Error delivering minimize changed event.", e);
-            }
-        }
-    }
-
-    /**
      * Notifies listeners that the PIP actions have changed.
      */
     private void notifyActionsChanged(List<RemoteAction> actions) {
@@ -359,8 +290,7 @@
     /**
      * Notifies listeners that the PIP movement bounds have changed.
      */
-    private void notifyMovementBoundsChanged(boolean fromImeAdjustment,
-            boolean fromShelfAdjustment) {
+    private void notifyMovementBoundsChanged(boolean fromImeAdjustment) {
         synchronized (mService.mGlobalLock) {
             if (mPinnedStackListener == null) {
                 return;
@@ -371,8 +301,7 @@
                 if (pinnedStack != null) {
                     pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
                 }
-                mPinnedStackListener.onMovementBoundsChanged(animatingBounds,
-                        fromImeAdjustment, fromShelfAdjustment);
+                mPinnedStackListener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment);
             } catch (RemoteException e) {
                 Slog.e(TAG_WM, "Error delivering actions changed event.", e);
             }
@@ -391,24 +320,10 @@
         }
     }
 
-    /**
-     * Notifies listeners that the PIP animation is about to happen.
-     */
-    private void notifyPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
-        if (mPinnedStackListener == null) return;
-        try {
-            mPinnedStackListener.onPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
-        } catch (RemoteException e) {
-            Slog.e(TAG_WM, "Error delivering prepare animation event.", e);
-        }
-    }
-
     void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "PinnedStackController");
-        pw.println(prefix + "  mDefaultAspectRatio=" + mDefaultAspectRatio);
         pw.println(prefix + "  mIsImeShowing=" + mIsImeShowing);
         pw.println(prefix + "  mImeHeight=" + mImeHeight);
-        pw.println(prefix + "  mIsMinimized=" + mIsMinimized);
         pw.println(prefix + "  mAspectRatio=" + mAspectRatio);
         pw.println(prefix + "  mMinAspectRatio=" + mMinAspectRatio);
         pw.println(prefix + "  mMaxAspectRatio=" + mMaxAspectRatio);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index e92bbaa..adafdec 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -26,8 +26,6 @@
 import static android.view.WindowManager.TRANSIT_NONE;
 
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.BoundsAnimationController.BOUNDS;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -426,11 +424,6 @@
             return;
         }
 
-        final DisplayContent dc =
-                mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent;
-        dc.mBoundsAnimationController.setAnimationType(
-                controller.shouldDeferCancelUntilNextTransition() ? FADE_IN : BOUNDS);
-
         // We defer canceling the recents animation until the next app transition in the following
         // cases:
         // 1) The next launching task is not being animated by the recents animation
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6b6b946..e69551a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -26,7 +26,6 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
@@ -55,6 +54,7 @@
 import android.view.SurfaceControl.Transaction;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -231,7 +231,6 @@
                 mCallbacks.onAnimationFinished(moveHomeToTop
                         ? REORDER_MOVE_TO_TOP
                         : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint);
-                mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -301,7 +300,8 @@
                 final InputMethodManagerInternal inputMethodManagerInternal =
                         LocalServices.getService(InputMethodManagerInternal.class);
                 if (inputMethodManagerInternal != null) {
-                    inputMethodManagerInternal.hideCurrentInputMethod();
+                    inputMethodManagerInternal.hideCurrentInputMethod(
+                            SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -309,14 +309,6 @@
         }
 
         @Override
-        @Deprecated
-        public void setCancelWithDeferredScreenshot(boolean screenshot) {
-            synchronized (mService.mGlobalLock) {
-                setDeferredCancel(true /* deferred */, screenshot);
-            }
-        }
-
-        @Override
         public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
             synchronized (mService.mGlobalLock) {
                 setDeferredCancel(defer, screenshot);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index aa6bdfd..64d7db2 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2149,19 +2149,6 @@
             mService.continueWindowLayout();
         }
 
-        // TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation.
-        // Notify the pinned stack controller to prepare the PiP animation, expect callback
-        // delivered from SystemUI to WM to start the animation. Unless we are using
-        // the TaskOrganizer in which case the animation will be entirely handled
-        // on that side.
-        if (mService.mTaskOrganizerController.getTaskOrganizer(WINDOWING_MODE_PINNED)
-                == null) {
-            final PinnedStackController pinnedStackController =
-                display.mDisplayContent.getPinnedStackController();
-            pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio,
-                    null /* stackBounds */);
-        }
-
         // TODO: revisit the following statement after the animation is moved from WM to SysUI.
         // Update the visibility of all activities after the they have been reparented to the new
         // stack.  This MUST run after the animation above is scheduled to ensure that the windows
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ecfac1b..76805e9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -111,7 +111,6 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.drawable.Icon;
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -120,7 +119,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.voice.IVoiceInteractionSession;
-import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.view.DisplayInfo;
@@ -2910,8 +2908,7 @@
      * we will have a jump at the end.
      */
     boolean isFloating() {
-        return getWindowConfiguration().tasksAreFloating()
-                && !getStack().isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState;
+        return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index f715d8f..96a9127 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -37,7 +37,6 @@
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2;
     private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3;
     private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4;
-    private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5;
     private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6;
     private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7;
     private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8;
@@ -48,7 +47,6 @@
     private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13;
     private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14;
     private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
-    private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16;
     private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
     private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19;
@@ -124,14 +122,6 @@
         l.onPinnedActivityRestartAttempt(m.arg1 != 0);
     };
 
-    private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> {
-        l.onPinnedStackAnimationStarted();
-    };
-
-    private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> {
-        l.onPinnedStackAnimationEnded();
-    };
-
     private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
         l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2);
     };
@@ -233,12 +223,6 @@
                 case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG:
                     forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg);
                     break;
-                case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg);
-                    break;
-                case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG:
-                    forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg);
-                    break;
                 case NOTIFY_FORCED_RESIZABLE_MSG:
                     forAllRemoteListeners(mNotifyActivityForcedResizable, msg);
                     break;
@@ -386,24 +370,6 @@
         msg.sendToTarget();
     }
 
-    /** Notifies all listeners when the pinned stack animation starts. */
-    void notifyPinnedStackAnimationStarted() {
-        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
-        final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG);
-        forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg);
-        msg.sendToTarget();
-    }
-
-    /** Notifies all listeners when the pinned stack animation ends. */
-    void notifyPinnedStackAnimationEnded() {
-        mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
-        final Message msg =
-                mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
-        forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg);
-        msg.sendToTarget();
-    }
-
     void notifyActivityDismissingDockedStack() {
         mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
         final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 6caa27c..0f1e623 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -306,7 +306,8 @@
         task.fillTaskInfo(mTmpTaskInfo);
         boolean changed = lastInfo == null
                 || mTmpTaskInfo.topActivityType != lastInfo.topActivityType
-                || mTmpTaskInfo.isResizable() != lastInfo.isResizable();
+                || mTmpTaskInfo.isResizable() != lastInfo.isResizable()
+                || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams;
         if (!(changed || force)) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 5c73f92..f83b052 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -18,19 +18,18 @@
 
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 
-import static com.android.server.wm.TaskSnapshotPersister.DISABLE_HIGH_RES_BITMAPS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RenderNode;
@@ -89,14 +88,6 @@
     @VisibleForTesting
     static final int SNAPSHOT_MODE_NONE = 2;
 
-    /**
-     * Constant for <code>scaleFactor</code> when calling {@link #snapshotTask} which is
-     * interpreted as using the most appropriate scale ratio for the system.
-     * This may yield a smaller ratio on low memory devices.
-     */
-    @VisibleForTesting
-    static final float SNAPSHOT_SCALE_AUTO = -1f;
-
     private final WindowManagerService mService;
 
     private final TaskSnapshotCache mCache;
@@ -229,7 +220,7 @@
     @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
             boolean isLowResolution) {
         return mCache.getSnapshot(taskId, userId, restoreFromDisk, isLowResolution
-                || DISABLE_HIGH_RES_BITMAPS);
+                && mPersister.enableLowResSnapshots());
     }
 
     /**
@@ -273,8 +264,6 @@
      * information from the task and populates the builder.
      *
      * @param task the task to capture
-     * @param scaleFraction the scale fraction between 0-1.0, or {@link #SNAPSHOT_SCALE_AUTO}
-     *                      to automatically select
      * @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to
      *                    automatically select
      * @param builder the snapshot builder to populate
@@ -282,8 +271,7 @@
      * @return true if the state of the task is ok to proceed
      */
     @VisibleForTesting
-    boolean prepareTaskSnapshot(Task task, float scaleFraction, int pixelFormat,
-            TaskSnapshot.Builder builder) {
+    boolean prepareTaskSnapshot(Task task, int pixelFormat, TaskSnapshot.Builder builder) {
         if (!mService.mPolicy.isScreenOn()) {
             if (DEBUG_SCREENSHOT) {
                 Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
@@ -314,18 +302,6 @@
         builder.setId(System.currentTimeMillis());
         builder.setContentInsets(getInsets(mainWindow));
 
-        final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
-
-        if (scaleFraction == SNAPSHOT_SCALE_AUTO) {
-            builder.setScaleFraction(isLowRamDevice
-                    ? mPersister.getLowResScale()
-                    : mHighResTaskSnapshotScale);
-            builder.setIsLowResolution(isLowRamDevice);
-        } else {
-            builder.setScaleFraction(scaleFraction);
-            builder.setIsLowResolution(scaleFraction < 1.0f);
-        }
-
         final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
         final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
 
@@ -351,13 +327,23 @@
 
     @Nullable
     SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
-            float scaleFraction) {
-        return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888);
+            TaskSnapshot.Builder builder) {
+        Point taskSize = new Point();
+        final SurfaceControl.ScreenshotGraphicBuffer taskSnapshot = createTaskSnapshot(task,
+                mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize);
+        builder.setTaskSize(taskSize);
+        return taskSnapshot;
     }
 
     @Nullable
     SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
-            float scaleFraction, int pixelFormat) {
+            float scaleFraction) {
+        return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null);
+    }
+
+    @Nullable
+    SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
+            float scaleFraction, int pixelFormat, Point outTaskSize) {
         if (task.getSurfaceControl() == null) {
             if (DEBUG_SCREENSHOT) {
                 Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
@@ -369,6 +355,10 @@
         final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
                 SurfaceControl.captureLayers(
                         task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
+        if (outTaskSize != null) {
+            outTaskSize.x = mTmpRect.width();
+            outTaskSize.y = mTmpRect.height();
+        }
         final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
                 : null;
         if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
@@ -379,21 +369,20 @@
 
     @Nullable
     TaskSnapshot snapshotTask(Task task) {
-        return snapshotTask(task, SNAPSHOT_SCALE_AUTO, PixelFormat.UNKNOWN);
+        return snapshotTask(task, PixelFormat.UNKNOWN);
     }
 
     @Nullable
-    TaskSnapshot snapshotTask(Task task, float scaleFraction, int pixelFormat) {
+    TaskSnapshot snapshotTask(Task task, int pixelFormat) {
         TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
 
-        if (!prepareTaskSnapshot(task, scaleFraction, pixelFormat, builder)) {
+        if (!prepareTaskSnapshot(task, pixelFormat, builder)) {
             // Failed some pre-req. Has been logged.
             return null;
         }
 
         final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
-                createTaskSnapshot(task, builder.getScaleFraction(),
-                builder.getPixelFormat());
+                createTaskSnapshot(task, builder);
 
         if (screenshotBuffer == null) {
             // Failed to acquire image. Has been logged.
@@ -472,8 +461,10 @@
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
                 attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
                 mHighResTaskSnapshotScale, mainWindow.getRequestedInsetsState());
-        final int width = (int) (task.getBounds().width() * mHighResTaskSnapshotScale);
-        final int height = (int) (task.getBounds().height() * mHighResTaskSnapshotScale);
+        final int taskWidth = task.getBounds().width();
+        final int taskHeight = task.getBounds().height();
+        final int width = (int) (taskWidth * mHighResTaskSnapshotScale);
+        final int height = (int) (taskHeight * mHighResTaskSnapshotScale);
 
         final RenderNode node = RenderNode.create("TaskSnapshotController", null);
         node.setLeftTopRightBottom(0, 0, width, height);
@@ -494,9 +485,9 @@
                 System.currentTimeMillis() /* id */,
                 topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
                 hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
-                mainWindow.getWindowConfiguration().getRotation(),
-                getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* isLowResolution */,
-                mHighResTaskSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(),
+                mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
+                getInsets(mainWindow), false /* isLowResolution */,
+                false /* isRealSnapshot */, task.getWindowingMode(),
                 getSystemUiVisibility(task), false);
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index 01f3427..c20ce5f 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -19,6 +19,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.app.ActivityManager;
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.ComponentName;
 import android.graphics.Bitmap;
@@ -26,6 +27,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapFactory.Options;
 import android.graphics.GraphicBuffer;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Slog;
 
@@ -52,28 +54,110 @@
         mPersister = persister;
     }
 
+    static class PreRLegacySnapshotConfig {
+        /**
+         * If isPreRLegacy is {@code true}, specifies the scale the snapshot was taken at
+         */
+        final float mScale;
+
+        /**
+         * If {@code true}, always load *_reduced.jpg file, no matter what was requested
+         */
+        final boolean mForceLoadReducedJpeg;
+
+        PreRLegacySnapshotConfig(float scale, boolean forceLoadReducedJpeg) {
+            mScale = scale;
+            mForceLoadReducedJpeg = forceLoadReducedJpeg;
+        }
+    }
+
+    /**
+     * When device is upgraded, we might be loading a legacy snapshot. In those cases,
+     * restore the scale based on how it was configured historically. See history of
+     * TaskSnapshotPersister for more information.
+     *
+     *   | low_ram=false                      | low_ram=true
+     *   +------------------------------------------------------------------------------+
+     * O | *.jpg = 100%, *_reduced.jpg = 50%                                            |
+     *   |                                    +-----------------------------------------|
+     * P |                                    | *.jpg = NONE, *_reduced.jpg = 60%       |
+     *   +------------------------------------+-----------------------------------------+
+     * Q | *.jpg = proto.scale,               | *.jpg = NONE,                           |
+     *   | *_reduced.jpg = 50% * proto.scale  | *_reduced.jpg = proto.scale             |
+     *   +------------------------------------+-----------------------------------------+
+     *
+     * @return null if Android R, otherwise a PreRLegacySnapshotConfig object
+     */
+    PreRLegacySnapshotConfig getLegacySnapshotConfig(int taskWidth, float legacyScale,
+            boolean highResFileExists, boolean loadLowResolutionBitmap) {
+        float preRLegacyScale = 0;
+        boolean forceLoadReducedJpeg = false;
+        boolean isPreRLegacySnapshot = (taskWidth == 0);
+        if (!isPreRLegacySnapshot) {
+            return null;
+        }
+        final boolean isPreQLegacyProto = isPreRLegacySnapshot
+                && (Float.compare(legacyScale, 0f) == 0);
+
+        if (isPreQLegacyProto) {
+            // Android O or Android P
+            if (ActivityManager.isLowRamDeviceStatic() && !highResFileExists) {
+                // Android P w/ low_ram=true
+                preRLegacyScale = 0.6f;
+                // Force bitmapFile to always be *_reduced.jpg
+                forceLoadReducedJpeg = true;
+            } else {
+                // Android O, OR Android P w/ low_ram=false
+                preRLegacyScale = loadLowResolutionBitmap ? 0.5f : 1.0f;
+            }
+        } else if (isPreRLegacySnapshot) {
+            // If not pre-Q but is pre-R, then it must be Android Q
+            if (ActivityManager.isLowRamDeviceStatic()) {
+                preRLegacyScale = legacyScale;
+                // Force bitmapFile to always be *_reduced.jpg
+                forceLoadReducedJpeg = true;
+            } else {
+                preRLegacyScale =
+                        loadLowResolutionBitmap ? 0.5f * legacyScale : legacyScale;
+            }
+        }
+        return new PreRLegacySnapshotConfig(preRLegacyScale, forceLoadReducedJpeg);
+    }
+
     /**
      * Loads a task from the disk.
      * <p>
      * Do not hold the window manager lock when calling this method, as we directly read data from
      * disk here, which might be slow.
      *
-     * @param taskId The id of the task to load.
-     * @param userId The id of the user the task belonged to.
-     * @param isLowResolution Whether to load a reduced resolution version of the snapshot.
+     * @param taskId                  The id of the task to load.
+     * @param userId                  The id of the user the task belonged to.
+     * @param loadLowResolutionBitmap Whether to load a low resolution resolution version of the
+     *                                snapshot.
      * @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded.
      */
-    TaskSnapshot loadTask(int taskId, int userId, boolean isLowResolution) {
+    TaskSnapshot loadTask(int taskId, int userId, boolean loadLowResolutionBitmap) {
         final File protoFile = mPersister.getProtoFile(taskId, userId);
-        final File bitmapFile = isLowResolution
-                ? mPersister.getLowResolutionBitmapFile(taskId, userId)
-                : mPersister.getHighResolutionBitmapFile(taskId, userId);
-        if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) {
+        if (!protoFile.exists()) {
             return null;
         }
         try {
             final byte[] bytes = Files.readAllBytes(protoFile.toPath());
             final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
+            final File highResBitmap = mPersister.getHighResolutionBitmapFile(taskId, userId);
+
+            PreRLegacySnapshotConfig legacyConfig = getLegacySnapshotConfig(proto.taskWidth,
+                    proto.legacyScale, highResBitmap.exists(), loadLowResolutionBitmap);
+
+            boolean forceLoadReducedJpeg =
+                    legacyConfig != null && legacyConfig.mForceLoadReducedJpeg;
+            File bitmapFile = (loadLowResolutionBitmap || forceLoadReducedJpeg)
+                    ? mPersister.getLowResolutionBitmapFile(taskId, userId) : highResBitmap;
+
+            if (!bitmapFile.exists()) {
+                return null;
+            }
+
             final Options options = new Options();
             options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent
                     ? Config.RGB_565
@@ -99,13 +183,20 @@
 
             final ComponentName topActivityComponent = ComponentName.unflattenFromString(
                     proto.topActivityComponent);
-            // For legacy snapshots, restore the scale based on the reduced resolution state
-            final float legacyScale = isLowResolution ? mPersister.getLowResScale() : 1f;
-            final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale;
-            return new TaskSnapshot(proto.id, topActivityComponent, buffer, hwBitmap.getColorSpace(),
-                    proto.orientation, proto.rotation,
+
+            Point taskSize;
+            if (legacyConfig != null) {
+                int taskWidth = (int) ((float) hwBitmap.getWidth() / legacyConfig.mScale);
+                int taskHeight = (int) ((float) hwBitmap.getHeight() / legacyConfig.mScale);
+                taskSize = new Point(taskWidth, taskHeight);
+            } else {
+                taskSize = new Point(proto.taskWidth, proto.taskHeight);
+            }
+
+            return new TaskSnapshot(proto.id, topActivityComponent, buffer,
+                    hwBitmap.getColorSpace(), proto.orientation, proto.rotation, taskSize,
                     new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
-                    isLowResolution, scale, proto.isRealSnapshot, proto.windowingMode,
+                    loadLowResolutionBitmap, proto.isRealSnapshot, proto.windowingMode,
                     proto.systemUiVisibility, proto.isTranslucent);
         } catch (IOException e) {
             Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 31212b8..164d3e0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -21,8 +21,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.annotation.NonNull;
 import android.annotation.TestApi;
-import android.app.ActivityManager;
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.Config;
@@ -52,8 +52,6 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
     private static final String SNAPSHOTS_DIRNAME = "snapshots";
     private static final String LOW_RES_FILE_POSTFIX = "_reduced";
-    private static final float LOW_RAM_REDUCED_SCALE = .8f;
-    static final boolean DISABLE_HIGH_RES_BITMAPS = ActivityManager.isLowRamDeviceStatic();
     private static final long DELAY_MS = 100;
     private static final int QUALITY = 95;
     private static final String PROTO_EXTENSION = ".proto";
@@ -71,7 +69,8 @@
     private boolean mStarted;
     private final Object mLock = new Object();
     private final DirectoryResolver mDirectoryResolver;
-    private final float mLowResScale;
+    private final float mLowResScaleFactor;
+    private boolean mEnableLowResSnapshots;
     private final boolean mUse16BitFormat;
 
     /**
@@ -83,13 +82,29 @@
 
     TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
         mDirectoryResolver = resolver;
+        final float highResTaskSnapshotScale = service.mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_highResTaskSnapshotScale);
+        final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat(
+                com.android.internal.R.dimen.config_lowResTaskSnapshotScale);
 
-        if (ActivityManager.isLowRamDeviceStatic()) {
-            mLowResScale = LOW_RAM_REDUCED_SCALE;
-        } else {
-            mLowResScale = service.mContext.getResources().getFloat(
-                    com.android.internal.R.dimen.config_lowResTaskSnapshotScale);
+        if (lowResTaskSnapshotScale < 0 || 1 <= lowResTaskSnapshotScale) {
+            throw new RuntimeException("Low-res scale must be between 0 and 1");
         }
+        if (highResTaskSnapshotScale <= 0 || 1 < highResTaskSnapshotScale) {
+            throw new RuntimeException("High-res scale must be between 0 and 1");
+        }
+        if (highResTaskSnapshotScale <= lowResTaskSnapshotScale) {
+            throw new RuntimeException("High-res scale must be greater than low-res scale");
+        }
+
+        if (lowResTaskSnapshotScale > 0) {
+            mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale;
+            setEnableLowResSnapshots(true);
+        } else {
+            mLowResScaleFactor = 0;
+            setEnableLowResSnapshots(false);
+        }
+
         mUse16BitFormat = service.mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat);
     }
@@ -155,13 +170,16 @@
         }
     }
 
+    boolean enableLowResSnapshots() {
+        return mEnableLowResSnapshots;
+    }
+
     /**
-     * Gets the scaling the persister uses for low resolution task snapshots.
-     *
-     * @return the lowResBitmap scale of task snapshots when they are set to be low res
+     * Not to be used. Only here for testing.
      */
-    float getLowResScale() {
-        return mLowResScale;
+    @VisibleForTesting
+    void setEnableLowResSnapshots(boolean enabled) {
+        mEnableLowResSnapshots = enabled;
     }
 
     /**
@@ -213,14 +231,10 @@
     }
 
     File getHighResolutionBitmapFile(int taskId, int userId) {
-        // Full sized bitmaps are disabled on low ram devices
-        if (DISABLE_HIGH_RES_BITMAPS) {
-            Slog.wtf(TAG, "This device does not support full sized resolution bitmaps.");
-            return null;
-        }
         return new File(getDirectory(userId), taskId + BITMAP_EXTENSION);
     }
 
+    @NonNull
     File getLowResolutionBitmapFile(int taskId, int userId) {
         return new File(getDirectory(userId), taskId + LOW_RES_FILE_POSTFIX + BITMAP_EXTENSION);
     }
@@ -234,11 +248,11 @@
         final File protoFile = getProtoFile(taskId, userId);
         final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId);
         protoFile.delete();
-        bitmapLowResFile.delete();
-
-        // Low ram devices do not have a full sized file to delete
-        if (!DISABLE_HIGH_RES_BITMAPS) {
-            final File bitmapFile = getHighResolutionBitmapFile(taskId, userId);
+        if (bitmapLowResFile.exists()) {
+            bitmapLowResFile.delete();
+        }
+        final File bitmapFile = getHighResolutionBitmapFile(taskId, userId);
+        if (bitmapFile.exists()) {
             bitmapFile.delete();
         }
     }
@@ -343,6 +357,8 @@
             final TaskSnapshotProto proto = new TaskSnapshotProto();
             proto.orientation = mSnapshot.getOrientation();
             proto.rotation = mSnapshot.getRotation();
+            proto.taskWidth = mSnapshot.getTaskSize().x;
+            proto.taskHeight = mSnapshot.getTaskSize().y;
             proto.insetLeft = mSnapshot.getContentInsets().left;
             proto.insetTop = mSnapshot.getContentInsets().top;
             proto.insetRight = mSnapshot.getContentInsets().right;
@@ -352,7 +368,6 @@
             proto.systemUiVisibility = mSnapshot.getSystemUiVisibility();
             proto.isTranslucent = mSnapshot.isTranslucent();
             proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString();
-            proto.scale = mSnapshot.getScale();
             proto.id = mSnapshot.getId();
             final byte[] bytes = TaskSnapshotProto.toByteArray(proto);
             final File file = getProtoFile(mTaskId, mUserId);
@@ -379,11 +394,26 @@
             }
 
             final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */);
-            final Bitmap lowResBitmap = mSnapshot.isLowResolution()
-                    ? swBitmap
-                    : Bitmap.createScaledBitmap(swBitmap,
-                            (int) (bitmap.getWidth() * mLowResScale),
-                            (int) (bitmap.getHeight() * mLowResScale), true /* filter */);
+
+            final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
+            try {
+                FileOutputStream fos = new FileOutputStream(file);
+                swBitmap.compress(JPEG, QUALITY, fos);
+                fos.close();
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
+                return false;
+            }
+
+            if (!enableLowResSnapshots()) {
+                swBitmap.recycle();
+                return true;
+            }
+
+            final Bitmap lowResBitmap = Bitmap.createScaledBitmap(swBitmap,
+                    (int) (bitmap.getWidth() * mLowResScaleFactor),
+                    (int) (bitmap.getHeight() * mLowResScaleFactor), true /* filter */);
+            swBitmap.recycle();
 
             final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId);
             try {
@@ -396,22 +426,6 @@
             }
             lowResBitmap.recycle();
 
-            // For snapshots with lowResBitmap resolution, do not create or save full sized bitmaps
-            if (mSnapshot.isLowResolution()) {
-                swBitmap.recycle();
-                return true;
-            }
-
-            final File file = getHighResolutionBitmapFile(mTaskId, mUserId);
-            try {
-                FileOutputStream fos = new FileOutputStream(file);
-                swBitmap.compress(JPEG, QUALITY, fos);
-                fos.close();
-            } catch (IOException e) {
-                Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
-                return false;
-            }
-            swBitmap.recycle();
             return true;
         }
     }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index f4e4245..eb005e0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -36,6 +36,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+
 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.getColorViewLeftInset;
@@ -53,9 +54,11 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -131,6 +134,8 @@
     private final Rect mContentInsets = new Rect();
     private final Rect mFrame = new Rect();
     private TaskSnapshot mSnapshot;
+    private final RectF mTmpSnapshotSize = new RectF();
+    private final RectF mTmpDstFrame = new RectF();
     private final CharSequence mTitle;
     private boolean mHasDrawn;
     private long mShownTime;
@@ -141,6 +146,8 @@
     @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
     private final int mOrientationOnCreation;
     private final SurfaceControl.Transaction mTransaction;
+    private final Matrix mSnapshotMatrix = new Matrix();
+    private final float[] mTmpFloat9 = new float[9];
 
     static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity,
             TaskSnapshot snapshot) {
@@ -365,13 +372,17 @@
             frame = calculateSnapshotFrame(crop);
             mTransaction.setWindowCrop(mChildSurfaceControl, crop);
             mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
+            mTmpDstFrame.set(frame);
         } else {
             frame = null;
+            mTmpDstFrame.set(mFrame);
         }
 
         // Scale the mismatch dimensions to fill the task bounds
-        final float scale = 1 / mSnapshot.getScale();
-        mTransaction.setMatrix(mChildSurfaceControl, scale, 0, 0, scale);
+        mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
+        mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
+        mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9);
+
         mTransaction.apply();
         surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
         surface.release();
@@ -395,13 +406,17 @@
         rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
         final Rect insets = mSnapshot.getContentInsets();
 
+        final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x;
+        final float scaleY =
+                (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y;
+
         // Let's remove all system decorations except the status bar, but only if the task is at the
         // very top of the screen.
         final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
-        rect.inset((int) (insets.left * mSnapshot.getScale()),
-                isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
-                (int) (insets.right * mSnapshot.getScale()),
-                (int) (insets.bottom * mSnapshot.getScale()));
+        rect.inset((int) (insets.left * scaleX),
+                isTop ? 0 : (int) (insets.top * scaleY),
+                (int) (insets.right * scaleX),
+                (int) (insets.bottom * scaleY));
         return rect;
     }
 
@@ -412,14 +427,20 @@
      */
     @VisibleForTesting
     Rect calculateSnapshotFrame(Rect crop) {
-        final Rect frame = new Rect(crop);
-        final float scale = mSnapshot.getScale();
+        final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x;
+        final float scaleY =
+                (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y;
 
         // Rescale the frame from snapshot to window coordinate space
-        frame.scale(1 / scale);
+        final Rect frame = new Rect(
+                (int) (crop.left / scaleX + 0.5f),
+                (int) (crop.top / scaleY + 0.5f),
+                (int) (crop.right / scaleX + 0.5f),
+                (int) (crop.bottom / scaleY + 0.5f)
+        );
 
         // By default, offset it to to top/left corner
-        frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale));
+        frame.offsetTo((int) (-crop.left / scaleX), (int) (-crop.top / scaleY));
 
         // However, we also need to make space for the navigation bar on the left side.
         final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7a4d0b0..aaaabf2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -524,13 +524,6 @@
 
         if (mSurfaceControl != null) {
             getPendingTransaction().remove(mSurfaceControl);
-
-            // Merge to parent transaction to ensure the transactions on this WindowContainer are
-            // applied in native even if WindowContainer is removed.
-            if (mParent != null) {
-                mParent.getPendingTransaction().merge(getPendingTransaction());
-            }
-
             setSurfaceControl(null);
             mLastSurfacePosition.set(0, 0);
             scheduleAnimation();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 9cb5ba7..e0f8f0e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8075,32 +8075,39 @@
     public void getWindowInsets(WindowManager.LayoutParams attrs,
             int displayId, Rect outContentInsets, Rect outStableInsets,
             DisplayCutout.ParcelableWrapper displayCutout) {
-        synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
-            if (dc == null) {
-                throw new WindowManager.InvalidDisplayException("Display#" + displayId
-                        + "could not be found!");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent dc = getDisplayContentOrCreate(displayId, attrs.token);
+                if (dc == null) {
+                    throw new WindowManager.InvalidDisplayException("Display#" + displayId
+                            + "could not be found!");
+                }
+                final WindowToken windowToken = dc.getWindowToken(attrs.token);
+                final ActivityRecord activity;
+                if (windowToken != null && windowToken.asActivityRecord() != null) {
+                    activity = windowToken.asActivityRecord();
+                } else {
+                    activity = null;
+                }
+                final Rect taskBounds;
+                final boolean floatingStack;
+                if (activity != null && activity.getTask() != null) {
+                    final Task task = activity.getTask();
+                    taskBounds = new Rect();
+                    task.getBounds(taskBounds);
+                    floatingStack = task.isFloating();
+                } else {
+                    taskBounds = null;
+                    floatingStack = false;
+                }
+                final DisplayFrames displayFrames = dc.mDisplayFrames;
+                final DisplayPolicy policy = dc.getDisplayPolicy();
+                policy.getLayoutHintLw(attrs, taskBounds, displayFrames, floatingStack,
+                        new Rect(), outContentInsets, outStableInsets, displayCutout);
             }
-            final WindowToken windowToken = dc.getWindowToken(attrs.token);
-            final ActivityRecord activity;
-            if (windowToken != null && windowToken.asActivityRecord() != null) {
-                activity = windowToken.asActivityRecord();
-            } else {
-                activity = null;
-            }
-            final Rect taskBounds = new Rect();
-            final boolean floatingStack;
-            if (activity != null && activity.getTask() != null) {
-                final Task task = activity.getTask();
-                task.getBounds(taskBounds);
-                floatingStack = task.isFloating();
-            } else {
-                floatingStack = false;
-            }
-            final DisplayFrames displayFrames = dc.mDisplayFrames;
-            final DisplayPolicy policy = dc.getDisplayPolicy();
-            policy.getLayoutHintLw(attrs, taskBounds, displayFrames, floatingStack,
-                    new Rect(), outContentInsets, outStableInsets, displayCutout);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b250083..37597fb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1078,15 +1078,6 @@
             }
 
             final ActivityStack stack = getRootTask();
-            if (inPinnedWindowingMode() && stack != null
-                    && stack.lastAnimatingBoundsWasToFullscreen()) {
-                // PIP edge case: When going from pinned to fullscreen, we apply a
-                // tempInsetFrame for the full task - but we're still at the start of the animation.
-                // To prevent a jump if there's a letterbox, restrict to the parent frame.
-                mInsetFrame.intersectUnchecked(windowFrames.mParentFrame);
-                windowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame);
-            }
-
             layoutDisplayFrame = new Rect(windowFrames.mDisplayFrame);
             windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
             layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
@@ -1343,16 +1334,6 @@
             return;
         }
 
-        final Task task = getTask();
-        // In the case of stack bound animations, the window frames will update (unlike other
-        // animations which just modify various transformation properties). We don't want to
-        // notify the client of frame changes in this case. Not only is it a lot of churn, but
-        // the frame may not correspond to the surface size or the onscreen area at various
-        // phases in the animation, and the client will become sad and confused.
-        if (task != null && task.getStack().isAnimatingBounds()) {
-            return;
-        }
-
         boolean didFrameInsetsChange = setReportResizeHints();
         boolean configChanged = !isLastConfigReportedToClient();
         if (DEBUG_CONFIGURATION && configChanged) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index a1a9af6..81d0e3e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -902,15 +902,9 @@
             boolean allowStretching = false;
             task.getStack().getFinalAnimationSourceHintBounds(mTmpSourceBounds);
             // If we don't have source bounds, we can attempt to use the content insets
-            // in the following scenario:
-            //    1. We have content insets.
-            //    2. We are not transitioning to full screen
-            // We have to be careful to check "lastAnimatingBoundsWasToFullscreen" rather than
-            // the mBoundsAnimating state, as we may have already left it and only be here
-            // because of the force-scale until resize state.
+            // if we have content insets.
             if (mTmpSourceBounds.isEmpty() && (mWin.mLastRelayoutContentInsets.width() > 0
-                    || mWin.mLastRelayoutContentInsets.height() > 0)
-                        && !task.getStack().lastAnimatingBoundsWasToFullscreen()) {
+                    || mWin.mLastRelayoutContentInsets.height() > 0)) {
                 mTmpSourceBounds.set(task.getStack().mPreAnimationBounds);
                 mTmpSourceBounds.inset(mWin.mLastRelayoutContentInsets);
                 allowStretching = true;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index e888f2a..27bd58e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -24,6 +24,7 @@
         "BroadcastRadio/regions.cpp",
         "stats/PowerStatsPuller.cpp",
         "stats/SubsystemSleepStatePuller.cpp",
+        "com_android_server_adb_AdbDebuggingManager.cpp",
         "com_android_server_am_BatteryStatsService.cpp",
         "com_android_server_connectivity_Vpn.cpp",
         "com_android_server_ConsumerIrService.cpp",
@@ -86,6 +87,8 @@
 cc_defaults {
     name: "libservices.core-libs",
     shared_libs: [
+        "libadb_pairing_server",
+        "libadb_pairing_connection",
         "libandroid_runtime",
         "libandroidfw",
         "libaudioclient",
diff --git a/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
new file mode 100644
index 0000000..9c834aa
--- /dev/null
+++ b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "AdbDebuggingManager-JNI"
+
+#define LOG_NDEBUG 0
+
+#include <algorithm>
+#include <condition_variable>
+#include <mutex>
+#include <optional>
+#include <random>
+#include <string>
+#include <vector>
+
+#include <adb/pairing/pairing_server.h>
+#include <android-base/properties.h>
+#include <utils/Log.h>
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+namespace {
+
+template <class T, class N>
+class JSmartWrapper {
+public:
+    JSmartWrapper(JNIEnv* env, T* jData) : mEnv(env), mJData(jData) {}
+
+    virtual ~JSmartWrapper() = default;
+
+    const N* data() const { return mRawData; }
+
+    jsize size() const { return mSize; }
+
+protected:
+    N* mRawData = nullptr;
+    JNIEnv* mEnv = nullptr;
+    T* mJData = nullptr;
+    jsize mSize = 0;
+}; // JSmartWrapper
+
+class JStringUTFWrapper : public JSmartWrapper<jstring, const char> {
+public:
+    explicit JStringUTFWrapper(JNIEnv* env, jstring* str) : JSmartWrapper(env, str) {
+        mRawData = env->GetStringUTFChars(*str, NULL);
+        mSize = env->GetStringUTFLength(*str);
+    }
+
+    virtual ~JStringUTFWrapper() {
+        if (data()) {
+            mEnv->ReleaseStringUTFChars(*mJData, mRawData);
+        }
+    }
+}; // JStringUTFWrapper
+
+struct ServerDeleter {
+    void operator()(PairingServerCtx* p) { pairing_server_destroy(p); }
+};
+using PairingServerPtr = std::unique_ptr<PairingServerCtx, ServerDeleter>;
+struct PairingResultWaiter {
+    std::mutex mutex_;
+    std::condition_variable cv_;
+    std::optional<bool> is_valid_;
+    PeerInfo peer_info_;
+
+    static void ResultCallback(const PeerInfo* peer_info, void* opaque) {
+        auto* p = reinterpret_cast<PairingResultWaiter*>(opaque);
+        {
+            std::unique_lock<std::mutex> lock(p->mutex_);
+            if (peer_info) {
+                memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
+            }
+            p->is_valid_ = (peer_info != nullptr);
+        }
+        p->cv_.notify_one();
+    }
+};
+
+PairingServerPtr sServer;
+std::unique_ptr<PairingResultWaiter> sWaiter;
+} // namespace
+
+static jint native_pairing_start(JNIEnv* env, jobject thiz, jstring guid, jstring password) {
+    // Server-side only sends its GUID on success.
+    PeerInfo system_info = {};
+    system_info.type = ADB_DEVICE_GUID;
+    JStringUTFWrapper guidWrapper(env, &guid);
+    memcpy(system_info.data, guidWrapper.data(), guidWrapper.size());
+
+    JStringUTFWrapper passwordWrapper(env, &password);
+
+    // Create the pairing server
+    sServer = PairingServerPtr(
+            pairing_server_new_no_cert(reinterpret_cast<const uint8_t*>(passwordWrapper.data()),
+                                       passwordWrapper.size(), &system_info, 0));
+
+    sWaiter.reset(new PairingResultWaiter);
+    uint16_t port = pairing_server_start(sServer.get(), sWaiter->ResultCallback, sWaiter.get());
+    if (port == 0) {
+        ALOGE("Failed to start pairing server");
+        return -1;
+    }
+
+    return port;
+}
+
+static void native_pairing_cancel(JNIEnv* /* env */, jclass /* clazz */) {
+    if (sServer != nullptr) {
+        sServer.reset();
+    }
+}
+
+static jboolean native_pairing_wait(JNIEnv* env, jobject thiz) {
+    ALOGI("Waiting for pairing server to complete");
+    std::unique_lock<std::mutex> lock(sWaiter->mutex_);
+    if (!sWaiter->is_valid_.has_value()) {
+        sWaiter->cv_.wait(lock, [&]() { return sWaiter->is_valid_.has_value(); });
+    }
+    if (!*(sWaiter->is_valid_)) {
+        return JNI_FALSE;
+    }
+
+    std::string peer_public_key = reinterpret_cast<char*>(sWaiter->peer_info_.data);
+    // Write to PairingThread's member variables
+    jclass clazz = env->GetObjectClass(thiz);
+    jfieldID mPublicKey = env->GetFieldID(clazz, "mPublicKey", "Ljava/lang/String;");
+    jstring jpublickey = env->NewStringUTF(peer_public_key.c_str());
+    env->SetObjectField(thiz, mPublicKey, jpublickey);
+    return JNI_TRUE;
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gPairingThreadMethods[] = {
+        /* name, signature, funcPtr */
+        {"native_pairing_start", "(Ljava/lang/String;Ljava/lang/String;)I",
+         (void*)native_pairing_start},
+        {"native_pairing_cancel", "()V", (void*)native_pairing_cancel},
+        {"native_pairing_wait", "()Z", (void*)native_pairing_wait},
+};
+
+int register_android_server_AdbDebuggingManager(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env,
+                                       "com/android/server/adb/AdbDebuggingManager$PairingThread",
+                                       gPairingThreadMethods, NELEM(gPairingThreadMethods));
+    (void)res; // Faked use when LOG_NDEBUG.
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+    return 0;
+}
+
+} /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index f9238e3..a5339a5 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -61,6 +61,7 @@
 int register_android_server_incremental_IncrementalManagerService(JNIEnv* env);
 int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
 int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
+int register_android_server_AdbDebuggingManager(JNIEnv* env);
 };
 
 using namespace android;
@@ -115,5 +116,6 @@
     register_android_server_incremental_IncrementalManagerService(env);
     register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
     register_android_server_stats_pull_StatsPullAtomService(env);
+    register_android_server_AdbDebuggingManager(env);
     return JNI_VERSION_1_4;
 }
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 2499614..5d7f4e9 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -71,12 +71,12 @@
     }
 
     @Override
-    public void onUnlockUser(@NonNull TargetUser targetUser) {
+    public void onUserUnlocking(@NonNull TargetUser targetUser) {
         mDataManager.onUserUnlocked(targetUser.getUserIdentifier());
     }
 
     @Override
-    public void onStopUser(@NonNull TargetUser targetUser) {
+    public void onUserStopping(@NonNull TargetUser targetUser) {
         mDataManager.onUserStopped(targetUser.getUserIdentifier());
     }
 
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index 35d245f..3e4c992 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.LocusId;
+import android.os.FileUtils;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 
@@ -251,5 +252,6 @@
     void onDestroy() {
         mEventStore.onDestroy();
         mConversationStore.onDestroy();
+        FileUtils.deleteContentsAndDir(mPackageDataDir);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index 5a1ad86..757a2b1 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -696,6 +696,31 @@
                 mAdbKeyXmlFile.exists());
     }
 
+    @Test
+    public void testAdbKeyStore_removeKey() throws Exception {
+        // Accept the test key with the 'Always allow' option selected.
+        runAdbTest(TEST_KEY_1, true, true, false);
+        runAdbTest(TEST_KEY_2, true, true, false);
+
+        // Set the connection time to 0 to restore the original behavior.
+        setAllowedConnectionTime(0);
+
+        // Verify that the key is in the adb_keys file to ensure subsequent connections are
+        // automatically allowed by adbd.
+        persistKeyStore();
+        assertTrue("The key was not in the adb_keys file after persisting the keystore",
+                isKeyInFile(TEST_KEY_1, mAdbKeyFile));
+        assertTrue("The key was not in the adb_keys file after persisting the keystore",
+                isKeyInFile(TEST_KEY_2, mAdbKeyFile));
+
+        // Now remove one of the keys and make sure the other key is still there
+        mKeyStore.removeKey(TEST_KEY_1);
+        assertFalse("The key was still in the adb_keys file after removing the key",
+                isKeyInFile(TEST_KEY_1, mAdbKeyFile));
+        assertTrue("The key was not in the adb_keys file after removing a different key",
+                isKeyInFile(TEST_KEY_2, mAdbKeyFile));
+    }
+
     /**
      * Runs an adb test with the provided configuration.
      *
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index d2ec500..a19d919 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -430,13 +430,6 @@
             ParsedProvider b
     ) {
         assertComponentsEqual(a, b);
-
-        // Sanity check for ProviderInfo
-        ProviderInfo aInfo = PackageInfoUtils.generateProviderInfo(aPkg, a, 0,
-                new PackageUserState(), 0, mockPkgSetting(aPkg));
-        ProviderInfo bInfo = PackageInfoUtils.generateProviderInfo(bPkg, b, 0,
-                new PackageUserState(), 0, mockPkgSetting(bPkg));
-        assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
         assertEquals(a.getName(), b.getName());
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index 30ab9cd..dc30add 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -117,7 +117,7 @@
 
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyVibration_WithAccessibilityEnabled() {
-        mBatterySaverPolicy.setAccessibilityEnabledForTest(true);
+        mBatterySaverPolicy.setAccessibilityEnabled(true);
         testServiceDefaultValue_Off(ServiceType.VIBRATION);
     }
 
@@ -339,4 +339,57 @@
                 Policy.fromSettings(BATTERY_SAVER_CONSTANTS, ""));
         verifyBatterySaverConstantsUpdated();
     }
+
+    public void testCarModeChanges_Full() {
+        mBatterySaverPolicy.updateConstantsLocked(
+                "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
+                        + ",enable_night_mode=true", "");
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_FULL);
+        assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+                .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+        assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+                ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+        mBatterySaverPolicy.setCarModeEnabled(true);
+
+        assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+                .isAnyOf(PowerManager.LOCATION_MODE_NO_CHANGE,
+                        PowerManager.LOCATION_MODE_FOREGROUND_ONLY);
+        assertFalse(mBatterySaverPolicy.getBatterySaverPolicy(
+                ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+        mBatterySaverPolicy.setCarModeEnabled(false);
+
+        assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+                .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+        assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+                ServiceType.NIGHT_MODE).batterySaverEnabled);
+    }
+
+    public void testCarModeChanges_Adaptive() {
+        mBatterySaverPolicy.setAdaptivePolicyLocked(
+                Policy.fromSettings(
+                        "gps_mode=" + PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF
+                                + ",enable_night_mode=true", ""));
+        mBatterySaverPolicy.setPolicyLevel(POLICY_LEVEL_ADAPTIVE);
+        assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+                .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+        assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+                ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+        mBatterySaverPolicy.setCarModeEnabled(true);
+
+        assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+                .isAnyOf(PowerManager.LOCATION_MODE_NO_CHANGE,
+                        PowerManager.LOCATION_MODE_FOREGROUND_ONLY);
+        assertFalse(mBatterySaverPolicy.getBatterySaverPolicy(
+                ServiceType.NIGHT_MODE).batterySaverEnabled);
+
+        mBatterySaverPolicy.setCarModeEnabled(false);
+
+        assertThat(mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.LOCATION).locationMode)
+                .isEqualTo(PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+        assertTrue(mBatterySaverPolicy.getBatterySaverPolicy(
+                ServiceType.NIGHT_MODE).batterySaverEnabled);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
new file mode 100644
index 0000000..192c6fe
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tv.tunerresourcemanager;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.media.tv.ITvInputManager;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.media.tv.tuner.frontend.FrontendSettings;
+import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
+import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Tests for {@link TunerResourceManagerService} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class TunerResourceManagerServiceTest {
+    private static final String TAG = "TunerResourceManagerServiceTest";
+    private Context mContextSpy;
+    @Mock private ITvInputManager mITvInputManagerMock;
+    private TunerResourceManagerService mTunerResourceManagerService;
+    private int mReclaimingId;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        TvInputManager tvInputManager = new TvInputManager(mITvInputManagerMock, 0);
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+        when(mContextSpy.getSystemService(Context.TV_INPUT_SERVICE)).thenReturn(tvInputManager);
+        mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy) {
+            @Override
+            protected void reclaimFrontendResource(int reclaimingId) {
+                mReclaimingId = reclaimingId;
+            }
+        };
+        mTunerResourceManagerService.onStart(true /*isForTesting*/);
+        mReclaimingId = -1;
+    }
+
+    @Test
+    public void setFrontendListTest_addFrontendResources_noExclusiveGroupId() {
+        // Init frontend resources.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+        infos[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+        infos[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+        SparseArray<FrontendResource> resources =
+                mTunerResourceManagerService.getFrontendResources();
+        assertThat(resources.size()).isEqualTo(infos.length);
+        for (int id = 0; id < infos.length; id++) {
+            FrontendResource fe = resources.get(infos[id].getId());
+            assertThat(fe.getId()).isEqualTo(infos[id].getId());
+            assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType());
+            assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId());
+            assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+        }
+    }
+
+    @Test
+    public void setFrontendListTest_addFrontendResources_underTheSameExclusiveGroupId() {
+        // Init frontend resources.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[4];
+        infos[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+        infos[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        infos[2] =
+                new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+        infos[3] =
+                new TunerFrontendInfo(3 /*id*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+        SparseArray<FrontendResource> resources =
+                mTunerResourceManagerService.getFrontendResources();
+        assertThat(resources.size()).isEqualTo(infos.length);
+        for (int id = 0; id < infos.length; id++) {
+            FrontendResource fe = resources.get(infos[id].getId());
+            assertThat(fe.getId()).isEqualTo(infos[id].getId());
+            assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType());
+            assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId());
+        }
+
+        assertThat(resources.get(0).getExclusiveGroupMemberFeIds())
+                .isEqualTo(new ArrayList<Integer>());
+        assertThat(resources.get(1).getExclusiveGroupMemberFeIds())
+                .isEqualTo(new ArrayList<Integer>(Arrays.asList(2, 3)));
+        assertThat(resources.get(2).getExclusiveGroupMemberFeIds())
+                .isEqualTo(new ArrayList<Integer>(Arrays.asList(1, 3)));
+        assertThat(resources.get(3).getExclusiveGroupMemberFeIds())
+                .isEqualTo(new ArrayList<Integer>(Arrays.asList(1, 2)));
+    }
+
+    @Test
+    public void setFrontendListTest_updateExistingFrontendResources() {
+        // Init frontend resources.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+        infos[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        infos[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+        SparseArray<FrontendResource> resources0 =
+                mTunerResourceManagerService.getFrontendResources();
+
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+        SparseArray<FrontendResource> resources1 =
+                mTunerResourceManagerService.getFrontendResources();
+
+        assertThat(resources0).isEqualTo(resources1);
+    }
+
+    @Test
+    public void setFrontendListTest_removeFrontendResources_noExclusiveGroupId() {
+        // Init frontend resources.
+        TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3];
+        infos0[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+        infos0[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        infos0[2] =
+                new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 2 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos0);
+
+        TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1];
+        infos1[0] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
+
+        SparseArray<FrontendResource> resources =
+                mTunerResourceManagerService.getFrontendResources();
+        assertThat(resources.size()).isEqualTo(infos1.length);
+        for (int id = 0; id < infos1.length; id++) {
+            FrontendResource fe = resources.get(infos1[id].getId());
+            assertThat(fe.getId()).isEqualTo(infos1[id].getId());
+            assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType());
+            assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId());
+            assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+        }
+    }
+
+    @Test
+    public void setFrontendListTest_removeFrontendResources_underTheSameExclusiveGroupId() {
+        // Init frontend resources.
+        TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3];
+        infos0[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+        infos0[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        infos0[2] =
+                new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos0);
+
+        TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1];
+        infos1[0] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos1);
+
+        SparseArray<FrontendResource> resources =
+                mTunerResourceManagerService.getFrontendResources();
+        assertThat(resources.size()).isEqualTo(infos1.length);
+        for (int id = 0; id < infos1.length; id++) {
+            FrontendResource fe = resources.get(infos1[id].getId());
+            assertThat(fe.getId()).isEqualTo(infos1[id].getId());
+            assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType());
+            assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId());
+            assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+        }
+    }
+
+    @Test
+    public void requestFrontendTest_ClientNotRegistered() {
+        TunerFrontendRequest request =
+                new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
+        int[] frontendId = new int[1];
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isFalse();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+    }
+
+    @Test
+    public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() {
+        ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        int[] clientId = new int[1];
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profile, null /*listener*/, clientId);
+        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+        // Init frontend resources.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[1];
+        infos[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBS, 0 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+        TunerFrontendRequest request =
+                new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+        int[] frontendId = new int[1];
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isFalse();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+    }
+
+    @Test
+    public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() {
+        ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        int[] clientId = new int[1];
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profile, null /*listener*/, clientId);
+        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+        // Init frontend resources.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
+        infos[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+        infos[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        infos[2] =
+                new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+        TunerFrontendRequest request =
+                new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+        int[] frontendId = new int[1];
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isTrue();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        assertThat(frontendId[0]).isEqualTo(0);
+    }
+
+    @Test
+    public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() {
+        ResourceClientProfile profile0 = new ResourceClientProfile("0" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        ResourceClientProfile profile1 = new ResourceClientProfile("1" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        int[] clientId0 = new int[1];
+        int[] clientId1 = new int[1];
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profile0, null /*listener*/, clientId0);
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profile1, null /*listener*/, clientId1);
+        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+        // Init frontend resources.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
+        infos[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
+        infos[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        infos[2] =
+                new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+        int[] frontendId = new int[1];
+        TunerFrontendRequest request =
+                new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isTrue();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+
+        request =
+                new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isTrue();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+        assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+                .isInUse()).isTrue();
+        assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[2].getId())
+                .isInUse()).isTrue();
+    }
+
+    @Test
+    public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() {
+        // Register clients
+        ResourceClientProfile[] profiles = new ResourceClientProfile[2];
+        profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        int[] clientPriorities = {100, 50};
+        int[] clientId0 = new int[1];
+        int[] clientId1 = new int[1];
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profiles[0], null /*listener*/, clientId0);
+        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        mTunerResourceManagerService.getClientProfiles().get(clientId0[0])
+                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profiles[1], null /*listener*/, clientId1);
+        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        mTunerResourceManagerService.getClientProfiles().get(clientId1[0])
+                .setPriority(clientPriorities[1]);
+
+        // Init frontend resources.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+        infos[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        infos[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+        TunerFrontendRequest request =
+                new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+        int[] frontendId = new int[1];
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isTrue();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        request =
+                new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isFalse();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        assertThat(mReclaimingId).isEqualTo(-1);
+
+        request =
+                new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isFalse();
+            assertThat(mReclaimingId).isEqualTo(-1);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Test
+    public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() {
+        // Register clients
+        ResourceClientProfile[] profiles = new ResourceClientProfile[2];
+        profiles[0] = new ResourceClientProfile("0" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        profiles[1] = new ResourceClientProfile("1" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        int[] clientPriorities = {100, 500};
+        int[] clientId0 = new int[1];
+        int[] clientId1 = new int[1];
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profiles[0], null /*listener*/, clientId0);
+        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        mTunerResourceManagerService.getClientProfiles().get(clientId0[0])
+                .setPriority(clientPriorities[0]);
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profiles[1], null /*listener*/, clientId1);
+        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        mTunerResourceManagerService.getClientProfiles().get(clientId1[0])
+                .setPriority(clientPriorities[1]);
+
+        // Init frontend resources.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+        infos[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        infos[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+        TunerFrontendRequest request =
+                new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+        int[] frontendId = new int[1];
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isTrue();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+
+        request =
+                new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isTrue();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+        assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId())
+                .isInUse()).isTrue();
+        assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+                .isInUse()).isTrue();
+        assertThat(mTunerResourceManagerService.getFrontendResources()
+                .get(infos[0].getId()).getOwnerClientId()).isEqualTo(clientId1[0]);
+        assertThat(mTunerResourceManagerService.getFrontendResources()
+                .get(infos[1].getId()).getOwnerClientId()).isEqualTo(clientId1[0]);
+        assertThat(mReclaimingId).isEqualTo(clientId0[0]);
+    }
+
+    @Test
+    public void unregisterClientTest_usingFrontend() {
+        // Register client
+        ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        int[] clientId = new int[1];
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profile, null /*listener*/, clientId);
+        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+        // Init frontend resources.
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
+        infos[0] =
+                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
+        infos[1] =
+                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+        mTunerResourceManagerService.setFrontendInfoListInternal(infos);
+
+        TunerFrontendRequest request =
+                new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+        int[] frontendId = new int[1];
+        try {
+            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
+                    .isTrue();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+        assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId())
+                .isInUse()).isTrue();
+        assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+                .isInUse()).isTrue();
+
+        // Unregister client when using frontend
+        mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]);
+        assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId())
+                .isInUse()).isFalse();
+        assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId())
+                .isInUse()).isFalse();
+
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java
new file mode 100644
index 0000000..ab5665b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tv.tunerresourcemanager;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.media.tv.TvInputService;
+import android.util.Slog;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests for {@link UseCasePriorityHints} class.
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class UseCasePriorityHintsTest {
+    private static final String TAG = "UseCasePriorityHintsTest";
+    private UseCasePriorityHints mPriorityHints;
+
+    private final String mExampleXML =
+            "<!-- A sample Use Case Priority Hints xml -->"
+            + "<config version=\"1.0\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">"
+            + "<useCaseDefault fgPriority=\"150\" bgPriority=\"50\"/>"
+            + "<useCasePreDefined type=\"USE_CASE_RECORD\" fgPriority=\"600\" bgPriority=\"500\"/>"
+            + "<useCasePreDefined type=\"USE_CASE_LIVE\" fgPriority=\"490\" bgPriority=\"400\"/>"
+            + "<useCasePreDefined type=\"USE_CASE_PLAYBACK\" fgPriority=\"480\""
+            + " bgPriority=\"300\"/>"
+            + "<useCasePreDefined type=\"USE_CASE_BACKGROUND\" fgPriority=\"180\""
+            + " bgPriority=\"100\"/>"
+            + "<useCaseVendor type=\"VENDOR_USE_CASE_1\" id=\"1001\" fgPriority=\"300\""
+            + " bgPriority=\"80\"/>"
+            + "</config>";
+
+    @Before
+    public void setUp() throws Exception {
+        mPriorityHints = new UseCasePriorityHints();
+        try {
+            mPriorityHints.parseInternal(
+                    new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8)));
+        } catch (IOException | XmlPullParserException e) {
+            Slog.e(TAG, "Error parse xml.", e);
+        }
+    }
+
+    @Test
+    public void parseTest_parseSampleXml() {
+        // Pre-defined foreground
+        assertThat(mPriorityHints.getForegroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(180);
+        assertThat(mPriorityHints.getForegroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(150);
+        assertThat(mPriorityHints.getForegroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(480);
+        assertThat(mPriorityHints.getForegroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(490);
+        assertThat(mPriorityHints.getForegroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(600);
+
+        // Pre-defined background
+        assertThat(mPriorityHints.getBackgroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(100);
+        assertThat(mPriorityHints.getBackgroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(50);
+        assertThat(mPriorityHints.getBackgroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(300);
+        assertThat(mPriorityHints.getBackgroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(400);
+        assertThat(mPriorityHints.getBackgroundPriority(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(500);
+
+        // Vendor use case
+        assertThat(mPriorityHints.getForegroundPriority(1001)).isEqualTo(300);
+        assertThat(mPriorityHints.getBackgroundPriority(1001)).isEqualTo(80);
+    }
+
+    @Test
+    public void isDefinedUseCaseTest_invalidUseCase() {
+        assertThat(mPriorityHints.isDefinedUseCase(1992)).isFalse();
+    }
+
+    @Test
+    public void isDefinedUseCaseTest_validUseCase() {
+        assertThat(mPriorityHints.isDefinedUseCase(1001)).isTrue();
+        assertThat(mPriorityHints.isDefinedUseCase(
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isTrue();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 0fdffd5..23613e0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -76,6 +76,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.SystemService;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -87,6 +88,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -127,6 +129,15 @@
     private MyInjector mInjector;
     private AppStandbyController mController;
 
+    private CountDownLatch mStateChangedLatch = new CountDownLatch(1);
+    private AppIdleStateChangeListener mListener = new AppIdleStateChangeListener() {
+        @Override
+        public void onAppIdleStateChanged(String packageName, int userId,
+                boolean idle, int bucket, int reason) {
+            mStateChangedLatch.countDown();
+        }
+    };
+
     static class MyContextWrapper extends ContextWrapper {
         PackageManager mockPm = mock(PackageManager.class);
 
@@ -156,6 +167,7 @@
         String mBoundWidgetPackage = PACKAGE_EXEMPTED_1;
         int[] mRunningUsers = new int[] {USER_ID};
         List<UserHandle> mCrossProfileTargets = Collections.emptyList();
+        boolean mDeviceIdleMode = false;
 
         MyInjector(Context context, Looper looper) {
             super(context, looper);
@@ -251,7 +263,7 @@
 
         @Override
         public boolean isDeviceIdleMode() {
-            return false;
+            return mDeviceIdleMode;
         }
 
         @Override
@@ -327,6 +339,7 @@
                 controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
                         mInjector.mElapsedRealtime, false));
 
+        controller.addListener(mListener);
         return controller;
     }
 
@@ -1055,6 +1068,46 @@
                 STANDBY_BUCKET_WORKING_SET, getStandbyBucket(USER_ID2, mController, PACKAGE_1));
     }
 
+    @Test
+    public void testUnexemptedSyncScheduled() throws Exception {
+        mStateChangedLatch = new CountDownLatch(1);
+        mController.addListener(mListener);
+        assertEquals("Test package did not start in the Never bucket", STANDBY_BUCKET_NEVER,
+                getStandbyBucket(mController, PACKAGE_1));
+
+        mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false);
+        mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+        assertEquals("Unexempted sync scheduled should bring the package out of the Never bucket",
+                STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+        setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
+
+        mStateChangedLatch = new CountDownLatch(1);
+        mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false);
+        mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+        assertEquals("Unexempted sync scheduled should not elevate a non Never bucket",
+                STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+    }
+
+    @Test
+    public void testExemptedSyncScheduled() throws Exception {
+        setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
+        mInjector.mDeviceIdleMode = true;
+        mStateChangedLatch = new CountDownLatch(1);
+        mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true);
+        mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+        assertEquals("Exempted sync scheduled in doze should set bucket to working set",
+                STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
+
+        setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM);
+        mInjector.mDeviceIdleMode = false;
+        mStateChangedLatch = new CountDownLatch(1);
+        mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true);
+        mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+        assertEquals("Exempted sync scheduled while not in doze should set bucket to active",
+                STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
+    }
+
     private String getAdminAppsStr(int userId) {
         return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
     }
@@ -1095,4 +1148,12 @@
     private void setActiveAdmins(int userId, String... admins) {
         mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId);
     }
+
+    private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception {
+        mStateChangedLatch = new CountDownLatch(1);
+        mController.setAppStandbyBucket(pkg, user, bucket, reason);
+        mStateChangedLatch.await(100, TimeUnit.MILLISECONDS);
+        assertEquals("Failed to set package bucket", bucket,
+                getStandbyBucket(mController, PACKAGE_1));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index b917e1b..049c8e1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -31,7 +31,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
@@ -50,7 +49,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -139,39 +137,6 @@
     }
 
     @Test
-    public void testUpdateLaunchBounds() {
-        // When in a non-resizeable stack, the task bounds should be updated.
-        final Task task = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
-                .build();
-        final Rect bounds = new Rect(10, 10, 100, 100);
-
-        mStarter.updateBounds(task, bounds);
-        assertEquals(bounds, task.getRequestedOverrideBounds());
-        assertEquals(new Rect(), task.getStack().getRequestedOverrideBounds());
-
-        // When in a resizeable stack, the stack bounds should be updated as well.
-        final Task task2 = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack(
-                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
-                .build();
-        assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class);
-        mStarter.updateBounds(task2, bounds);
-
-        verify(mService, times(1)).animateResizePinnedStack(eq(task2.getRootTaskId()),
-                eq(bounds), anyInt());
-
-        // In the case of no animation, the stack and task bounds should be set immediately.
-        if (!ANIMATE) {
-            assertEquals(bounds, task2.getStack().getRequestedOverrideBounds());
-            assertEquals(bounds, task2.getRequestedOverrideBounds());
-        } else {
-            assertEquals(new Rect(), task2.getRequestedOverrideBounds());
-        }
-    }
-
-    @Test
     public void testStartActivityPreconditions() {
         verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED);
         verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT,
@@ -383,7 +348,7 @@
         // Never review permissions
         doReturn(false).when(mMockPackageManager).isPermissionsReviewRequired(any(), anyInt());
         doNothing().when(mMockPackageManager).grantImplicitAccess(
-                anyInt(), any(), anyInt(), anyInt());
+                anyInt(), any(), anyInt(), anyInt(), anyBoolean());
         doNothing().when(mMockPackageManager).notifyPackageUse(anyString(), anyInt());
 
         final Intent intent = new Intent();
diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
deleted file mode 100644
index 1dda535..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ /dev/null
@@ -1,627 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.server.wm.BoundsAnimationController.BOUNDS;
-import static com.android.server.wm.BoundsAnimationController.FADE_IN;
-import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
-import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
-import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.wm.BoundsAnimationController.BoundsAnimator;
-import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
- * depending on the various interactions.
- *
- * We are really concerned about only three of the transition states [F = fullscreen, !F = floating]
- * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition,
- * or if a new animation starts on the same target.  The tests below verifies that the target is
- * notified of all the cases where it is animating and cancelled so that it can respond
- * appropriately.
- *
- * Build/Install/Run:
- *  atest WmTests:BoundsAnimationControllerTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class BoundsAnimationControllerTests extends WindowTestsBase {
-
-    /**
-     * Mock value animator to simulate updates with.
-     */
-    private static class MockValueAnimator extends ValueAnimator {
-
-        private float mFraction;
-
-        MockValueAnimator getWithValue(float fraction) {
-            mFraction = fraction;
-            return this;
-        }
-
-        @Override
-        public Object getAnimatedValue() {
-            return mFraction;
-        }
-    }
-
-    /**
-     * Mock app transition to fire notifications to the bounds animator.
-     */
-    private static class MockAppTransition extends AppTransition {
-
-        private AppTransitionListener mListener;
-
-        MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) {
-            super(context, wm, displayContent);
-        }
-
-        @Override
-        void registerListenerLocked(AppTransitionListener listener) {
-            mListener = listener;
-        }
-
-        public void notifyTransitionPending() {
-            mListener.onAppTransitionPendingLocked();
-        }
-
-        public void notifyTransitionCancelled(int transit) {
-            mListener.onAppTransitionCancelledLocked(transit);
-        }
-
-        public void notifyTransitionStarting(int transit) {
-            mListener.onAppTransitionStartingLocked(transit, 0, 0, 0);
-        }
-
-        public void notifyTransitionFinished() {
-            mListener.onAppTransitionFinishedLocked(null);
-        }
-    }
-
-    /**
-     * A test animate bounds user to track callbacks from the bounds animation.
-     */
-    private static class TestBoundsAnimationTarget implements BoundsAnimationTarget {
-
-        boolean mAwaitingAnimationStart;
-        boolean mMovedToFullscreen;
-        boolean mAnimationStarted;
-        boolean mSchedulePipModeChangedOnStart;
-        boolean mForcePipModeChangedCallback;
-        boolean mAnimationEnded;
-        Rect mAnimationEndFinalStackBounds;
-        boolean mSchedulePipModeChangedOnEnd;
-        boolean mBoundsUpdated;
-        boolean mCancelRequested;
-        Rect mStackBounds;
-        Rect mTaskBounds;
-        float mAlpha;
-        @BoundsAnimationController.AnimationType int mAnimationType;
-
-        void initialize(Rect from) {
-            mAwaitingAnimationStart = true;
-            mMovedToFullscreen = false;
-            mAnimationStarted = false;
-            mAnimationEnded = false;
-            mAnimationEndFinalStackBounds = null;
-            mForcePipModeChangedCallback = false;
-            mSchedulePipModeChangedOnStart = false;
-            mSchedulePipModeChangedOnEnd = false;
-            mStackBounds = from;
-            mTaskBounds = null;
-            mBoundsUpdated = false;
-        }
-
-        @Override
-        public boolean onAnimationStart(boolean schedulePipModeChangedCallback,
-                boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType) {
-            mAwaitingAnimationStart = false;
-            mAnimationStarted = true;
-            mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
-            mForcePipModeChangedCallback = forceUpdate;
-            mAnimationType = animationType;
-            return true;
-        }
-
-        @Override
-        public boolean shouldDeferStartOnMoveToFullscreen() {
-            return true;
-        }
-
-        @Override
-        public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) {
-            // TODO: Once we break the runs apart, we should fail() here if this is called outside
-            //       of onAnimationStart() and onAnimationEnd()
-            if (mCancelRequested) {
-                mCancelRequested = false;
-                return false;
-            } else {
-                mBoundsUpdated = true;
-                mStackBounds = stackBounds;
-                mTaskBounds = taskBounds;
-                return true;
-            }
-        }
-
-        @Override
-        public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds,
-                boolean moveToFullscreen) {
-            mAnimationEnded = true;
-            mAnimationEndFinalStackBounds = finalStackBounds;
-            mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback;
-            mMovedToFullscreen = moveToFullscreen;
-            mTaskBounds = null;
-        }
-
-        @Override
-        public boolean setPinnedStackAlpha(float alpha) {
-            mAlpha = alpha;
-            return true;
-        }
-    }
-
-    /**
-     * Drives the animations, makes common assertions along the way.
-     */
-    private static class BoundsAnimationDriver {
-
-        private final BoundsAnimationController mController;
-        private final TestBoundsAnimationTarget mTarget;
-        private final MockValueAnimator mMockAnimator;
-
-        private BoundsAnimator mAnimator;
-        private Rect mFrom;
-        private Rect mTo;
-        private Rect mLargerBounds;
-        private Rect mExpectedFinalBounds;
-        private @BoundsAnimationController.AnimationType int mAnimationType;
-
-        BoundsAnimationDriver(BoundsAnimationController controller,
-                TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) {
-            mController = controller;
-            mTarget = target;
-            mMockAnimator = mockValueAnimator;
-        }
-
-        BoundsAnimationDriver start(Rect from, Rect to,
-                @BoundsAnimationController.AnimationType int animationType) {
-            if (mAnimator != null) {
-                throw new IllegalArgumentException("Call restart() to restart an animation");
-            }
-
-            boolean fromFullscreen = from.equals(BOUNDS_FULL);
-            boolean toFullscreen = to.equals(BOUNDS_FULL);
-
-            mTarget.initialize(from);
-
-            // Started, not running
-            assertTrue(mTarget.mAwaitingAnimationStart);
-            assertFalse(mTarget.mAnimationStarted);
-
-            startImpl(from, to, animationType);
-
-            // Ensure that the animator is paused for the all windows drawn signal when animating
-            // to/from fullscreen
-            if (fromFullscreen || toFullscreen) {
-                assertTrue(mAnimator.isPaused());
-                mController.onAllWindowsDrawn();
-            } else {
-                assertTrue(!mAnimator.isPaused());
-            }
-
-            // Started and running
-            assertFalse(mTarget.mAwaitingAnimationStart);
-            assertTrue(mTarget.mAnimationStarted);
-
-            return this;
-        }
-
-        BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) {
-            if (mAnimator == null) {
-                throw new IllegalArgumentException("Call start() to start a new animation");
-            }
-
-            BoundsAnimator oldAnimator = mAnimator;
-            boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo);
-
-            // Reset the animation start state
-            mTarget.mAnimationStarted = false;
-
-            // Start animation
-            startImpl(mTarget.mStackBounds, to, BOUNDS);
-
-            if (toSameBounds) {
-                // Same animator if same final bounds
-                assertSame(oldAnimator, mAnimator);
-            }
-
-            if (expectStartedAndPipModeChangedCallback) {
-                // Replacing animation with pending pip mode changed callback, ensure we update
-                assertTrue(mTarget.mAnimationStarted);
-                assertTrue(mTarget.mSchedulePipModeChangedOnStart);
-                assertTrue(mTarget.mForcePipModeChangedCallback);
-            } else {
-                // No animation start for replacing animation
-                assertFalse(mTarget.mAnimationStarted);
-            }
-            mTarget.mAnimationStarted = true;
-            return this;
-        }
-
-        private BoundsAnimationDriver startImpl(Rect from, Rect to,
-                @BoundsAnimationController.AnimationType int animationType) {
-            boolean fromFullscreen = from.equals(BOUNDS_FULL);
-            boolean toFullscreen = to.equals(BOUNDS_FULL);
-            mFrom = new Rect(from);
-            mTo = new Rect(to);
-            mExpectedFinalBounds = new Rect(to);
-            mLargerBounds = getLargerBounds(mFrom, mTo);
-            mAnimationType = animationType;
-
-            // Start animation
-            final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen
-                    ? SCHEDULE_PIP_MODE_CHANGED_ON_START
-                    : fromFullscreen
-                            ? SCHEDULE_PIP_MODE_CHANGED_ON_END
-                            : NO_PIP_MODE_CHANGED_CALLBACKS;
-            mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION,
-                    schedulePipModeChangedState, fromFullscreen, toFullscreen, animationType);
-
-            if (animationType == BOUNDS) {
-                // Original stack bounds, frozen task bounds
-                assertEquals(mFrom, mTarget.mStackBounds);
-                assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
-
-                // Animating to larger size
-                if (mFrom.equals(mLargerBounds)) {
-                    assertFalse(mAnimator.animatingToLargerSize());
-                } else if (mTo.equals(mLargerBounds)) {
-                    assertTrue(mAnimator.animatingToLargerSize());
-                }
-            }
-
-            return this;
-        }
-
-        BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) {
-            // Callback made
-            assertTrue(mTarget.mAnimationStarted);
-
-            assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart);
-            return this;
-        }
-
-        BoundsAnimationDriver update(float t) {
-            mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t));
-
-            if (mAnimationType == BOUNDS) {
-                // Temporary stack bounds, frozen task bounds
-                if (t == 0f) {
-                    assertEquals(mFrom, mTarget.mStackBounds);
-                } else if (t == 1f) {
-                    assertEquals(mTo, mTarget.mStackBounds);
-                } else {
-                    assertNotEquals(mFrom, mTarget.mStackBounds);
-                    assertNotEquals(mTo, mTarget.mStackBounds);
-                }
-                assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
-            } else {
-                assertEquals((float) mMockAnimator.getAnimatedValue(), mTarget.mAlpha, 0.01f);
-            }
-            return this;
-        }
-
-        BoundsAnimationDriver cancel() {
-            // Cancel
-            mTarget.mCancelRequested = true;
-            mTarget.mBoundsUpdated = false;
-            mExpectedFinalBounds = null;
-
-            // Update
-            mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f));
-
-            // Not started, not running, cancel reset
-            assertFalse(mTarget.mCancelRequested);
-
-            // Stack/task bounds not updated
-            assertFalse(mTarget.mBoundsUpdated);
-
-            // Callback made
-            assertTrue(mTarget.mAnimationEnded);
-            assertNull(mTarget.mAnimationEndFinalStackBounds);
-
-            return this;
-        }
-
-        BoundsAnimationDriver end() {
-            mAnimator.end();
-
-            if (mAnimationType == BOUNDS) {
-                // Final stack bounds
-                assertEquals(mTo, mTarget.mStackBounds);
-                assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds);
-                assertNull(mTarget.mTaskBounds);
-            } else {
-                assertEquals(mTarget.mAlpha, 1f, 0.01f);
-            }
-
-            return this;
-        }
-
-        BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged,
-                boolean moveToFullscreen) {
-            // Callback made
-            assertTrue(mTarget.mAnimationEnded);
-
-            assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd);
-            assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen);
-            return this;
-        }
-
-        private static Rect getLargerBounds(Rect r1, Rect r2) {
-            int r1Area = r1.width() * r1.height();
-            int r2Area = r2.width() * r2.height();
-            return (r1Area <= r2Area) ? r2 : r1;
-        }
-    }
-
-    // Constants
-    private static final boolean SCHEDULE_PIP_MODE_CHANGED = true;
-    private static final boolean MOVE_TO_FULLSCREEN = true;
-    private static final int DURATION = 100;
-
-    // Some dummy bounds to represent fullscreen and floating bounds
-    private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100);
-    private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95);
-    private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95);
-
-    // Common
-    private MockAppTransition mMockAppTransition;
-    private TestBoundsAnimationTarget mTarget;
-    private BoundsAnimationController mController;
-    private BoundsAnimationDriver mDriver;
-
-    // Temp
-    private static final Rect sTmpRect = new Rect();
-
-    @Before
-    public void setUp() throws Exception {
-        final Context context = getInstrumentation().getTargetContext();
-        final Handler handler = new Handler(Looper.getMainLooper());
-        mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent);
-        mTarget = new TestBoundsAnimationTarget();
-        mController = new BoundsAnimationController(context, mMockAppTransition, handler, null);
-        final MockValueAnimator mockValueAnimator = new MockValueAnimator();
-        mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator);
-    }
-
-    /** BASE TRANSITIONS **/
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingTransition() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToFullscreenTransition() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
-                .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToSmallerFloatingTransition() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToLargerFloatingTransition() {
-        mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    /** F->!F w/ CANCEL **/
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingCancelFromTarget() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .cancel()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingCancelFromAnimationToSameBounds() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_SMALLER_FLOATING,
-                        false /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() {
-        // When animating from fullscreen and the animation is interruped, we expect the animation
-        // start callback to be made, with a forced pip mode change callback
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
-    }
-
-    /** !F->F w/ CANCEL **/
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToFullscreenCancelFromTarget() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
-                .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .cancel()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToFullscreenCancelFromAnimationToSameBounds() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
-                .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
-                .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .restart(BOUNDS_SMALLER_FLOATING,
-                        false /* expectStartedAndPipModeChangedCallback */)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
-    }
-
-    /** !F->!F w/ CANCEL **/
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToSmallerFloatingCancelFromTarget() {
-        mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .cancel()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFloatingToLargerFloatingCancelFromTarget() {
-        mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0.25f)
-                .cancel()
-                .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    @UiThreadTest
-    @Test
-    public void testFadeIn() {
-        mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, FADE_IN)
-                .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
-                .update(0f)
-                .update(0.5f)
-                .update(1f)
-                .end()
-                .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
-    }
-
-    /** MISC **/
-
-    @UiThreadTest
-    @Test
-    public void testBoundsAreCopied() {
-        Rect from = new Rect(0, 0, 100, 100);
-        Rect to = new Rect(25, 25, 75, 75);
-        mDriver.start(from, to, BOUNDS)
-                .update(0.25f)
-                .end();
-        assertEquals(new Rect(0, 0, 100, 100), from);
-        assertEquals(new Rect(25, 25, 75, 75), to);
-    }
-
-    /**
-     * @return whether the task and stack bounds would be the same if they were at the same offset.
-     */
-    private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) {
-        sTmpRect.set(taskBounds);
-        sTmpRect.offsetTo(stackBounds.left, stackBounds.top);
-        return stackBounds.equals(sTmpRect);
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 4449069..d9c5c4c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1102,12 +1102,8 @@
         assertSecurityException(expectCallable,
                 () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
         assertSecurityException(expectCallable,
-                () -> mService.animateResizePinnedStack(INVALID_STACK_ID, new Rect(), -1));
-        assertSecurityException(expectCallable,
                 () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
                         new Rect()));
-        assertSecurityException(expectCallable,
-                () -> mService.resizePinnedStack(new Rect(), new Rect()));
         assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
         assertSecurityException(expectCallable,
                 () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index c9efb70..5c36f5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -333,7 +333,7 @@
         assertTrue(mController.isAnimatingTask(activity.getTask()));
 
         // Assume activity transition should animate when no
-        // IRecentsAnimationController#setCancelWithDeferredScreenshot called.
+        // IRecentsAnimationController#setDeferCancelUntilNextTransition called.
         assertFalse(mController.shouldDeferCancelWithScreenshot());
         assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index b78107e..b3a25302 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -208,7 +208,6 @@
     @Test
     public void testSetLaunchTaskBehindOfTargetActivity() {
         DisplayContent display = mRootWindowContainer.getDefaultDisplay();
-        display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
         ActivityStack homeStack = display.getRootHomeTask();
         // Assume the home activity support recents.
         ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity();
@@ -254,7 +253,6 @@
     @Test
     public void testCancelAnimationOnVisibleStackOrderChange() {
         DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay();
-        display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
         ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         new ActivityBuilder(mService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index bd8aacb..20d9aff 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -25,6 +25,7 @@
 import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
@@ -36,6 +37,7 @@
 import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArraySet;
@@ -138,6 +140,7 @@
         final int orientation = Configuration.ORIENTATION_PORTRAIT;
         final float scaleFraction = 0.25f;
         final Rect contentInsets = new Rect(1, 2, 3, 4);
+        final Point taskSize = new Point(5, 6);
 
         try {
             ActivityManager.TaskSnapshot.Builder builder =
@@ -147,14 +150,13 @@
             builder.setSystemUiVisibility(systemUiVisibility);
             builder.setWindowingMode(windowingMode);
             builder.setColorSpace(sRGB);
-            builder.setIsLowResolution(true);
             builder.setOrientation(orientation);
             builder.setContentInsets(contentInsets);
             builder.setIsTranslucent(true);
-            builder.setScaleFraction(0.25f);
             builder.setSnapshot(buffer);
             builder.setIsRealSnapshot(true);
             builder.setPixelFormat(pixelFormat);
+            builder.setTaskSize(taskSize);
 
             // Not part of TaskSnapshot itself, used in screenshot process
             assertEquals(pixelFormat, builder.getPixelFormat());
@@ -165,13 +167,15 @@
             assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility());
             assertEquals(windowingMode, snapshot.getWindowingMode());
             assertEquals(sRGB, snapshot.getColorSpace());
-            assertTrue(snapshot.isLowResolution());
+            // Snapshots created with the Builder class are always high-res. The only way to get a
+            // low-res snapshot is to load it from the disk in TaskSnapshotLoader.
+            assertFalse(snapshot.isLowResolution());
             assertEquals(orientation, snapshot.getOrientation());
             assertEquals(contentInsets, snapshot.getContentInsets());
             assertTrue(snapshot.isTranslucent());
-            assertEquals(scaleFraction, builder.getScaleFraction(), 0.001f);
             assertSame(buffer, snapshot.getSnapshot());
             assertTrue(snapshot.isRealSnapshot());
+            assertEquals(taskSize, snapshot.getTaskSize());
         } finally {
             if (buffer != null) {
                 buffer.destroy();
@@ -188,11 +192,9 @@
 
         final ActivityManager.TaskSnapshot.Builder builder =
                 new ActivityManager.TaskSnapshot.Builder();
-        final float scaleFraction = 0.8f;
         mWm.mTaskSnapshotController.prepareTaskSnapshot(mAppWindow.mActivityRecord.getTask(),
-                scaleFraction, PixelFormat.UNKNOWN, builder);
+                PixelFormat.UNKNOWN, builder);
 
-        assertEquals(scaleFraction, builder.getScaleFraction(), 0 /* delta */);
         // The pixel format should be selected automatically.
         assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 0b16e5c..40f15b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -19,12 +19,16 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -36,10 +40,12 @@
 
 import androidx.test.filters.MediumTest;
 
+import com.android.server.wm.TaskSnapshotLoader.PreRLegacySnapshotConfig;
 import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
 
 import java.io.File;
 import java.util.function.Predicate;
@@ -55,6 +61,8 @@
 @RunWith(WindowTestRunner.class)
 public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
 
+    private static final float DELTA = 0.00001f;
+
     private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
 
     @Test
@@ -148,29 +156,172 @@
     }
 
     @Test
-    public void testLowResolutionPersistAndLoadSnapshot() {
+    public void testLegacyPLowRamConfig() throws Exception {
+        MockitoSession mockSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(ActivityManager.class)
+                .startMocking();
+
+        when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true);
+
+        // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+        // for any P low_ram device
+        final int taskWidth = 0;
+        final float legacyScale = 0f;
+        final boolean hasHighResFile = false;
+
+        PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+        assertNotNull(highResConf);
+        assertEquals(highResConf.mScale, 0.6f, DELTA);
+        assertTrue(highResConf.mForceLoadReducedJpeg);
+
+        PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+        assertNotNull(lowResConf);
+        assertEquals(lowResConf.mScale, 0.6f, DELTA);
+        assertTrue(lowResConf.mForceLoadReducedJpeg);
+
+        mockSession.finishMocking();
+    }
+
+    @Test
+    public void testLegacyPNonLowRamConfig() throws Exception {
+        MockitoSession mockSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(ActivityManager.class)
+                .startMocking();
+
+        when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false);
+
+        // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+        // for any O device, or a P non-low_ram device
+        final int taskWidth = 0;
+        final float legacyScale = 0f;
+        final boolean hasHighResFile = true;
+
+        PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+        assertNotNull(highResConf);
+        assertEquals(highResConf.mScale, 1.0f, DELTA);
+        assertFalse(highResConf.mForceLoadReducedJpeg);
+
+        PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+        assertNotNull(lowResConf);
+        assertEquals(lowResConf.mScale, 0.5f, DELTA);
+        assertFalse(lowResConf.mForceLoadReducedJpeg);
+
+        mockSession.finishMocking();
+    }
+
+    @Test
+    public void testLegacyQLowRamConfig() throws Exception {
+        MockitoSession mockSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(ActivityManager.class)
+                .startMocking();
+
+        when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true);
+
+        // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+        // for any Q low_ram device
+        final int taskWidth = 0;
+        final float legacyScale = 0.6f;
+        final boolean hasHighResFile = false;
+
+        PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+        assertNotNull(highResConf);
+        assertEquals(highResConf.mScale, legacyScale, DELTA);
+        assertEquals(highResConf.mScale, 0.6f, DELTA);
+        assertTrue(highResConf.mForceLoadReducedJpeg);
+
+        PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+        assertNotNull(lowResConf);
+        assertEquals(lowResConf.mScale, legacyScale, DELTA);
+        assertEquals(lowResConf.mScale, 0.6f, DELTA);
+        assertTrue(lowResConf.mForceLoadReducedJpeg);
+
+        mockSession.finishMocking();
+    }
+
+    @Test
+    public void testLegacyQNonLowRamConfig() throws Exception {
+        MockitoSession mockSession = mockitoSession()
+                .initMocks(this)
+                .mockStatic(ActivityManager.class)
+                .startMocking();
+
+        when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false);
+
+        // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+        // for any Q non-low_ram device
+        final int taskWidth = 0;
+        final float legacyScale = 0.8f;
+        final boolean hasHighResFile = true;
+
+        PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+        assertNotNull(highResConf);
+        assertEquals(highResConf.mScale, legacyScale, DELTA);
+        assertEquals(highResConf.mScale, 0.8f, DELTA);
+        assertFalse(highResConf.mForceLoadReducedJpeg);
+
+        PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+        assertNotNull(lowResConf);
+        assertEquals(lowResConf.mScale, 0.5f * legacyScale, DELTA);
+        assertEquals(lowResConf.mScale, 0.5f * 0.8f, DELTA);
+        assertFalse(lowResConf.mForceLoadReducedJpeg);
+
+        mockSession.finishMocking();
+    }
+
+    @Test
+    public void testNonLegacyRConfig() throws Exception {
+        // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file,
+        // for any R device
+        final int taskWidth = 1440;
+        final float legacyScale = 0f;
+        final boolean hasHighResFile = true;
+
+        PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */);
+        assertNull(highResConf);
+
+        PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig(
+                taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */);
+        assertNull(lowResConf);
+    }
+
+    @Test
+    public void testDisabledLowResolutionPersistAndLoadSnapshot() {
+        mPersister.setEnableLowResSnapshots(false);
+
         TaskSnapshot a = new TaskSnapshotBuilder()
-                .setScale(0.5f)
+                .setScaleFraction(0.5f)
                 .setIsLowResolution(true)
                 .build();
         assertTrue(a.isLowResolution());
         mPersister.persistSnapshot(1, mTestUserId, a);
         mPersister.waitForQueueEmpty();
         final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")};
+                new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
         final File[] nonExistsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1.jpg"),
+                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
         };
         assertTrueForFiles(files, File::exists, " must exist");
         assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
-        final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, true /* isLowResolution */);
+        final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
         assertNotNull(snapshot);
         assertEquals(TEST_INSETS, snapshot.getContentInsets());
         assertNotNull(snapshot.getSnapshot());
         assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
 
         final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId,
-                false /* isLowResolution */);
+                true /* isLowResolution */);
         assertNull(snapshotNotExist);
     }
 
@@ -271,13 +422,11 @@
     @Test
     public void testScalePersistAndLoadSnapshot() {
         TaskSnapshot a = new TaskSnapshotBuilder()
-                .setScale(0.25f)
+                .setScaleFraction(0.25f)
                 .build();
         TaskSnapshot b = new TaskSnapshotBuilder()
-                .setScale(0.75f)
+                .setScaleFraction(0.75f)
                 .build();
-        assertEquals(0.25f, a.getScale(), 1E-5);
-        assertEquals(0.75f, b.getScale(), 1E-5);
         mPersister.persistSnapshot(1, mTestUserId, a);
         mPersister.persistSnapshot(2, mTestUserId, b);
         mPersister.waitForQueueEmpty();
@@ -287,8 +436,6 @@
                 false /* isLowResolution */);
         assertNotNull(snapshotA);
         assertNotNull(snapshotB);
-        assertEquals(0.25f, snapshotA.getScale(), 1E-5);
-        assertEquals(0.75f, snapshotB.getScale(), 1E-5);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 4612dba..fa6663c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -30,6 +30,7 @@
 import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.UserManager;
 import android.view.Surface;
@@ -87,8 +88,10 @@
      * Builds a TaskSnapshot.
      */
     static class TaskSnapshotBuilder {
+        private static final int SNAPSHOT_WIDTH = 100;
+        private static final int SNAPSHOT_HEIGHT = 100;
 
-        private float mScale = 1f;
+        private float mScaleFraction = 1f;
         private boolean mIsLowResolution = false;
         private boolean mIsRealSnapshot = true;
         private boolean mIsTranslucent = false;
@@ -96,8 +99,11 @@
         private int mSystemUiVisibility = 0;
         private int mRotation = Surface.ROTATION_0;
 
-        TaskSnapshotBuilder setScale(float scale) {
-            mScale = scale;
+        TaskSnapshotBuilder() {
+        }
+
+        TaskSnapshotBuilder setScaleFraction(float scale) {
+            mScaleFraction = scale;
             return this;
         }
 
@@ -132,15 +138,20 @@
         }
 
         TaskSnapshot build() {
-            final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888,
+            // To satisfy existing tests, ensure the graphics buffer is always 100x100, and
+            // compute the ize of the task according to mScaleFraction.
+            Point taskSize = new Point((int) (SNAPSHOT_WIDTH / mScaleFraction),
+                    (int) (SNAPSHOT_HEIGHT / mScaleFraction));
+            final GraphicBuffer buffer = GraphicBuffer.create(SNAPSHOT_WIDTH, SNAPSHOT_HEIGHT,
+                    PixelFormat.RGBA_8888,
                     USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY);
             Canvas c = buffer.lockCanvas();
             c.drawColor(Color.RED);
             buffer.unlockCanvasAndPost(c);
             return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer,
                     ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
-                    mRotation, TEST_INSETS,
-                    mIsLowResolution, mScale, mIsRealSnapshot,
+                    mRotation, taskSize, TEST_INSETS,
+                    mIsLowResolution, mIsRealSnapshot,
                     mWindowingMode, mSystemUiVisibility, mIsTranslucent);
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index bb0e5ae..2164de9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -38,6 +38,7 @@
 import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Surface;
@@ -67,12 +68,22 @@
             int windowFlags, Rect taskBounds) {
         final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888,
                 GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER);
+
+        // Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic
+        // this behavior set the taskSize to be the same as the taskBounds width and height. The
+        // taskBounds passed here are assumed to be the same task bounds as when the snapshot was
+        // taken. We assume there is no aspect ratio mismatch between the screenshot and the
+        // taskBounds
+        assertEquals(width, taskBounds.width());
+        assertEquals(height, taskBounds.height());
+        Point taskSize = new Point(taskBounds.width(), taskBounds.height());
+
         final TaskSnapshot snapshot = new TaskSnapshot(
                 System.currentTimeMillis(),
                 new ComponentName("", ""), buffer,
                 ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
-                Surface.ROTATION_0, contentInsets, false,
-                1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
+                Surface.ROTATION_0, taskSize, contentInsets, false,
+                true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
                 0 /* systemUiVisibility */, false /* isTranslucent */);
         mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
                 createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
@@ -152,7 +163,7 @@
 
     @Test
     public void testCalculateSnapshotCrop_taskNotOnTop() {
-        setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100));
+        setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150));
         assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop());
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 84f411f..07cc2d4 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1720,21 +1720,6 @@
         private static final int ENUMERATION_TIME_OUT_MS = 2000;
 
         /**
-         * Command to start native service.
-         */
-        protected static final String CTL_START = "ctl.start";
-
-        /**
-         * Command to start native service.
-         */
-        protected static final String CTL_STOP = "ctl.stop";
-
-        /**
-         * Adb native daemon.
-         */
-        protected static final String ADBD = "adbd";
-
-        /**
          * Gadget HAL fully qualified instance name for registering for ServiceNotification.
          */
         protected static final String GADGET_HAL_FQ_NAME =
@@ -1932,17 +1917,7 @@
                     return;
                 }
                 try {
-                    if ((config & UsbManager.FUNCTION_ADB) != 0) {
-                        /**
-                         * Start adbd if ADB function is included in the configuration.
-                         */
-                        setSystemProperty(CTL_START, ADBD);
-                    } else {
-                        /**
-                         * Stop adbd otherwise.
-                         */
-                        setSystemProperty(CTL_STOP, ADBD);
-                    }
+                    // Adbd will be started by AdbService once Global.ADB_ENABLED is set.
                     UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
                             config, chargingFunctions);
                     mGadgetProxy.setCurrentUsbFunctions(config, usbGadgetCallback,
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 61f2c50..14c7f04 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -110,18 +110,18 @@
         }
 
         @Override
-        public void onSwitchUser(TargetUser from, TargetUser to) {
+        public void onUserSwitching(TargetUser from, TargetUser to) {
             FgThread.getHandler()
                     .postAtFrontOfQueue(() -> mUsbService.onSwitchUser(to.getUserIdentifier()));
         }
 
         @Override
-        public void onStopUser(TargetUser userInfo) {
+        public void onUserStopping(TargetUser userInfo) {
             mUsbService.onStopUser(userInfo.getUserHandle());
         }
 
         @Override
-        public void onUnlockUser(TargetUser userInfo) {
+        public void onUserUnlocking(TargetUser userInfo) {
             mUsbService.onUnlockUser(userInfo.getUserIdentifier());
         }
     }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 8378d8e..3c0e0af 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -167,7 +167,7 @@
     }
 
     @Override
-    public boolean isSupportedUser(TargetUser user) {
+    public boolean isUserSupported(TargetUser user) {
         return isSupported(user.getUserInfo());
     }
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4604cd2..5f33a3d 100755
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3358,7 +3358,6 @@
         private boolean mImmutable = false;
         public FailureSignalingConnection(DisconnectCause disconnectCause) {
             setDisconnected(disconnectCause);
-            mImmutable = true;
         }
 
         public void checkImmutable() {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 5d7d649..7f4fcc0 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -330,6 +330,14 @@
             "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
 
     /**
+     * Optional extra for incoming and outgoing calls containing a long which specifies the Epoch
+     * time the call was created.
+     * @hide
+     */
+    public static final String EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS =
+            "android.telecom.extra.CALL_CREATED_EPOCH_TIME_MILLIS";
+
+    /**
      * Optional extra for incoming and outgoing calls containing a long which specifies the time
      * telecom began routing the call. This value is in milliseconds since boot.
      * @hide
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a7e52ea..9d77623 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3549,6 +3549,30 @@
     public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL =
             "show_forwarded_number_bool";
 
+    /**
+     * The list of originating address of missed incoming call SMS. If the SMS has originator
+     * matched, the SMS will be treated as special SMS for notifying missed incoming call to the
+     * user.
+     *
+     * @hide
+     */
+    public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY =
+            "missed_incoming_call_sms_originator_string_array";
+
+    /**
+     * The patterns of missed incoming call sms. This is the regular expression used for
+     * matching the missed incoming call's date, time, and caller id. The pattern should match
+     * fields for at least month, day, hour, and minute. Year is optional although it is encouraged.
+     *
+     * An usable pattern should look like this:
+     * ^(?<month>0[1-9]|1[012])\/(?<day>0[1-9]|1[0-9]|2[0-9]|3[0-1]) (?<hour>[0-1][0-9]|2[0-3]):
+     * (?<minute>[0-5][0-9])\s*(?<callerId>[0-9]+)\s*$
+     *
+     * @hide
+     */
+    public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY =
+            "missed_incoming_call_sms_pattern_string_array";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -4057,6 +4081,9 @@
         sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false);
         sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1));
+        sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
+                new String[0]);
+        sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index e4198d1..d672c77 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -25,6 +25,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -54,6 +55,8 @@
     private final int mEarfcn;
     // cell bandwidth, in kHz
     private final int mBandwidth;
+    // cell bands
+    private final List<Integer> mBands;
 
     // a list of additional PLMN-IDs reported for this cell
     private final ArraySet<String> mAdditionalPlmns;
@@ -70,6 +73,7 @@
         mPci = CellInfo.UNAVAILABLE;
         mTac = CellInfo.UNAVAILABLE;
         mEarfcn = CellInfo.UNAVAILABLE;
+        mBands = Collections.emptyList();
         mBandwidth = CellInfo.UNAVAILABLE;
         mAdditionalPlmns = new ArraySet<>();
         mCsgInfo = null;
@@ -87,8 +91,9 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
-        this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
-                String.valueOf(mnc), null, null, new ArraySet<>(), null);
+        this(ci, pci, tac, CellInfo.UNAVAILABLE, Collections.emptyList(), CellInfo.UNAVAILABLE,
+                String.valueOf(mcc), String.valueOf(mnc), null, null, new ArraySet<>(),
+                null);
     }
 
     /**
@@ -107,7 +112,7 @@
      *
      * @hide
      */
-    public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth,
+    public CellIdentityLte(int ci, int pci, int tac, int earfcn, List<Integer> bands, int bandwidth,
             @Nullable String mccStr, @Nullable String mncStr, @Nullable String alphal,
             @Nullable String alphas, @NonNull Collection<String> additionalPlmns,
             @Nullable ClosedSubscriberGroupInfo csgInfo) {
@@ -116,6 +121,7 @@
         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
         mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
+        mBands = new ArrayList<>(bands);
         mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
         mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
@@ -128,28 +134,28 @@
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_0.CellIdentityLte cid) {
-        this(cid.ci, cid.pci, cid.tac, cid.earfcn,
+        this(cid.ci, cid.pci, cid.tac, cid.earfcn, Collections.emptyList(),
                 CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", new ArraySet<>(), null);
     }
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_2.CellIdentityLte cid) {
-        this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth,
-                cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
+        this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, Collections.emptyList(),
+                cid.bandwidth, cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
                 cid.operatorNames.alphaShort, new ArraySet<>(), null);
     }
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_5.CellIdentityLte cid) {
         this(cid.base.base.ci, cid.base.base.pci, cid.base.base.tac, cid.base.base.earfcn,
-                cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc,
+                cid.bands, cid.base.bandwidth, cid.base.base.mcc, cid.base.base.mnc,
                 cid.base.operatorNames.alphaLong, cid.base.operatorNames.alphaShort,
                 cid.additionalPlmns, cid.optionalCsgInfo.csgInfo() != null
                         ? new ClosedSubscriberGroupInfo(cid.optionalCsgInfo.csgInfo()) : null);
     }
 
     private CellIdentityLte(@NonNull CellIdentityLte cid) {
-        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBandwidth, cid.mMccStr,
+        this(cid.mCi, cid.mPci, cid.mTac, cid.mEarfcn, cid.mBands, cid.mBandwidth, cid.mMccStr,
                 cid.mMncStr, cid.mAlphaLong, cid.mAlphaShort, cid.mAdditionalPlmns, cid.mCsgInfo);
     }
 
@@ -157,7 +163,7 @@
     @Override
     public @NonNull CellIdentityLte sanitizeLocationInfo() {
         return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
-                CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
+                CellInfo.UNAVAILABLE, mBands, CellInfo.UNAVAILABLE,
                 mMccStr, mMncStr, mAlphaLong, mAlphaShort, mAdditionalPlmns, null);
     }
 
@@ -226,8 +232,7 @@
      */
     @NonNull
     public List<Integer> getBands() {
-        // Todo: Add actual support
-        return Collections.emptyList();
+        return Collections.unmodifiableList(mBands);
     }
 
     /**
@@ -343,6 +348,7 @@
         .append(" mPci=").append(mPci)
         .append(" mTac=").append(mTac)
         .append(" mEarfcn=").append(mEarfcn)
+        .append(" mBands=").append(mBands)
         .append(" mBandwidth=").append(mBandwidth)
         .append(" mMcc=").append(mMccStr)
         .append(" mMnc=").append(mMncStr)
@@ -362,6 +368,7 @@
         dest.writeInt(mPci);
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
+        dest.writeList(mBands);
         dest.writeInt(mBandwidth);
         dest.writeArraySet(mAdditionalPlmns);
         dest.writeParcelable(mCsgInfo, flags);
@@ -374,6 +381,7 @@
         mPci = in.readInt();
         mTac = in.readInt();
         mEarfcn = in.readInt();
+        mBands = in.readArrayList(null);
         mBandwidth = in.readInt();
         mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
         mCsgInfo = in.readParcelable(null);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0660776..a36df49 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2788,39 +2788,22 @@
     /**
      * Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current
      * registered operator or the cell nearby, if available.
-     * <p>
-     * The ISO-3166 country code is provided in lowercase 2 character format.
-     * <p>
-     * Note: In multi-sim, this returns a shared emergency network country iso from other
-     * subscription if the subscription used to create the TelephonyManager doesn't camp on
-     * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
-     * slot.
+     *
      * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
      * if on a CDMA network).
      * <p>
      * @return the lowercase 2 character ISO-3166 country code, or empty string if not available.
      */
     public String getNetworkCountryIso() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony == null) return "";
-            return telephony.getNetworkCountryIsoForPhone(getPhoneId(),
-                    null /* no permission check */, null);
-        } catch (RemoteException ex) {
-            return "";
-        }
+        return getNetworkCountryIso(getSlotIndex());
     }
 
     /**
      * Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current
-     * registered operator or the cell nearby, if available.
-     * <p>
-     * The ISO-3166 country code is provided in lowercase 2 character format.
-     * <p>
-     * Note: In multi-sim, this returns a shared emergency network country iso from other
-     * subscription if the subscription used to create the TelephonyManager doesn't camp on
-     * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding
-     * slot.
+     * registered operator or the cell nearby, if available. This is same as
+     * {@link #getNetworkCountryIso()} but allowing specifying the SIM slot index. This is used for
+     * accessing network country info from the SIM slot that does not have SIM inserted.
+     *
      * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine
      * if on a CDMA network).
      * <p>
@@ -2831,22 +2814,18 @@
      *
      * @throws IllegalArgumentException when the slotIndex is invalid.
      *
-     * {@hide}
      */
-    @SystemApi
-    @TestApi
     @NonNull
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String getNetworkCountryIso(int slotIndex) {
         try {
-            if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+            if (slotIndex != SubscriptionManager.DEFAULT_SIM_SLOT_INDEX
+                    && !SubscriptionManager.isValidSlotIndex(slotIndex)) {
                 throw new IllegalArgumentException("invalid slot index " + slotIndex);
             }
 
             ITelephony telephony = getITelephony();
             if (telephony == null) return "";
-            return telephony.getNetworkCountryIsoForPhone(slotIndex, getOpPackageName(),
-                    getFeatureId());
+            return telephony.getNetworkCountryIsoForPhone(slotIndex);
         } catch (RemoteException ex) {
             return "";
         }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 861925f..af5089f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -281,7 +281,7 @@
      * operator's MCC (Mobile Country Code).
      * @see android.telephony.TelephonyManager#getNetworkCountryIso
      */
-    String getNetworkCountryIsoForPhone(int phoneId, String callingPkg, String callingFeatureId);
+    String getNetworkCountryIsoForPhone(int phoneId);
 
     /**
      * Returns the neighboring cell information of the device.
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
index d4e34f9..f6dcff4 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -53,6 +53,7 @@
 
     private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE";
     private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED";
+    private static final String ROLLBACK_SUCCESS = "ROLLBACK_SUCCESS";
 
     private WatchdogEventLogger mLogger = new WatchdogEventLogger();
 
@@ -93,6 +94,7 @@
                     REASON_EXPLICIT_HEALTH_CHECK, null));
             assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                     null, null));
+            assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
         } finally {
             // Reconnect internet again so we won't break tests which assume internet available
             getDevice().executeShellCommand("svc wifi enable");
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 43759cf..4afebb5 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -76,10 +76,10 @@
 
     private static final String REASON_APP_CRASH = "REASON_APP_CRASH";
     private static final String REASON_NATIVE_CRASH = "REASON_NATIVE_CRASH";
-    private static final String REASON_EXPLICIT_HEALTH_CHECK = "REASON_EXPLICIT_HEALTH_CHECK";
 
     private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE";
     private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED";
+    private static final String ROLLBACK_SUCCESS = "ROLLBACK_SUCCESS";
 
     private WatchdogEventLogger mLogger = new WatchdogEventLogger();
 
@@ -146,6 +146,7 @@
                 REASON_APP_CRASH, TESTAPP_A));
         assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
+        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
     }
 
     @Test
@@ -179,6 +180,7 @@
                         REASON_NATIVE_CRASH, null));
         assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
+        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
     }
 
     @Test
@@ -219,6 +221,7 @@
                         REASON_NATIVE_CRASH, null));
         assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
+        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
     }
 
     /**
@@ -290,6 +293,7 @@
                 REASON_APP_CRASH, TESTAPP_A));
         assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
+        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
     }
 
     /**
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 26916bc..aed62d0 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -46,6 +46,7 @@
             "android.view.InsetsSourceTest",
             "android.view.InsetsSourceConsumerTest",
             "android.view.InsetsStateTest",
+            "android.view.WindowMetricsTest"
     };
 
     public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index ded4b91..786223a 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -415,7 +415,7 @@
         }
 
         const SectionFlags s = getSectionFlags(field);
-        if (s.userdebug_and_eng_only()) {
+        if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) {
             printf("#if ALLOW_RESTRICTED_SECTIONS\n");
         }
 
@@ -449,8 +449,13 @@
                 printf("    new TombstoneSection(%d, \"%s\"),\n", field->number(),
                         s.args().c_str());
                 break;
+            case SECTION_TEXT_DUMPSYS:
+                printf("    new TextDumpsysSection(%d, ", field->number());
+                splitAndPrint(s.args());
+                printf(" NULL),\n");
+                break;
         }
-        if (s.userdebug_and_eng_only()) {
+        if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) {
             printf("#endif\n");
         }
     }
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 0c6cf1c..e253d6d 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -132,27 +132,68 @@
     ],
 }
 
-droidstubs {
-    name: "framework-wifi-stubs-srcs",
+stubs_defaults {
+    name: "framework-wifi-stubs-srcs-defaults",
     srcs: [
-        ":framework-annotations",
         ":framework-wifi-updatable-sources",
+        ":framework-wifi-util-lib-aidls",
     ],
-    // This is needed as IOnWifiActivityEnergyInfoListener.aidl in framework-wifi depends on
-    // WifiActivityEnergyInfo.aidl in framework-minus-apex
-    aidl: {
-        include_dirs: ["frameworks/base/core/java"],
-    },
-    defaults: [ "framework-module-stubs-defaults-systemapi" ],
-    sdk_version: "core_current",
-    libs: ["android_system_stubs_current"],
+    libs: [ "framework-annotations-lib" ],
+    sdk_version: "module_current",
+}
+
+droidstubs {
+    name: "framework-wifi-stubs-srcs-publicapi",
+    defaults: [
+        "framework-module-stubs-defaults-publicapi",
+        "framework-wifi-stubs-srcs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-wifi-stubs-srcs-systemapi",
+    defaults: [
+        "framework-module-stubs-defaults-systemapi",
+        "framework-wifi-stubs-srcs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-wifi-api-module_libs_api",
+    defaults: [
+        "framework-module-api-defaults-module_libs_api",
+        "framework-wifi-stubs-srcs-defaults",
+    ],
+}
+
+droidstubs {
+    name: "framework-wifi-stubs-srcs-module_libs_api",
+    defaults: [
+        "framework-module-stubs-defaults-module_libs_api",
+        "framework-wifi-stubs-srcs-defaults",
+    ],
 }
 
 java_library {
-    name: "framework-wifi-stubs",
-    srcs: [":framework-wifi-stubs-srcs"],
-    sdk_version: "core_current",
-    libs: ["android_system_stubs_current"],
+    name: "framework-wifi-stubs-publicapi",
+    srcs: [":framework-wifi-stubs-srcs-publicapi"],
+    sdk_version: "module_current",
+    installable: false,
+}
+
+java_library {
+    name: "framework-wifi-stubs-systemapi",
+    srcs: [":framework-wifi-stubs-srcs-systemapi"],
+    sdk_version: "module_current",
+    libs: ["framework-annotations-lib"],
+    installable: false,
+}
+
+java_library {
+    name: "framework-wifi-stubs-module_libs_api",
+    srcs: [":framework-wifi-stubs-srcs-module_libs_api"],
+    sdk_version: "module_current",
+    libs: ["framework-annotations-lib"],
     installable: false,
 }
 
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index a831984..18b26db 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -100,12 +100,12 @@
     }
 
     /**
-     * Returns true when feature supported, otherwise false.
+     * Returns true when all of the queried features are supported, otherwise false.
      *
-     * @param feature one of feature from {@link HotspotFeatures}
+     * @param features One or combination of the features from {@link @HotspotFeatures}
      */
-    public boolean isFeatureSupported(@HotspotFeatures long feature) {
-        return (mSupportedFeatures & feature) == feature;
+    public boolean areFeaturesSupported(@HotspotFeatures long features) {
+        return (mSupportedFeatures & features) == features;
     }
 
     /**
@@ -122,7 +122,7 @@
      * Constructor with combination of the feature.
      * Zero to no supported feature.
      *
-     * @param features One or combination of the feature from {@link @HotspotFeatures}.
+     * @param features One or combination of the features from {@link @HotspotFeatures}.
      * @hide
      */
     public SoftApCapability(@HotspotFeatures long features) {
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index ae5bf7d..2b47623 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -762,7 +762,8 @@
          * {@link #setBand(@BandType int)}.
          *
          * The channel auto selection will offload to driver when
-         * {@link SoftApCapability#isFeatureSupported(SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)}
+         * {@link SoftApCapability#areFeaturesSupported(
+         * SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD)}
          * return true. Driver will auto select best channel which based on environment
          * interference to get best performance. Check {@link SoftApCapability} to get more detail.
          *
@@ -807,7 +808,7 @@
          *
          * <p>
          * Use {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
-         * {@link SoftApCapability#isFeatureSupported(int)}
+         * {@link SoftApCapability#areFeaturesSupported(int)}
          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT} to determine whether
          * or not this feature is supported.
          *
@@ -882,7 +883,7 @@
          * <p>
          * This method requires hardware support. Hardware support can be determined using
          * {@link WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} and
-         * {@link SoftApCapability#isFeatureSupported(int)}
+         * {@link SoftApCapability#areFeaturesSupported(int)}
          * with {@link SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT}
          *
          * <p>
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7d4b632..b1a4cac 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3769,11 +3769,19 @@
     }
 
     /**
-     * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the current
-     * soft AP state and number of connected devices immediately after a successful call to this API
-     * via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state
-     * indicates that the latest attempt to start soft AP has failed. Caller can unregister a
-     * previously registered callback using {@link #unregisterSoftApCallback}
+     * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the
+     * following callbacks on registration:
+     * <ul>
+     * <li> {@link SoftApCallback#onStateChanged(int, int)}</li>
+     * <li> {@link SoftApCallback#onConnectedClientsChanged(List<WifiClient>)}</li>
+     * <li> {@link SoftApCallback#onInfoChanged(SoftApInfo)}</li>
+     * <li> {@link SoftApCallback#onCapabilityChanged(SoftApCapability)}</li>
+     * </ul>
+     * These will be dispatched on registration to provide the caller with the current state
+     * (and are not an indication of any current change). Note that receiving an immediate
+     * WIFI_AP_STATE_FAILED value for soft AP state indicates that the latest attempt to start
+     * soft AP has failed. Caller can unregister a previously registered callback using
+     * {@link #unregisterSoftApCallback}
      * <p>
      * Applications should have the
      * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers
@@ -5075,7 +5083,7 @@
     }
 
     /**
-     * Returns soft ap config from the backed up data.
+     * Returns soft ap config from the backed up data or null if data is invalid.
      * @param data byte stream in the same format produced by {@link #retrieveSoftApBackupData()}
      *
      * @hide
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index b4eb30b..5e48919 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -36,6 +36,7 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.WorkSource;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -744,6 +745,25 @@
             public PnoNetwork(String ssid) {
                 this.ssid = ssid;
             }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(ssid, flags, authBitField);
+            }
+
+            @Override
+            public boolean equals(Object obj) {
+                if (this == obj) {
+                    return true;
+                }
+                if (!(obj instanceof PnoNetwork)) {
+                    return false;
+                }
+                PnoNetwork lhs = (PnoNetwork) obj;
+                return TextUtils.equals(this.ssid, lhs.ssid)
+                        && this.flags == lhs.flags
+                        && this.authBitField == lhs.authBitField;
+            }
         }
 
         /** Connected vs Disconnected PNO flag {@hide} */