Merge "Seek bar polish" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 874d76f..d4ca706 100644
--- a/Android.bp
+++ b/Android.bp
@@ -911,6 +911,7 @@
 filegroup {
     name: "incremental_aidl",
     srcs: [
+        "core/java/android/os/incremental/IIncrementalServiceConnector.aidl",
         "core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl",
     ],
     path: "core/java",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 9abb308..2fd2e33 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -9,6 +9,7 @@
                core/jni/
                libs/input/
                services/core/jni/
+               services/incremental/
 
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
index 8633c96..8139a2e 100644
--- a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
@@ -30,6 +30,7 @@
 import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowSession;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -120,6 +121,7 @@
                 new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
         final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration();
         final InsetsState mOutInsetsState = new InsetsState();
+        final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
         final IWindow mWindow;
         final View mView;
         final WindowManager.LayoutParams mParams;
@@ -152,7 +154,7 @@
                         mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrame,
                         mOutContentInsets, mOutVisibleInsets, mOutStableInsets,
                         mOutBackDropFrame, mOutDisplayCutout, mOutMergedConfiguration,
-                        mOutSurfaceControl, mOutInsetsState, mOutSurfaceSize,
+                        mOutSurfaceControl, mOutInsetsState, mOutControls, mOutSurfaceSize,
                         mOutBlastSurfaceControl);
             }
         }
diff --git a/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java
index 4ac3adf..c72cc9d 100644
--- a/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java
@@ -29,6 +29,7 @@
 import android.view.DisplayCutout;
 import android.view.IWindowSession;
 import android.view.InputChannel;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.View;
 import android.view.WindowManager;
@@ -91,6 +92,7 @@
         final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
         final InsetsState mOutInsetsState = new InsetsState();
+        final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
 
         TestWindow() {
             mLayoutParams.setTitle(TestWindow.class.getName());
@@ -109,7 +111,7 @@
                 long startTime = SystemClock.elapsedRealtimeNanos();
                 session.addToDisplay(this, mSeq, mLayoutParams, View.VISIBLE,
                         Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets,
-                        mOutDisplayCutout, inputChannel, mOutInsetsState);
+                        mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls);
                 final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
                 state.addExtraResult("add", elapsedTimeNsOfAdd);
 
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 5cdcdd3..2eb7e99 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -48,5 +48,5 @@
     name: "service-permission-stubs",
     srcs: [":service-permission-stubs-srcs"],
     defaults: ["service-module-stubs-defaults"],
-    visibility: ["//visibility:private"]
+    visibility: ["//frameworks/base/services/core"]
 }
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index 32e13e3..15d7495 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -67,7 +67,6 @@
         "liblog",  // Has a stable abi - should not be copied into apex.
         "libstatssocket",
     ],
-    //TODO: is libc++_static correct?
     stl: "libc++_static",
     cflags: [
         "-Wall",
diff --git a/apex/statsd/tests/libstatspull/Android.bp b/apex/statsd/tests/libstatspull/Android.bp
index 2d64f19..0df96e1 100644
--- a/apex/statsd/tests/libstatspull/Android.bp
+++ b/apex/statsd/tests/libstatspull/Android.bp
@@ -32,7 +32,7 @@
         "protos/**/*.proto",
         ],
     test_suites: [
-        "general-tests",
+        "device-tests",
     ],
     platform_apis: true,
     privileged: true,
diff --git a/api/test-current.txt b/api/test-current.txt
index 4eeaaf87..22b051b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1504,6 +1504,13 @@
     ctor public AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String);
   }
 
+  public class AudioSystem {
+    method public static float getMasterBalance();
+    method public static final int getNumStreamTypes();
+    method public static int setMasterBalance(float);
+    field public static final int STREAM_DEFAULT = -1; // 0xffffffff
+  }
+
   public static final class AudioTrack.MetricsConstants {
     field public static final String ATTRIBUTES = "android.media.audiotrack.attributes";
     field public static final String CHANNEL_MASK = "android.media.audiotrack.channelMask";
@@ -3522,6 +3529,32 @@
 
 }
 
+package android.service.watchdog {
+
+  public abstract class ExplicitHealthCheckService extends android.app.Service {
+    ctor public ExplicitHealthCheckService();
+    method public final void notifyHealthCheckPassed(@NonNull String);
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onCancelHealthCheck(@NonNull String);
+    method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
+    method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages();
+    method public abstract void onRequestHealthCheck(@NonNull String);
+    method public void setCallback(@Nullable android.os.RemoteCallback);
+    field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+    field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
+  }
+
+  public static final class ExplicitHealthCheckService.PackageConfig implements android.os.Parcelable {
+    ctor public ExplicitHealthCheckService.PackageConfig(@NonNull String, long);
+    method public int describeContents();
+    method public long getHealthCheckTimeoutMillis();
+    method @NonNull public String getPackageName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> CREATOR;
+  }
+
+}
+
 package android.telecom {
 
   public final class Call {
@@ -4681,7 +4714,6 @@
     field public static final String FFLAG_OVERRIDE_PREFIX = "sys.fflag.override.";
     field public static final String FFLAG_PREFIX = "sys.fflag.";
     field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
-    field public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ = "settings_notif_convo_bypass_shortcut_req";
     field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
     field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
     field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
@@ -4782,7 +4814,14 @@
 
   public final class Display {
     method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
+    method public int getType();
     method public boolean hasAccess(int);
+    field public static final int TYPE_EXTERNAL = 2; // 0x2
+    field public static final int TYPE_INTERNAL = 1; // 0x1
+    field public static final int TYPE_OVERLAY = 4; // 0x4
+    field public static final int TYPE_UNKNOWN = 0; // 0x0
+    field public static final int TYPE_VIRTUAL = 5; // 0x5
+    field public static final int TYPE_WIFI = 3; // 0x3
   }
 
   public class FocusFinder {
@@ -4820,6 +4859,13 @@
     method public abstract String asyncImpl() default "";
   }
 
+  public final class SurfaceControl implements android.os.Parcelable {
+    ctor public SurfaceControl(@NonNull android.view.SurfaceControl);
+    method public static long acquireFrameRateFlexibilityToken();
+    method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
+    method public static void releaseFrameRateFlexibilityToken(long);
+  }
+
   public class SurfaceControlViewHost {
     method public void relayout(android.view.WindowManager.LayoutParams);
     method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams);
@@ -5169,10 +5215,10 @@
     method public void onDisplayAreaAppeared(@NonNull android.window.WindowContainerToken);
     method public void onDisplayAreaVanished(@NonNull android.window.WindowContainerToken);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void registerOrganizer(int);
+    field public static final int FEATURE_DEFAULT_TASK_CONTAINER = 1; // 0x1
     field public static final int FEATURE_ROOT = 0; // 0x0
     field public static final int FEATURE_SYSTEM_FIRST = 0; // 0x0
     field public static final int FEATURE_SYSTEM_LAST = 10000; // 0x2710
-    field public static final int FEATURE_TASK_CONTAINER = 1; // 0x1
     field public static final int FEATURE_UNDEFINED = -1; // 0xffffffff
     field public static final int FEATURE_VENDOR_FIRST = 10001; // 0x2711
     field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index d3d7e1d..65061d0 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -113,17 +113,18 @@
     static_libs: [
         "libbase",
         "libcutils",
+        "libgtest_prod",
         "libprotoutil",
         "libstatsmetadata",
         "libstatslog_statsd",
         "libsysutils",
         "libutils",
+        "statsd-aidl-ndk_platform",
     ],
     shared_libs: [
         "libbinder_ndk",
         "libincident",
         "liblog",
-        "statsd-aidl-ndk_platform",
     ],
 }
 
@@ -268,10 +269,11 @@
 
     proto: {
         type: "lite",
+        static: true,
     },
+    stl: "libc++_static",
 
     shared_libs: [
-        "libgtest_prod",
         "libstatssocket",
     ],
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index bd15264..d1995bc 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -418,7 +418,7 @@
         AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
         SharesheetStarted sharesheet_started = 259 [(module) = "framework"];
         RankingSelected ranking_selected = 260 [(module) = "framework"];
-        TvSettingsUIInteracted tvsettings_ui_interacted = 261;
+        TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"];
         SdkExtensionStatus sdk_extension_status = 354;
     }
 
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 6d9c644..bbae3fe 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -43,20 +43,23 @@
 using std::unique_ptr;
 
 struct ConfigReceiverDeathCookie {
-    ConfigReceiverDeathCookie(sp<ConfigManager> configManager, const ConfigKey& configKey,
-                              const shared_ptr<IPendingIntentRef>& pir):
-        mConfigManager(configManager),
-        mConfigKey(configKey),
-        mPir(pir) {}
+    ConfigReceiverDeathCookie(const wp<ConfigManager>& configManager, const ConfigKey& configKey,
+                              const shared_ptr<IPendingIntentRef>& pir) :
+            mConfigManager(configManager), mConfigKey(configKey), mPir(pir) {
+    }
 
-    sp<ConfigManager> mConfigManager;
+    wp<ConfigManager> mConfigManager;
     ConfigKey mConfigKey;
     shared_ptr<IPendingIntentRef> mPir;
 };
 
 void ConfigManager::configReceiverDied(void* cookie) {
     auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
-    sp<ConfigManager>& thiz = cookie_->mConfigManager;
+    sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+    if (!thiz) {
+        return;
+    }
+
     ConfigKey& configKey = cookie_->mConfigKey;
     shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
 
@@ -74,20 +77,23 @@
 }
 
 struct ActiveConfigChangedReceiverDeathCookie {
-    ActiveConfigChangedReceiverDeathCookie(sp<ConfigManager> configManager, const int uid,
-                                           const shared_ptr<IPendingIntentRef>& pir):
-        mConfigManager(configManager),
-        mUid(uid),
-        mPir(pir) {}
+    ActiveConfigChangedReceiverDeathCookie(const wp<ConfigManager>& configManager, const int uid,
+                                           const shared_ptr<IPendingIntentRef>& pir) :
+            mConfigManager(configManager), mUid(uid), mPir(pir) {
+    }
 
-    sp<ConfigManager> mConfigManager;
+    wp<ConfigManager> mConfigManager;
     int mUid;
     shared_ptr<IPendingIntentRef> mPir;
 };
 
 void ConfigManager::activeConfigChangedReceiverDied(void* cookie) {
     auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
-    sp<ConfigManager>& thiz = cookie_->mConfigManager;
+    sp<ConfigManager> thiz = cookie_->mConfigManager.promote();
+    if (!thiz) {
+        return;
+    }
+
     int uid = cookie_->mUid;
     shared_ptr<IPendingIntentRef>& pir = cookie_->mPir;
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 79a7e8d..ebe9610 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -44,19 +44,23 @@
 // Stores the puller as a wp to avoid holding a reference in case it is unregistered and
 // pullAtomCallbackDied is never called.
 struct PullAtomCallbackDeathCookie {
-    PullAtomCallbackDeathCookie(sp<StatsPullerManager> pullerManager, const PullerKey& pullerKey,
-                                const wp<StatsPuller>& puller)
-        : mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
+    PullAtomCallbackDeathCookie(const wp<StatsPullerManager>& pullerManager,
+                                const PullerKey& pullerKey, const wp<StatsPuller>& puller) :
+            mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
     }
 
-    sp<StatsPullerManager> mPullerManager;
+    wp<StatsPullerManager> mPullerManager;
     PullerKey mPullerKey;
     wp<StatsPuller> mPuller;
 };
 
 void StatsPullerManager::pullAtomCallbackDied(void* cookie) {
     PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie);
-    sp<StatsPullerManager>& thiz = cookie_->mPullerManager;
+    sp<StatsPullerManager> thiz = cookie_->mPullerManager.promote();
+    if (!thiz) {
+        return;
+    }
+
     const PullerKey& pullerKey = cookie_->mPullerKey;
     wp<StatsPuller> puller = cookie_->mPuller;
 
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 850e2b0..9e21c77 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -218,9 +218,6 @@
      * The below three variables are only valid during the execution of
      * parseBuffer. There are no guarantees about the state of these variables
      * before/after.
-     *
-     * TODO (b/150312423): These shouldn't be member variables. We should pass
-     * them around as parameters.
      */
     uint8_t* mBuf;
     uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index 1cee4d9..bed836a 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -152,7 +152,6 @@
                 mPullerMgr->Pull(pullInfo.mPullerMatcher.atom_id(), uids, &data);
                 VLOG("pulled %zu atoms with id %d", data.size(), pullInfo.mPullerMatcher.atom_id());
 
-                // TODO(b/150969574): Don't write to a pipe while holding a lock.
                 if (!writePulledAtomsLocked(data, pullInfo.mPullerMatcher)) {
                     mSubscriptionInfo->mClientAlive = false;
                     mSubscriptionShouldEnd.notify_one();
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index 6aff9ef..4b9bac1 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -190,13 +190,13 @@
     int32_t uid = 123;
     values.push_back(value);
 
-    StatsPullerManager pullerManager;
-    pullerManager.RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs,
-                                           vector<int32_t>(), cb);
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    pullerManager->RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs,
+                                            vector<int32_t>(), cb);
     vector<shared_ptr<LogEvent>> dataHolder;
     int64_t startTimeNs = getElapsedRealtimeNs();
     // Returns false, since StatsPuller code will evaluate the timeout.
-    EXPECT_FALSE(pullerManager.Pull(pullTagId, {uid}, &dataHolder));
+    EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, &dataHolder));
     int64_t endTimeNs = getElapsedRealtimeNs();
     int64_t actualPullDurationNs = endTimeNs - startTimeNs;
 
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index 2a7cfd3..d5da0b4 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -68,6 +68,7 @@
     };
     private static final String[] DEFAULT_PULL_SOURCES = {
             "AID_SYSTEM",
+            "AID_RADIO"
     };
     private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
 
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index f3759fd..67334f5 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -47,7 +47,6 @@
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.KeyEvent;
-import android.view.SurfaceControl;
 import android.view.SurfaceView;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
@@ -1820,6 +1819,13 @@
 
     /**
      * Returns a list of system actions available in the system right now.
+     * <p>
+     * System actions that correspond to the global action constants will have matching action IDs.
+     * For example, an with id {@link #GLOBAL_ACTION_BACK} will perform the back action.
+     * </p>
+     * <p>
+     * These actions should be called by {@link #performGlobalAction}.
+     * </p>
      *
      * @return A list of available system actions.
      */
@@ -1981,8 +1987,6 @@
      * to declare the capability to take screenshot by setting the
      * {@link android.R.styleable#AccessibilityService_canTakeScreenshot}
      * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
-     * This API only will support {@link Display#DEFAULT_DISPLAY} until {@link SurfaceControl}
-     * supports non-default displays.
      * </p>
      *
      * @param displayId The logic display id, must be {@link Display#DEFAULT_DISPLAY} for
@@ -1990,18 +1994,11 @@
      * @param executor Executor on which to run the callback.
      * @param callback The callback invoked when taking screenshot has succeeded or failed.
      *                 See {@link TakeScreenshotCallback} for details.
-     *
-     * @throws IllegalArgumentException if displayId is not {@link Display#DEFAULT_DISPLAY}.
      */
     public void takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
             @NonNull TakeScreenshotCallback callback) {
         Preconditions.checkNotNull(executor, "executor cannot be null");
         Preconditions.checkNotNull(callback, "callback cannot be null");
-
-        if (displayId != Display.DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("DisplayId isn't the default display");
-        }
-
         final IAccessibilityServiceConnection connection =
                 AccessibilityInteractionClient.getInstance().getConnection(
                         mConnectionId);
diff --git a/core/java/android/annotation/NonNull.java b/core/java/android/annotation/NonNull.java
index a95bf3b..c5aff9d 100644
--- a/core/java/android/annotation/NonNull.java
+++ b/core/java/android/annotation/NonNull.java
@@ -30,8 +30,8 @@
  * <p>
  * This is a marker annotation and it has no specific attributes.
  *
- * @paramDoc This value must never be {@code null}.
- * @returnDoc This value will never be {@code null}.
+ * @paramDoc This value cannot be {@code null}.
+ * @returnDoc This value cannot be {@code null}.
  * @hide
  */
 @Retention(SOURCE)
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index e476993..b7ceb6a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -299,7 +299,6 @@
             in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
 
     void suppressResizeConfigChanges(boolean suppress);
-    void moveTasksToFullscreenStack(int fromStackId, boolean onTop);
     boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
     boolean isInMultiWindowMode(in IBinder token);
     boolean isInPictureInPictureMode(in IBinder token);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f461a17..7a59349 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7158,7 +7158,8 @@
             CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
                     ? super.mBigContentTitle
                     : mConversationTitle;
-            if (!TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) {
+            if (mConversationType == CONVERSATION_TYPE_LEGACY
+                    && !TextUtils.isEmpty(conversationTitle) && !hasOnlyWhiteSpaceSenders()) {
                 return conversationTitle;
             }
             return null;
diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java
index eaee060..7a18b81 100644
--- a/core/java/android/app/Presentation.java
+++ b/core/java/android/app/Presentation.java
@@ -19,6 +19,7 @@
 import static android.content.Context.DISPLAY_SERVICE;
 import static android.content.Context.WINDOW_SERVICE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -191,12 +192,16 @@
         mDisplay = display;
         mDisplayManager = (DisplayManager)getContext().getSystemService(DISPLAY_SERVICE);
 
+        final int windowType =
+                (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION
+                        : TYPE_PRESENTATION;
+
         final Window w = getWindow();
         final WindowManager.LayoutParams attr = w.getAttributes();
         attr.token = mToken;
         w.setAttributes(attr);
         w.setGravity(Gravity.FILL);
-        w.setType(TYPE_PRESENTATION);
+        w.setType(windowType);
         setCanceledOnTouchOutside(false);
     }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 91a8572..e599a5c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1658,6 +1658,9 @@
         public final T getService(ContextImpl ctx) {
             final Object[] cache = ctx.mServiceCache;
             final int[] gates = ctx.mServiceInitializationStateArray;
+            boolean interrupted = false;
+
+            T ret = null;
 
             for (;;) {
                 boolean doInitialize = false;
@@ -1665,7 +1668,8 @@
                     // Return it if we already have a cached instance.
                     T service = (T) cache[mCacheIndex];
                     if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
-                        return service;
+                        ret = service;
+                        break; // exit the for (;;)
                     }
 
                     // If we get here, there's no cached instance.
@@ -1708,24 +1712,33 @@
                             cache.notifyAll();
                         }
                     }
-                    return service;
+                    ret = service;
+                    break; // exit the for (;;)
                 }
                 // The other threads will wait for the first thread to call notifyAll(),
                 // and go back to the top and retry.
                 synchronized (cache) {
+                    // Repeat until the state becomes STATE_READY or STATE_NOT_FOUND.
+                    // We can't respond to interrupts here; just like we can't in the "doInitialize"
+                    // path, so we remember the interrupt state here and re-interrupt later.
                     while (gates[mCacheIndex] < ContextImpl.STATE_READY) {
                         try {
+                            // Clear the interrupt state.
+                            interrupted |= Thread.interrupted();
                             cache.wait();
                         } catch (InterruptedException e) {
                             // This shouldn't normally happen, but if someone interrupts the
                             // thread, it will.
-                            Slog.wtf(TAG, "getService() interrupted");
-                            Thread.currentThread().interrupt();
-                            return null;
+                            Slog.w(TAG, "getService() interrupted");
+                            interrupted = true;
                         }
                     }
                 }
             }
+            if (interrupted) {
+                Thread.currentThread().interrupt();
+            }
+            return ret;
         }
 
         public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 80fa871..41f04f7 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -176,19 +176,31 @@
      * for cross-profile communication, via {@link
      * DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.</li>
      * <li>The default package names that are allowed to request user consent for cross-profile
-     * communication without being explicitly enabled by the admin , via {@link
-     * DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)}.</li>
+     * communication without being explicitly enabled by the admin, via
+     * {@link com.android.internal.R.array#cross_profile_apps} and
+     * {@link com.android.internal.R.array#vendor_cross_profile_apps}.</li>
      * </ul>
      *
      * @return the combined set of whitelisted package names set via
      * {@link DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)} and
-     * {@link DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)}
+     * {@link com.android.internal.R.array#cross_profile_apps} and
+     * {@link com.android.internal.R.array#vendor_cross_profile_apps}
      *
      * @hide
      */
     public abstract List<String> getAllCrossProfilePackages();
 
     /**
+     * Returns the default package names set by the OEM that are allowed to request user consent for
+     * cross-profile communication without being explicitly enabled by the admin, via
+     * {@link com.android.internal.R.array#cross_profile_apps} and
+     * {@link com.android.internal.R.array#vendor_cross_profile_apps}.
+     *
+     * @hide
+     */
+    public abstract List<String> getDefaultCrossProfilePackages();
+
+    /**
      * Sends the {@code intent} to the packages with cross profile capabilities.
      *
      * <p>This means the application must have the {@code crossProfile} property and the
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index f216db6..fc48e7f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -979,17 +979,14 @@
                 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
                 @Override
                 protected Integer recompute(Void query) {
+                    // This function must be called while holding the
+                    // mServiceLock, and with mService not null. The public
+                    // getState() method makes this guarantee.
                     try {
-                        mServiceLock.readLock().lock();
-                        if (mService != null) {
-                            return mService.getState();
-                        }
+                        return mService.getState();
                     } catch (RemoteException e) {
-                        Log.e(TAG, "", e);
-                    } finally {
-                        mServiceLock.readLock().unlock();
+                        throw e.rethrowFromSystemServer();
                     }
-                    return BluetoothAdapter.STATE_OFF;
                 }
             };
 
@@ -1016,7 +1013,24 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     @AdapterState
     public int getState() {
-        int state = mBluetoothGetStateCache.query(null);
+        int state = BluetoothAdapter.STATE_OFF;
+
+        try {
+            mServiceLock.readLock().lock();
+            // The test for mService must either be outside the cache, or
+            // the cache must be invalidated when mService changes.
+            if (mService != null) {
+                state = mBluetoothGetStateCache.query(null);
+            }
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof RemoteException) {
+                Log.e(TAG, "", e.getCause());
+            } else {
+                throw e;
+            }
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
 
         // Consider all internal states as OFF
         if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 7578ede..144a07e 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -435,6 +435,9 @@
      * <p>This differs from {@link #canConfigureInteractAcrossProfiles(String)} since it will
      * not return {@code false} if the app is not whitelisted or not installed in the other profile.
      *
+     * <p>Note that platform-signed apps that are automatically granted the permission and are not
+     * whitelisted by the OEM will not be included in this list.
+     *
      * @hide
      */
     public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
diff --git a/core/java/android/content/pm/FileSystemControlParcel.aidl b/core/java/android/content/pm/FileSystemControlParcel.aidl
index f00feae..92df16c 100644
--- a/core/java/android/content/pm/FileSystemControlParcel.aidl
+++ b/core/java/android/content/pm/FileSystemControlParcel.aidl
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.content.pm.IPackageInstallerSessionFileSystemConnector;
+import android.os.incremental.IIncrementalServiceConnector;
 import android.os.incremental.IncrementalFileSystemControlParcel;
 
 /**
@@ -26,6 +27,8 @@
 parcelable FileSystemControlParcel {
     // Incremental FS control descriptors.
     @nullable IncrementalFileSystemControlParcel incremental;
+    // Incremental FS service.
+    @nullable IIncrementalServiceConnector service;
     // Callback-based installation connector.
     @nullable IPackageInstallerSessionFileSystemConnector callback;
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f48d78a..9a2e07e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1542,6 +1542,14 @@
      */
     public static final int INSTALL_FAILED_PROCESS_NOT_DEFINED = -122;
 
+    /**
+     * Installation parse return code: system is in a minimal boot state, and the parser only
+     * allows the package with {@code coreApp} manifest attribute to be a valid application.
+     *
+     * @hide
+     */
+    public static final int INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED = -123;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 12328cf..c94d428 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -22,6 +22,7 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
 import static android.os.Build.VERSION_CODES.DONUT;
 import static android.os.Build.VERSION_CODES.O;
@@ -229,7 +230,8 @@
         final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir,
                 0);
         if (mOnlyCoreApps && !lite.coreApp) {
-            return input.error("Not a coreApp: " + packageDir);
+            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
+                    "Not a coreApp: " + packageDir);
         }
 
         // Build the split dependency tree.
@@ -291,7 +293,8 @@
         final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile,
                 flags);
         if (mOnlyCoreApps && !lite.coreApp) {
-            return input.error("Not a coreApp: " + apkFile);
+            return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
+                    "Not a coreApp: " + apkFile);
         }
 
         final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index d6a9f69..c399bc7 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -40,6 +40,7 @@
 import android.annotation.StyleRes;
 import android.annotation.StyleableRes;
 import android.annotation.XmlRes;
+import android.app.Application;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.Config;
@@ -58,9 +59,11 @@
 import android.util.LongSparseArray;
 import android.util.Pools.SynchronizedPool;
 import android.util.TypedValue;
+import android.view.Display;
 import android.view.DisplayAdjustments;
 import android.view.ViewDebug;
 import android.view.ViewHierarchyEncoder;
+import android.view.WindowManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -101,6 +104,12 @@
  * (such as for different languages and screen sizes). This is an important aspect of developing
  * Android applications that are compatible on different types of devices.</p>
  *
+ * <p>After {@link Build.VERSION_CODES#R}, {@link Resources} must be obtained by
+ * {@link android.app.Activity} or {@link android.content.Context} created with
+ * {@link android.content.Context#createWindowContext(int, Bundle)}.
+ * {@link Application#getResources()} may report wrong values in multi-window or on secondary
+ * displays.
+ *
  * <p>For more information about using resources, see the documentation about <a
  * href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.</p>
  */
@@ -2024,10 +2033,20 @@
     }
 
     /**
-     * Return the current display metrics that are in effect for this resource 
-     * object.  The returned object should be treated as read-only.
-     * 
-     * @return The resource's current display metrics. 
+     * Return the current display metrics that are in effect for this resource
+     * object. The returned object should be treated as read-only.
+     *
+     * <p>Note that the reported value may be different than the window this application is
+     * interested in.</p>
+     *
+     * <p>Best practices are to obtain metrics from {@link WindowManager#getCurrentWindowMetrics()}
+     * for window bounds, {@link Display#getRealMetrics(DisplayMetrics)} for display bounds and
+     * obtain density from {@link Configuration#densityDpi}. The value obtained from this API may be
+     * wrong if the {@link Resources} is from the context which is different than the window is
+     * attached such as {@link Application#getResources()}.
+     * <p/>
+     *
+     * @return The resource's current display metrics.
      */
     public DisplayMetrics getDisplayMetrics() {
         return mResourcesImpl.getDisplayMetrics();
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index ea2b9e7..462110f 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -17,6 +17,7 @@
 package android.hardware.display;
 
 import android.annotation.Nullable;
+import android.graphics.Point;
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -72,6 +73,15 @@
     public abstract SurfaceControl.ScreenshotGraphicBuffer screenshot(int displayId);
 
     /**
+     * Take a screenshot without secure layer of the specified display and return a buffer.
+     *
+     * @param displayId The display id to take the screenshot of.
+     * @return The buffer or null if we have failed.
+     */
+    public abstract SurfaceControl.ScreenshotGraphicBuffer screenshotWithoutSecureLayer(
+            int displayId);
+
+    /**
      * Returns information about the specified logical display.
      *
      * @param displayId The logical display id.
@@ -81,6 +91,16 @@
     public abstract DisplayInfo getDisplayInfo(int displayId);
 
     /**
+     * Returns the position of the display's projection.
+     *
+     * @param displayId The logical display id.
+     * @return The x, y coordinates of the display, or null if the display does not exist. The
+     * return object must be treated as immutable.
+     */
+    @Nullable
+    public abstract Point getDisplayPosition(int displayId);
+
+    /**
      * Registers a display transaction listener to provide the client a chance to
      * update its surfaces within the same transaction as any display layout updates.
      *
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index fe90a84..e9bcefe 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -556,12 +556,12 @@
     @NonNull
     public Network register() {
         if (VDBG) log("Registering NetworkAgent");
-        final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
-                .getSystemService(Context.CONNECTIVITY_SERVICE);
         synchronized (mRegisterLock) {
             if (mNetwork != null) {
                 throw new IllegalStateException("Agent already registered");
             }
+            final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
+                    .getSystemService(Context.CONNECTIVITY_SERVICE);
             mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
                     new NetworkInfo(mInitialConfiguration.info),
                     mInitialConfiguration.properties, mInitialConfiguration.capabilities,
@@ -624,7 +624,9 @@
             throw new UnsupportedOperationException(
                     "Legacy agents can't call markConnected.");
         }
-        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+        // |reason| cannot be used by the non-legacy agents
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null /* reason */,
+                mNetworkInfo.getExtraInfo());
         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
     }
 
@@ -638,7 +640,9 @@
         if (mIsLegacy) {
             throw new UnsupportedOperationException("Legacy agents can't call unregister.");
         }
-        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+        // When unregistering an agent nobody should use the extrainfo (or reason) any more.
+        mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null /* reason */,
+                null /* extraInfo */);
         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
     }
 
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 9c1fb41..b7fb280 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -1195,18 +1195,24 @@
 
     /**
      * Remove all rows that match one of specified UIDs.
+     * This mutates the original structure in place.
      * @hide
      */
     public void removeUids(int[] uids) {
-        int nextOutputEntry = 0;
-        for (int i = 0; i < size; i++) {
-            if (!ArrayUtils.contains(uids, uid[i])) {
-                maybeCopyEntry(nextOutputEntry, i);
-                nextOutputEntry++;
-            }
-        }
+        filter(e -> !ArrayUtils.contains(uids, e.uid));
+    }
 
-        size = nextOutputEntry;
+    /**
+     * Remove all rows that match one of specified UIDs.
+     * @return the result object.
+     * @hide
+     */
+    @NonNull
+    public NetworkStats removeEmptyEntries() {
+        final NetworkStats ret = this.clone();
+        ret.filter(e -> e.rxBytes != 0 || e.rxPackets != 0 || e.txBytes != 0 || e.txPackets != 0
+                || e.operations != 0);
+        return ret;
     }
 
     /**
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index ae65f1d..5f8c4f5 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -1268,6 +1268,7 @@
     public static boolean isExternalStorageLegacy(@NonNull File path) {
         final Context context = AppGlobals.getInitialApplication();
         final int uid = context.getApplicationInfo().uid;
+        // Isolated processes and Instant apps are never allowed to be in scoped storage
         if (Process.isIsolated(uid)) {
             return false;
         }
@@ -1277,8 +1278,6 @@
             return false;
         }
 
-        // TODO(b/150672994): Compat flags do not override instant app and isolated process's
-        //  behavior.
         boolean defaultScopedStorage = Compatibility.isChangeEnabled(DEFAULT_SCOPED_STORAGE);
         boolean forceEnableScopedStorage = Compatibility.isChangeEnabled(
                 FORCE_ENABLE_SCOPED_STORAGE);
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 51f01ca..653a559 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -102,9 +102,9 @@
      *
      * This method must only be called by the window manager.
      *
-     * @param brightness The overridden brightness, or -1 to disable the override.
+     * @param brightness The overridden brightness, or Float.NaN to disable the override.
      */
-    public abstract void setScreenBrightnessOverrideFromWindowManager(int brightness);
+    public abstract void setScreenBrightnessOverrideFromWindowManager(float brightness);
 
     /**
      * Used by the window manager to override the user activity timeout based on the
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b7b3c4f..5d2c9d1 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -607,6 +607,9 @@
      *                             started.
      * @param pkgDataInfoMap Map from related package names to private data directory
      *                       volume UUID and inode number.
+     * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+     *                       volume UUID and inode number.
+     * @param bindMountAppsData whether zygote needs to mount CE and DE data.
      * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
@@ -631,13 +634,17 @@
                                            @Nullable long[] disabledCompatChanges,
                                            @Nullable Map<String, Pair<String, Long>>
                                                    pkgDataInfoMap,
+                                           @Nullable Map<String, Pair<String, Long>>
+                                                   whitelistedDataInfoMap,
+                                           boolean bindMountAppsData,
                                            boolean bindMountAppStorageDirs,
                                            @Nullable String[] zygoteArgs) {
         return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
                     zygotePolicyFlags, isTopApp, disabledCompatChanges,
-                    pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
+                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+                    bindMountAppStorageDirs, zygoteArgs);
     }
 
     /** @hide */
@@ -661,7 +668,8 @@
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
                     /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
-                disabledCompatChanges, /* pkgDataInfoMap */ null, false, zygoteArgs);
+                disabledCompatChanges, /* pkgDataInfoMap */ null,
+                /* whitelistedDataInfoMap */ null, false, false, zygoteArgs);
     }
 
     /**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 5f3f14f..a4c99c0 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,6 +333,9 @@
      *                             started.
      * @param pkgDataInfoMap Map from related package names to private data directory
      *                       volume UUID and inode number.
+     * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+     *                       volume UUID and inode number.
+     * @param bindMountAppsData whether zygote needs to mount CE and DE data.
      * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
      *
      * @param zygoteArgs Additional arguments to supply to the Zygote process.
@@ -355,6 +358,9 @@
                                                   @Nullable long[] disabledCompatChanges,
                                                   @Nullable Map<String, Pair<String, Long>>
                                                           pkgDataInfoMap,
+                                                  @Nullable Map<String, Pair<String, Long>>
+                                                          whitelistedDataInfoMap,
+                                                  boolean bindMountAppsData,
                                                   boolean bindMountAppStorageDirs,
                                                   @Nullable String[] zygoteArgs) {
         // TODO (chriswailes): Is there a better place to check this value?
@@ -367,7 +373,8 @@
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                     packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
-                    pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
+                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+                    bindMountAppStorageDirs, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -608,6 +615,9 @@
      * @param disabledCompatChanges a list of disabled compat changes for the process being started.
      * @param pkgDataInfoMap Map from related package names to private data directory volume UUID
      *                       and inode number.
+     * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+     *                       volume UUID and inode number.
+     * @param bindMountAppsData whether zygote needs to mount CE and DE data.
      * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
      * @param extraArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
@@ -631,6 +641,9 @@
                                                       @Nullable long[] disabledCompatChanges,
                                                       @Nullable Map<String, Pair<String, Long>>
                                                               pkgDataInfoMap,
+                                                      @Nullable Map<String, Pair<String, Long>>
+                                                              whitelistedDataInfoMap,
+                                                      boolean bindMountAppsData,
                                                       boolean bindMountAppStorageDirs,
                                                       @Nullable String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
@@ -728,11 +741,33 @@
             }
             argsForZygote.add(sb.toString());
         }
+        if (whitelistedDataInfoMap != null && whitelistedDataInfoMap.size() > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(Zygote.WHITELISTED_DATA_INFO_MAP);
+            sb.append("=");
+            boolean started = false;
+            for (Map.Entry<String, Pair<String, Long>> entry : whitelistedDataInfoMap.entrySet()) {
+                if (started) {
+                    sb.append(',');
+                }
+                started = true;
+                sb.append(entry.getKey());
+                sb.append(',');
+                sb.append(entry.getValue().first);
+                sb.append(',');
+                sb.append(entry.getValue().second);
+            }
+            argsForZygote.add(sb.toString());
+        }
 
         if (bindMountAppStorageDirs) {
             argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
         }
 
+        if (bindMountAppsData) {
+            argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS);
+        }
+
         if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
             StringBuilder sb = new StringBuilder();
             sb.append("--disabled-compat-changes=");
@@ -1291,6 +1326,7 @@
                     true /* startChildZygote */, null /* packageName */,
                     ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
                     null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
+                    null /* whitelistedDataInfoMap */, false /* bindMountAppsData*/,
                     /* bindMountAppStorageDirs */ false, extraArgs);
 
         } catch (ZygoteStartFailedEx ex) {
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index d8308c7..2dbaea8 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -38,13 +38,6 @@
     int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
 
     /**
-     * Changes storage params. Returns 0 on success, and -errno on failure.
-     * Use enableReadLogs to switch pages read logs reporting on and off.
-     * Returns 0 on success, and - errno on failure: permission check or remount.
-     */
-    int setStorageParams(int storageId, boolean enableReadLogs);
-
-    /**
      * Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
      */
     const int BIND_TEMPORARY = 0;
diff --git a/core/java/android/os/incremental/IIncrementalServiceConnector.aidl b/core/java/android/os/incremental/IIncrementalServiceConnector.aidl
new file mode 100644
index 0000000..5800ecf
--- /dev/null
+++ b/core/java/android/os/incremental/IIncrementalServiceConnector.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.os.incremental;
+
+/** @hide */
+interface IIncrementalServiceConnector {
+    /**
+     * Changes storage params. Returns 0 on success, and -errno on failure.
+     * Use enableReadLogs to switch pages read logs reporting on and off.
+     * Returns 0 on success, and - errno on failure: permission check or remount.
+     */
+    int setStorageParams(boolean enableReadLogs);
+}
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 5f01408..35518db 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -19,13 +19,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.content.pm.DataLoaderParams;
 import android.content.pm.IDataLoaderStatusListener;
 import android.os.RemoteException;
-import android.system.ErrnoException;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -321,23 +319,6 @@
         return nativeUnsafeGetFileSignature(path);
     }
 
-    /**
-     * Sets storage parameters.
-     *
-     * @param enableReadLogs - enables or disables read logs. Caller has to have a permission.
-     */
-    @RequiresPermission(android.Manifest.permission.LOADER_USAGE_STATS)
-    public void setStorageParams(int storageId, boolean enableReadLogs) throws ErrnoException {
-        try {
-            int res = mService.setStorageParams(storageId, enableReadLogs);
-            if (res < 0) {
-                throw new ErrnoException("setStorageParams", -res);
-            }
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-    }
-
     /* Native methods */
     private static native boolean nativeIsEnabled();
     private static native boolean nativeIsIncrementalPath(@NonNull String path);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e2d0c49..920302c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13757,6 +13757,16 @@
                 "show_notification_channel_warnings";
 
         /**
+         * When enabled, requires all notifications in the conversation section to be backed
+         * by a long-lived sharing shortcut
+         *
+         * The value 1 - require a shortcut, 0 - do not require a shortcut
+         * @hide
+         */
+        public static final String REQUIRE_SHORTCUTS_FOR_CONVERSATIONS =
+                "require_shortcuts_for_conversations";
+
+        /**
          * Whether cell is enabled/disabled
          * @hide
          */
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 05877a5..c047dc0 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -21,7 +21,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.Service;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.DataLoaderParams;
 import android.content.pm.DataLoaderParamsParcel;
@@ -32,8 +31,6 @@
 import android.content.pm.InstallationFileParcel;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
-import android.os.incremental.IncrementalManager;
-import android.system.ErrnoException;
 import android.util.ExceptionUtils;
 import android.util.Slog;
 
@@ -211,25 +208,6 @@
         private final long mNativeInstance;
     }
 
-    /* Used by native FileSystemConnector. */
-    private boolean setStorageParams(int storageId, boolean enableReadLogs) {
-        IncrementalManager incrementalManager = (IncrementalManager) getSystemService(
-                Context.INCREMENTAL_SERVICE);
-        if (incrementalManager == null) {
-            Slog.e(TAG, "Failed to obtain incrementalManager: " + storageId);
-            return false;
-        }
-        try {
-            // This has to be done directly in incrementalManager as the storage
-            // might be missing still.
-            incrementalManager.setStorageParams(storageId, enableReadLogs);
-        } catch (ErrnoException e) {
-            Slog.e(TAG, "Failed to set params for storage: " + storageId, e);
-            return false;
-        }
-        return true;
-    }
-
     /* Native methods */
     private native boolean nativeCreateDataLoader(int storageId,
             @NonNull FileSystemControlParcel control,
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 1beeb65..21b83c6 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -17,8 +17,6 @@
 package android.service.notification;
 
 import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
-import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ;
-import static android.util.FeatureFlagUtils.isEnabled;
 
 import android.annotation.NonNull;
 import android.app.Notification;
@@ -33,6 +31,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 
 import com.android.internal.logging.InstanceId;
@@ -477,9 +476,10 @@
      */
     public String getShortcutId(Context context) {
         String conversationId = getNotification().getShortcutId();
-        if (isEnabled(context,  NOTIF_CONVO_BYPASS_SHORTCUT_REQ)
-                && getNotification().getNotificationStyle() == Notification.MessagingStyle.class
-                && TextUtils.isEmpty(conversationId)) {
+        if (TextUtils.isEmpty(conversationId)
+                && (Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 0)
+                && getNotification().getNotificationStyle() == Notification.MessagingStyle.class) {
             conversationId = getId() + getTag() + PLACEHOLDER_CONVERSATION_ID;
         }
         return conversationId;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f531e12..f944dd7 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -55,6 +55,7 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
@@ -190,6 +191,7 @@
                 new DisplayCutout.ParcelableWrapper();
         DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
         final InsetsState mInsetsState = new InsetsState();
+        final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
         private final Point mSurfaceSize = new Point();
 
@@ -878,7 +880,7 @@
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
                                 mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
                                 mDisplayCutout, inputChannel,
-                                mInsetsState) < 0) {
+                                mInsetsState, mTempControls) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -903,7 +905,7 @@
                             View.VISIBLE, 0, -1, mWinFrame, mContentInsets,
                             mVisibleInsets, mStableInsets, mBackdropFrame,
                             mDisplayCutout, mMergedConfiguration, mSurfaceControl,
-                            mInsetsState, mSurfaceSize, mTmpSurfaceControl);
+                            mInsetsState, mTempControls, mSurfaceSize, mTmpSurfaceControl);
                     if (mSurfaceControl.isValid()) {
                         mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
                         mSurfaceControl.release();
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
index 9950143..b1647fe 100644
--- a/core/java/android/service/watchdog/ExplicitHealthCheckService.java
+++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -21,7 +21,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Service;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -64,6 +66,7 @@
  * </pre>
  * @hide
  */
+@TestApi
 @SystemApi
 public abstract class ExplicitHealthCheckService extends Service {
 
@@ -159,6 +162,15 @@
     }
 
     /**
+     * Sets {@link RemoteCallback}, for testing purpose.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setCallback(@Nullable RemoteCallback callback) {
+        mCallback = callback;
+    }
+    /**
      * Implementors should call this to notify the system when explicit health check passes
      * for {@code packageName};
      */
@@ -183,6 +195,7 @@
      *
      * @hide
      */
+    @TestApi
     @SystemApi
     public static final class PackageConfig implements Parcelable {
         private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(1);
@@ -263,7 +276,7 @@
         }
 
         @Override
-        public void writeToParcel(Parcel parcel, int flags) {
+        public void writeToParcel(@SuppressLint({"MissingNullability"}) Parcel parcel, int flags) {
             parcel.writeString(mPackageName);
             parcel.writeLong(mHealthCheckTimeoutMillis);
         }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index faa5cf5..cd20b35 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,8 +42,6 @@
     public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
     public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
     public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
-    public static final String NOTIF_CONVO_BYPASS_SHORTCUT_REQ =
-            "settings_notif_convo_bypass_shortcut_req";
     /** @hide */
     public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
             "settings_do_not_restore_preserved";
@@ -66,7 +64,6 @@
         DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "true");
         DEFAULT_FLAGS.put("settings_controller_loading_enhancement", "false");
         DEFAULT_FLAGS.put("settings_conditionals", "false");
-        DEFAULT_FLAGS.put(NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
         // This flags guards a feature introduced in R and will be removed in the next release
         // (b/148367230).
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0ccb1e0..4469fdb 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -253,12 +253,14 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @TestApi
     public static final int TYPE_UNKNOWN = 0;
 
     /**
      * Display type: Physical display connected through an internal port.
      * @hide
      */
+    @TestApi
     public static final int TYPE_INTERNAL = 1;
 
     /**
@@ -266,6 +268,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @TestApi
     public static final int TYPE_EXTERNAL = 2;
 
     /**
@@ -273,12 +276,14 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @TestApi
     public static final int TYPE_WIFI = 3;
 
     /**
      * Display type: Overlay display.
      * @hide
      */
+    @TestApi
     public static final int TYPE_OVERLAY = 4;
 
     /**
@@ -286,6 +291,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @TestApi
     public static final int TYPE_VIRTUAL = 5;
 
     /**
@@ -569,6 +575,7 @@
      * @hide
      */
     @UnsupportedAppUsage
+    @TestApi
     public int getType() {
         return mType;
     }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 45e51f7..81bfcb0 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -29,6 +29,7 @@
 import android.view.IWindowId;
 import android.view.MotionEvent;
 import android.view.WindowManager;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -46,7 +47,7 @@
             in int viewVisibility, in int layerStackId, out Rect outFrame,
             out Rect outContentInsets, out Rect outStableInsets,
             out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
-            out InsetsState insetsState);
+            out InsetsState insetsState, out InsetsSourceControl[] activeControls);
     int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outContentInsets,
             out Rect outStableInsets, out InsetsState insetsState);
@@ -106,8 +107,8 @@
             out Rect outBackdropFrame,
             out DisplayCutout.ParcelableWrapper displayCutout,
             out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
-            out InsetsState insetsState, out Point outSurfaceSize,
-            out SurfaceControl outBlastSurfaceControl);
+            out InsetsState insetsState, out InsetsSourceControl[] activeControls,
+            out Point outSurfaceSize, out SurfaceControl outBlastSurfaceControl);
 
     /*
      * Notify the window manager that an application is relaunching and
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 72ddaca..43c7bed 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -18,6 +18,7 @@
 
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.toInternalType;
 import static android.view.InsetsState.toPublicType;
 import static android.view.WindowInsets.Type.all;
 import static android.view.WindowInsets.Type.ime;
@@ -471,7 +472,7 @@
         if (!localStateChanged && mLastDispachedState.equals(state)) {
             return false;
         }
-        mState.set(state);
+        updateState(state);
         mLastDispachedState.set(state, true /* copySources */);
         applyLocalVisibilityOverride();
         if (localStateChanged) {
@@ -480,11 +481,25 @@
         if (!mState.equals(mLastDispachedState, true /* excludingCaptionInsets */)) {
             sendStateToWindowManager();
         }
+        return true;
+    }
+
+    private void updateState(InsetsState newState) {
+        mState.setDisplayFrame(newState.getDisplayFrame());
+        for (int i = newState.getSourcesCount() - 1; i >= 0; i--) {
+            InsetsSource source = newState.sourceAt(i);
+            getSourceConsumer(source.getType()).updateSource(source);
+        }
+        for (int i = mState.getSourcesCount() - 1; i >= 0; i--) {
+            InsetsSource source = mState.sourceAt(i);
+            if (newState.peekSource(source.getType()) == null) {
+                mState.removeSource(source.getType());
+            }
+        }
         if (mCaptionInsetsHeight != 0) {
             mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top,
                     mFrame.right, mFrame.top + mCaptionInsetsHeight));
         }
-        return true;
     }
 
     private boolean captionInsetsUnchanged() {
@@ -879,8 +894,15 @@
             control.cancel();
         }
         for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
-            if (mRunningAnimations.get(i).runner == control) {
+            RunningAnimation runningAnimation = mRunningAnimations.get(i);
+            if (runningAnimation.runner == control) {
                 mRunningAnimations.remove(i);
+                ArraySet<Integer> types = toInternalType(control.getTypes());
+                for (int j = types.size() - 1; j >= 0; j--) {
+                    if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) {
+                        mViewRoot.notifyInsetsChanged();
+                    }
+                }
                 break;
             }
         }
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index f74221d..6cb9374 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -16,11 +16,13 @@
 
 package android.view;
 
+import static android.view.InsetsController.ANIMATION_TYPE_NONE;
 import static android.view.InsetsController.AnimationType;
 import static android.view.InsetsState.toPublicType;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.graphics.Rect;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type.InsetsType;
@@ -64,6 +66,8 @@
     private final Supplier<Transaction> mTransactionSupplier;
     private @Nullable InsetsSourceControl mSourceControl;
     private boolean mHasWindowFocus;
+    private Rect mPendingFrame;
+    private Rect mPendingVisibleFrame;
 
     public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state,
             Supplier<Transaction> transactionSupplier, InsetsController controller) {
@@ -87,6 +91,8 @@
         if (mSourceControl == control) {
             return;
         }
+        SurfaceControl oldLeash = mSourceControl != null ? mSourceControl.getLeash() : null;
+
         final InsetsSourceControl lastControl = mSourceControl;
         mSourceControl = control;
 
@@ -112,6 +118,12 @@
                 // However make sure that the leash visibility is still up to date.
                 if (applyLocalVisibilityOverride()) {
                     mController.notifyVisibilityChanged();
+                }
+
+                // If we have a new leash, make sure visibility is up-to-date, even though we
+                // didn't want to run an animation above.
+                SurfaceControl newLeash = mSourceControl.getLeash();
+                if (oldLeash == null || newLeash == null || !oldLeash.isSameSurface(newLeash)) {
                     applyHiddenToControl();
                 }
             }
@@ -215,6 +227,38 @@
         // no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
     }
 
+    void updateSource(InsetsSource newSource) {
+        InsetsSource source = mState.peekSource(mType);
+        if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE
+                || source.getFrame().equals(newSource.getFrame())) {
+            mState.addSource(newSource);
+            return;
+        }
+
+        // Frame is changing while animating. Keep note of the new frame but keep existing frame
+        // until animaition is finished.
+        newSource = new InsetsSource(newSource);
+        mPendingFrame = new Rect(newSource.getFrame());
+        mPendingVisibleFrame = newSource.getVisibleFrame() != null
+                ? new Rect(newSource.getVisibleFrame())
+                : null;
+        newSource.setFrame(source.getFrame());
+        newSource.setVisibleFrame(source.getVisibleFrame());
+        mState.addSource(newSource);
+    }
+
+    boolean notifyAnimationFinished() {
+        if (mPendingFrame != null) {
+            InsetsSource source = mState.getSource(mType);
+            source.setFrame(mPendingFrame);
+            source.setVisibleFrame(mPendingVisibleFrame);
+            mPendingFrame = null;
+            mPendingVisibleFrame = null;
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Sets requested visibility from the client, regardless of whether we are able to control it at
      * the moment.
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index f3ec65f..e001b66 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -44,8 +44,7 @@
     public InsetsSourceControl(InsetsSourceControl other) {
         mType = other.mType;
         if (other.mLeash != null) {
-            mLeash = new SurfaceControl();
-            mLeash.copyFrom(other.mLeash);
+            mLeash = new SurfaceControl(other.mLeash);
         } else {
             mLeash = null;
         }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a37c1cb..ab65e3a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -32,6 +32,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
 import android.graphics.ColorSpace;
@@ -215,6 +216,10 @@
 
     private static native void nativeSetFrameRate(
             long transactionObj, long nativeObject, float frameRate, int compatibility);
+    private static native long nativeGetHandle(long nativeObject);
+
+    private static native long nativeAcquireFrameRateFlexibilityToken();
+    private static native void nativeReleaseFrameRateFlexibilityToken(long token);
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private String mName;
@@ -222,6 +227,7 @@
      * @hide
      */
     public long mNativeObject;
+    private long mNativeHandle;
 
     // TODO: Move this to native.
     private final Object mSizeLock = new Object();
@@ -424,12 +430,13 @@
             mCloseGuard.open("release");
         }
         mNativeObject = nativeObject;
+        mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
     }
 
     /**
      * @hide
      */
-    public void copyFrom(SurfaceControl other) {
+    public void copyFrom(@NonNull SurfaceControl other) {
         mName = other.mName;
         mWidth = other.mWidth;
         mHeight = other.mHeight;
@@ -849,23 +856,19 @@
             throw new OutOfResourcesException(
                     "Couldn't allocate SurfaceControl native object");
         }
-
+        mNativeHandle = nativeGetHandle(mNativeObject);
         mCloseGuard.open("release");
     }
 
-    /** This is a transfer constructor, useful for transferring a live SurfaceControl native
-     * object to another Java wrapper which could have some different behavior, e.g.
-     * event logging.
+    /**
+     * Copy constructor. Creates a new native object pointing to the same surface as {@code other}.
+     *
+     * @param other The object to copy the surface from.
      * @hide
      */
-    public SurfaceControl(SurfaceControl other) {
-        mName = other.mName;
-        mWidth = other.mWidth;
-        mHeight = other.mHeight;
-        mNativeObject = other.mNativeObject;
-        other.mCloseGuard.close();
-        other.mNativeObject = 0;
-        mCloseGuard.open("release");
+    @TestApi
+    public SurfaceControl(@NonNull SurfaceControl other) {
+        copyFrom(other);
     }
 
     private SurfaceControl(Parcel in) {
@@ -917,6 +920,18 @@
     }
 
     /**
+     * Checks whether two {@link SurfaceControl} objects represent the same surface.
+     *
+     * @param other The other object to check
+     * @return {@code true} if these two {@link SurfaceControl} objects represent the same surface.
+     * @hide
+     */
+    @TestApi
+    public boolean isSameSurface(@NonNull SurfaceControl other) {
+        return other.mNativeHandle == mNativeHandle;
+    }
+
+    /**
      * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link
      * android.view.SurfaceControlProto}.
      *
@@ -973,6 +988,7 @@
         if (mNativeObject != 0) {
             nativeRelease(mNativeObject);
             mNativeObject = 0;
+            mNativeHandle = 0;
             mCloseGuard.close();
         }
     }
@@ -2868,4 +2884,24 @@
             }
         }
     }
+
+    /**
+     * Acquire a frame rate flexibility token, which allows surface flinger to freely switch display
+     * frame rates. This is used by CTS tests to put the device in a consistent state. See
+     * ISurfaceComposer::acquireFrameRateFlexibilityToken().
+     * @hide
+     */
+    @TestApi
+    public static long acquireFrameRateFlexibilityToken() {
+        return nativeAcquireFrameRateFlexibilityToken();
+    }
+
+    /**
+     * Release a frame rate flexibility token.
+     * @hide
+     */
+    @TestApi
+    public static void releaseFrameRateFlexibilityToken(long token) {
+        nativeReleaseFrameRateFlexibilityToken(token);
+    }
 }
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index fe70ff7..174165c 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -124,8 +124,14 @@
     /** @hide */
     public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
             @NonNull WindowlessWindowManager wwm) {
+        this(c, d, wwm, false /* useSfChoreographer */);
+    }
+
+    /** @hide */
+    public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
+            @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) {
         mWm = wwm;
-        mViewRoot = new ViewRootImpl(c, d, mWm);
+        mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer);
         mViewRoot.forceDisableBLAST();
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index c89e0c9..b677ccd 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1037,7 +1037,7 @@
                             mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
                                     mSurfaceHeight);
                         }
-                    } else if ((layoutSizeChanged || positionChanged) &&
+                    } else if ((layoutSizeChanged || positionChanged || visibleChanged) &&
                             viewRoot.useBLAST()) {
                         viewRoot.setUseBLASTSyncTransaction();
                     }
@@ -1241,7 +1241,7 @@
     private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
             Rect position, long frameNumber) {
         final ViewRootImpl viewRoot = getViewRootImpl();
-        if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) {
+        if (frameNumber > 0 && viewRoot != null && !viewRoot.isDrawingToBLASTTransaction()) {
             t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
                     frameNumber);
         }
@@ -1258,7 +1258,7 @@
 
     private void setParentSpaceRectangle(Rect position, long frameNumber) {
         final ViewRootImpl viewRoot = getViewRootImpl();
-        final boolean useBLAST = viewRoot.useBLAST();
+        final boolean useBLAST = viewRoot.isDrawingToBLASTTransaction();
         final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
             mRtTransaction;
 
@@ -1319,7 +1319,7 @@
         @Override
         public void positionLost(long frameNumber) {
             final ViewRootImpl viewRoot = getViewRootImpl();
-            boolean useBLAST = viewRoot != null && viewRoot.useBLAST();
+            boolean useBLAST = viewRoot != null && viewRoot.isDrawingToBLASTTransaction();
             if (DEBUG) {
                 Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
                         System.identityHashCode(this), frameNumber));
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3534bb0..750b1ed 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,6 +21,7 @@
 import static android.view.InputDevice.SOURCE_CLASS_NONE;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.LAST_TYPE;
 import static android.view.View.PFLAG_DRAW_ANIMATION;
 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -449,7 +450,7 @@
     InputQueue mInputQueue;
     @UnsupportedAppUsage
     FallbackEventHandler mFallbackEventHandler;
-    Choreographer mChoreographer;
+    final Choreographer mChoreographer;
 
     // used in relayout to get SurfaceControl size
     // for BLAST adapter surface setup
@@ -558,7 +559,8 @@
     final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
             new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
     boolean mPendingAlwaysConsumeSystemBars;
-    private InsetsState mTempInsets = new InsetsState();
+    private final InsetsState mTempInsets = new InsetsState();
+    private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[LAST_TYPE + 1];
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
@@ -692,11 +694,18 @@
     private SurfaceControl.Transaction mRtBLASTSyncTransaction = new SurfaceControl.Transaction();
 
     private String mTag = TAG;
+
     public ViewRootImpl(Context context, Display display) {
-        this(context, display, WindowManagerGlobal.getWindowSession());
+        this(context, display, WindowManagerGlobal.getWindowSession(),
+                false /* useSfChoreographer */);
     }
 
     public ViewRootImpl(Context context, Display display, IWindowSession session) {
+        this(context, display, session, false /* useSfChoreographer */);
+    }
+
+    public ViewRootImpl(Context context, Display display, IWindowSession session,
+            boolean useSfChoreographer) {
         mContext = context;
         mWindowSession = session;
         mDisplay = display;
@@ -732,7 +741,8 @@
         mDensity = context.getResources().getDisplayMetrics().densityDpi;
         mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
         mFallbackEventHandler = new PhoneFallbackEventHandler(context);
-        mChoreographer = Choreographer.getInstance();
+        mChoreographer = useSfChoreographer
+                ? Choreographer.getSfInstance() : Choreographer.getInstance();
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         mInsetsController = new InsetsController(this);
 
@@ -995,7 +1005,7 @@
                             getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                             mAttachInfo.mDisplayCutout, inputChannel,
-                            mTempInsets);
+                            mTempInsets, mTempControls);
                     setFrame(mTmpFrame);
                 } catch (RemoteException e) {
                     mAdded = false;
@@ -1020,6 +1030,7 @@
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0;
                 mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
                 mInsetsController.onStateChanged(mTempInsets);
+                mInsetsController.onControlsChanged(mTempControls);
                 if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
                     mAttachInfo.mRootView = null;
@@ -7348,7 +7359,7 @@
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                 mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
                 mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
-                mSurfaceSize, mBlastSurfaceControl);
+                mTempControls, mSurfaceSize, mBlastSurfaceControl);
         if (mSurfaceControl.isValid()) {
             if (!mUseBLASTAdapter) {
                 mSurface.copyFrom(mSurfaceControl);
@@ -7378,6 +7389,7 @@
         }
         setFrame(mTmpFrame);
         mInsetsController.onStateChanged(mTempInsets);
+        mInsetsController.onControlsChanged(mTempControls);
         return relayoutResult;
     }
 
@@ -9602,4 +9614,12 @@
     boolean useBLAST() {
         return mUseBLASTAdapter;
     }
+
+    /**
+     * Returns true if we are about to or currently processing a draw directed
+     * in to a BLAST transaction.
+     */
+    boolean isDrawingToBLASTTransaction() {
+        return mNextReportConsumeBLAST;
+    }
 }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 144f8e3..39ed401 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -104,7 +104,7 @@
             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
             Rect outStableInsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState) {
+            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
         final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
                 .setParent(mRootSurface)
                 .setFormat(attrs.format)
@@ -179,7 +179,8 @@
             Rect outStableInsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-            Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
+            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
+            SurfaceControl outBLASTSurfaceControl) {
         final State state;
         synchronized (this) {
             state = mStateForWindow.get(window.asBinder());
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index dc87453..6a10925 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1275,7 +1275,14 @@
 
     /**
      * Register the provided {@link RemoteAction} with the given actionId
-     *
+     * <p>
+     * To perform established system actions, an accessibility service uses the GLOBAL_ACTION
+     * constants in {@link android.accessibilityservice.AccessibilityService}. To provide a
+     * customized implementation for one of these actions, the id of the registered system action
+     * must match that of the corresponding GLOBAL_ACTION constant. For example, to register a
+     * Back action, {@code actionId} must be
+     * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK}
+     * </p>
      * @param action The remote action to be registered with the given actionId as system action.
      * @param actionId The id uniquely identify the system action.
      * @hide
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 71ccb59..849488d 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -22,6 +22,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
@@ -484,21 +485,21 @@
         super.onInitializeAccessibilityNodeInfo(info);
         if (this.getOrientation() == HORIZONTAL) {
             info.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(1,
-                    getVisibleChildCount(), false,
+                    getVisibleChildWithTextCount(), false,
                     AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_SINGLE));
         } else {
             info.setCollectionInfo(
-                    AccessibilityNodeInfo.CollectionInfo.obtain(getVisibleChildCount(),
+                    AccessibilityNodeInfo.CollectionInfo.obtain(getVisibleChildWithTextCount(),
                     1, false,
                     AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_SINGLE));
         }
     }
 
-    private int getVisibleChildCount() {
+    private int getVisibleChildWithTextCount() {
         int count = 0;
         for (int i = 0; i < getChildCount(); i++) {
             if (this.getChildAt(i) instanceof RadioButton) {
-                if (((RadioButton) this.getChildAt(i)).getVisibility() == VISIBLE) {
+                if (isVisibleWithText((RadioButton) this.getChildAt(i))) {
                     count++;
                 }
             }
@@ -513,15 +514,19 @@
         int index = 0;
         for (int i = 0; i < getChildCount(); i++) {
             if (this.getChildAt(i) instanceof RadioButton) {
-                RadioButton radioButton = (RadioButton) this.getChildAt(i);
-                if (radioButton == child) {
+                RadioButton button = (RadioButton) this.getChildAt(i);
+                if (button == child) {
                     return index;
                 }
-                if (radioButton.getVisibility() == VISIBLE) {
+                if (isVisibleWithText(button)) {
                     index++;
                 }
             }
         }
         return -1;
     }
+
+    private boolean isVisibleWithText(RadioButton button) {
+        return button.getVisibility() == VISIBLE && !TextUtils.isEmpty(button.getText());
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 08b3293..fb96210 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -43,7 +43,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -610,10 +610,10 @@
          */
         TN(Context context, String packageName, Binder token, List<Callback> callbacks,
                 @Nullable Looper looper) {
-            WindowManager windowManager = context.getSystemService(WindowManager.class);
-            AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(context);
-            mPresenter = new ToastPresenter(context, windowManager, accessibilityManager,
-                    getService(), packageName);
+            IAccessibilityManager accessibilityManager = IAccessibilityManager.Stub.asInterface(
+                    ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
+            mPresenter = new ToastPresenter(context, accessibilityManager, getService(),
+                    packageName);
             mParams = mPresenter.getLayoutParams();
             mPackageName = packageName;
             mToken = token;
diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java
index e9d4aa6..2679c69 100644
--- a/core/java/android/widget/ToastPresenter.java
+++ b/core/java/android/widget/ToastPresenter.java
@@ -27,6 +27,7 @@
 import android.graphics.PixelFormat;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -34,8 +35,10 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 
 /**
@@ -49,12 +52,14 @@
     private static final long SHORT_DURATION_TIMEOUT = 4000;
     private static final long LONG_DURATION_TIMEOUT = 7000;
 
+    @VisibleForTesting
+    public static final int TEXT_TOAST_LAYOUT = R.layout.transient_notification;
+
     /**
      * Returns the default text toast view for message {@code text}.
      */
     public static View getTextToastView(Context context, CharSequence text) {
-        View view = LayoutInflater.from(context).inflate(
-                R.layout.transient_notification, null);
+        View view = LayoutInflater.from(context).inflate(TEXT_TOAST_LAYOUT, null);
         TextView textView = view.findViewById(com.android.internal.R.id.message);
         textView.setText(text);
         return view;
@@ -70,15 +75,23 @@
     @Nullable private View mView;
     @Nullable private IBinder mToken;
 
-    public ToastPresenter(Context context, WindowManager windowManager,
-            AccessibilityManager accessibilityManager,
+    public ToastPresenter(Context context, IAccessibilityManager accessibilityManager,
             INotificationManager notificationManager, String packageName) {
         mContext = context;
         mResources = context.getResources();
-        mWindowManager = windowManager;
-        mAccessibilityManager = accessibilityManager;
+        mWindowManager = context.getSystemService(WindowManager.class);
         mNotificationManager = notificationManager;
         mPackageName = packageName;
+
+        // We obtain AccessibilityManager manually via its constructor instead of using method
+        // AccessibilityManager.getInstance() for 2 reasons:
+        //   1. We want to be able to inject IAccessibilityManager in tests to verify behavior.
+        //   2. getInstance() caches the instance for the process even if we pass a different
+        //      context to it. This is problematic for multi-user because callers can pass a context
+        //      created via Context.createContextAsUser().
+        mAccessibilityManager = new AccessibilityManager(context, accessibilityManager,
+                UserHandle.getCallingUserId());
+
         mParams = createLayoutParams();
     }
 
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index eee222b..6ae70b7 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -33,8 +33,8 @@
     public static final int FEATURE_SYSTEM_FIRST = 0;
     // The Root display area on a display
     public static final int FEATURE_ROOT = FEATURE_SYSTEM_FIRST;
-    // Display area hosting the task container.
-    public static final int FEATURE_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1;
+    // Display area hosting the default task container.
+    public static final int FEATURE_DEFAULT_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1;
     // Display area hosting non-activity window tokens.
     public static final int FEATURE_WINDOW_TOKENS = FEATURE_SYSTEM_FIRST + 2;
 
diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java
index 39a0101..2091c93 100644
--- a/core/java/android/window/TaskOrganizerTaskEmbedder.java
+++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java
@@ -254,9 +254,7 @@
             mTaskToken = taskInfo.token;
             mTaskLeash = mTaskToken.getLeash();
             mTransaction.reparent(mTaskLeash, mSurfaceControl)
-                    .show(mTaskLeash)
-                    .show(mSurfaceControl)
-                    .apply();
+                    .show(mSurfaceControl).apply();
             if (mPendingNotifyBoundsChanged) {
                 // TODO: Either defer show or hide and synchronize show with the resize
                 notifyBoundsChanged();
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index 9fc0da8..d43333e 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -55,6 +55,7 @@
     private static final String TAG = "AbstractMultiProfilePagerAdapter";
     static final int PROFILE_PERSONAL = 0;
     static final int PROFILE_WORK = 1;
+
     @IntDef({PROFILE_PERSONAL, PROFILE_WORK})
     @interface Profile {}
 
@@ -365,7 +366,7 @@
         UserHandle listUserHandle = listAdapter.getUserHandle();
         if (!listUserHandle.equals(mWorkProfileUserHandle)
                 || !mInjector.isQuietModeEnabled(mWorkProfileUserHandle)
-                || !hasResolvedAppsInWorkProfile(listAdapter)) {
+                || listAdapter.getCount() == 0) {
             return false;
         }
         DevicePolicyEventLogger
@@ -382,20 +383,6 @@
         return true;
     }
 
-    /**
-     * Returns {@code true} if there is at least one app resolved in the work profile,
-     * regardless of whether the work profile is enabled or not.
-     */
-    private boolean hasResolvedAppsInWorkProfile(ResolverListAdapter listAdapter) {
-        List<ResolverActivity.ResolvedComponentInfo> userStateIndependentWorkResolvers =
-                listAdapter.mResolverListController.getUserStateIndependentResolversAsUser(
-                        listAdapter.getIntents(), mWorkProfileUserHandle);
-        return userStateIndependentWorkResolvers.stream()
-                .anyMatch(resolvedComponentInfo ->
-                        resolvedComponentInfo.getResolveInfoAt(0).targetUserId
-                                == UserHandle.USER_CURRENT);
-    }
-
     private void maybeShowNoAppsAvailableEmptyState(ResolverListAdapter listAdapter) {
         UserHandle listUserHandle = listAdapter.getUserHandle();
         if (mWorkProfileUserHandle != null
@@ -529,6 +516,13 @@
         return false;
     }
 
+    boolean shouldShowEmptyStateScreen(ResolverListAdapter listAdapter) {
+        int count = listAdapter.getUnfilteredCount();
+        return (count == 0 && listAdapter.getPlaceholderCount() == 0)
+                || (listAdapter.getUserHandle().equals(mWorkProfileUserHandle)
+                    && isQuietModeEnabled(mWorkProfileUserHandle));
+    }
+
     protected class ProfileDescriptor {
         final ViewGroup rootView;
         private final ViewGroup mEmptyStateView;
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 553721d7..be43e82 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -286,6 +286,7 @@
         if (mTitleView != null) {
             mTitleView.setText(title);
         }
+        mWindow.setTitle(title);
     }
 
     /**
diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java
index dc48244..088973c 100644
--- a/core/java/com/android/internal/app/ChooserActivityLogger.java
+++ b/core/java/com/android/internal/app/ChooserActivityLogger.java
@@ -191,6 +191,9 @@
      * ChooserActivity.
      */
     default int typeFromIntentString(String intent) {
+        if (intent == null) {
+            return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_DEFAULT;
+        }
         switch (intent) {
             case Intent.ACTION_VIEW:
                 return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_VIEW;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 35253b6..fc3f20f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -994,7 +994,7 @@
         if (isAutolaunching() || maybeAutolaunchActivity()) {
             return;
         }
-        if (isResolverListEmpty(listAdapter)) {
+        if (mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(listAdapter)) {
             mMultiProfilePagerAdapter.showEmptyResolverListEmptyState(listAdapter);
         } else {
             mMultiProfilePagerAdapter.showListView(listAdapter);
@@ -1641,16 +1641,11 @@
 
     private void setupViewVisibilities() {
         ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
-        if (!isResolverListEmpty(activeListAdapter)) {
+        if (!mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)) {
             addUseDifferentAppLabelIfNecessary(activeListAdapter);
         }
     }
 
-    private boolean isResolverListEmpty(ResolverListAdapter listAdapter) {
-        int count = listAdapter.getUnfilteredCount();
-        return count == 0 && listAdapter.getPlaceholderCount() == 0;
-    }
-
     /**
      * Add a label to signify that the user can pick a different app.
      * @param adapter The adapter used to provide data to item views.
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 3f897a5..033ac72 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -121,23 +121,13 @@
             List<Intent> intents,
             UserHandle userHandle) {
         int baseFlags = PackageManager.MATCH_DEFAULT_ONLY
+                | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                 | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
                 | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0);
         return getResolversForIntentAsUserInternal(intents, userHandle, baseFlags);
     }
 
-    /**
-     * Returns a list of resolved intents which is user state-independent. This means it will
-     * return the same results regardless of whether the {@code userHandle} user is disabled or not.
-     */
-    public List<ResolverActivity.ResolvedComponentInfo> getUserStateIndependentResolversAsUser(
-            List<Intent> intents,
-            UserHandle userHandle) {
-        int baseFlags = PackageManager.MATCH_DIRECT_BOOT_AWARE
-                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
-        return getResolversForIntentAsUserInternal(intents, userHandle, baseFlags);
-    }
-
     private List<ResolverActivity.ResolvedComponentInfo> getResolversForIntentAsUserInternal(
             List<Intent> intents,
             UserHandle userHandle,
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index ff03f1a..505a05e 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -205,9 +205,15 @@
     /** List of packages with the same uid, and its app data info: volume uuid and inode. */
     public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
 
+    /** List of whitelisted packages and its app data info: volume uuid and inode. */
+    public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map";
+
     /** Bind mount app storage dirs to lower fs not via fuse */
     public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
 
+    /** Bind mount app storage dirs to lower fs not via fuse */
+    public static final String BIND_MOUNT_APP_DATA_DIRS = "--bind-mount-data-dirs";
+
     /**
      * An extraArg passed when a zygote process is forking a child-zygote, specifying a name
      * in the abstract socket namespace. This socket name is what the new child zygote
@@ -313,6 +319,8 @@
      * @param isTopApp true if the process is for top (high priority) application.
      * @param pkgDataInfoList A list that stores related packages and its app data
      * info: volume uuid and inode.
+     * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+     * @param bindMountAppDataDirs  True if the zygote needs to mount data dirs.
      * @param bindMountAppStorageDirs  True if the zygote needs to mount storage dirs.
      *
      * @return 0 if this is the child, pid of the child
@@ -321,13 +329,15 @@
     static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
             int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
-            boolean isTopApp, String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
+            boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+            boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
         ZygoteHooks.preFork();
 
         int pid = nativeForkAndSpecialize(
                 uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                 fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
-                pkgDataInfoList, bindMountAppStorageDirs);
+                pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
+                bindMountAppStorageDirs);
         if (pid == 0) {
             // Note that this event ends at the end of handleChildProc,
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -344,6 +354,7 @@
             int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
             int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
             String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
+            String[] whitelistedDataInfoList, boolean bindMountAppDataDirs,
             boolean bindMountAppStorageDirs);
 
     /**
@@ -371,15 +382,19 @@
      * volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
      * app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
      * app_b_ce_inode, ...];
+     * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+     * @param bindMountAppDataDirs  True if the zygote needs to mount data dirs.
      * @param bindMountAppStorageDirs  True if the zygote needs to mount storage dirs.
      */
     private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
-            String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
+            String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+            boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
         nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
                 niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
-                pkgDataInfoList, bindMountAppStorageDirs);
+                pkgDataInfoList, whitelistedDataInfoList,
+                bindMountAppDataDirs, bindMountAppStorageDirs);
 
         // Note that this event ends at the end of handleChildProc.
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -399,7 +414,8 @@
     private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
             int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
-            String[] pkgDataInfoList, boolean bindMountAppStorageDirs);
+            String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+            boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs);
 
     /**
      * Called to do any initialization before starting an application.
@@ -724,7 +740,8 @@
                                  args.mRuntimeFlags, rlimits, args.mMountExternal,
                                  args.mSeInfo, args.mNiceName, args.mStartChildZygote,
                                  args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
-                                 args.mPkgDataInfoList, args.mBindMountAppStorageDirs);
+                                 args.mPkgDataInfoList, args.mWhitelistedDataInfoList,
+                                 args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
 
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
@@ -1060,4 +1077,11 @@
      */
     @FastNative
     public static native int nativeParseSigChld(byte[] in, int length, int[] out);
+
+    /**
+     * Returns whether the kernel supports tagged pointers. Present in the
+     * Android Common Kernel from 4.14 and up. By default, you should prefer
+     * fully-feature Memory Tagging, rather than the static Tagged Pointers.
+     */
+    public static native boolean nativeSupportsTaggedPointers();
 }
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 1a63765..94c1f71 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -227,11 +227,22 @@
     String[] mPkgDataInfoList;
 
     /**
+     * A list that stores all whitelisted app data info: volume uuid and inode.
+     * Null if it does need to do app data isolation.
+     */
+    String[] mWhitelistedDataInfoList;
+
+    /**
      * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
      */
     boolean mBindMountAppStorageDirs;
 
     /**
+     * @see Zygote#BIND_MOUNT_APP_DATA_DIRS
+     */
+    boolean mBindMountAppDataDirs;
+
+    /**
      * Constructs instance and parses args
      *
      * @param args zygote command-line args
@@ -452,8 +463,12 @@
                 }
             } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
                 mPkgDataInfoList = getAssignmentList(arg);
+            } else if (arg.startsWith(Zygote.WHITELISTED_DATA_INFO_MAP)) {
+                mWhitelistedDataInfoList = getAssignmentList(arg);
             } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
                 mBindMountAppStorageDirs = true;
+            } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
+                mBindMountAppDataDirs = true;
             } else {
                 break;
             }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index bc8dfd4..e6a3029 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -258,7 +258,8 @@
                 parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                 parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                 parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
-                parsedArgs.mPkgDataInfoList, parsedArgs.mBindMountAppStorageDirs);
+                parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
+                parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
 
         try {
             if (pid == 0) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index ec1f516..c2b13c9 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -757,9 +757,11 @@
             Zygote.applyDebuggerSystemProperty(parsedArgs);
             Zygote.applyInvokeWithSystemProperty(parsedArgs);
 
-            /* Enable pointer tagging in the system server unconditionally. Hardware support for
-             * this is present in all ARMv8 CPUs; this flag has no effect on other platforms. */
-            parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+            if (Zygote.nativeSupportsTaggedPointers()) {
+                /* Enable pointer tagging in the system server. Hardware support for this is present
+                 * in all ARMv8 CPUs. */
+                parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+            }
 
             /* Enable gwp-asan on the system server with a small probability. This is the same
              * policy as applied to native processes and system apps. */
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 5b79b18..38f5f32 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -204,8 +204,8 @@
     /**
      * Displays a text toast.
      */
-    void showToast(String packageName, IBinder token, CharSequence text, IBinder windowToken,
-            int duration, @nullable ITransientNotificationCallback callback);
+    void showToast(int uid, String packageName, IBinder token, CharSequence text,
+            IBinder windowToken, int duration, @nullable ITransientNotificationCallback callback);
 
     /**
      * Cancels toast with token {@code token} in {@code packageName}.
diff --git a/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java b/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java
deleted file mode 100644
index 924b3f7..0000000
--- a/core/java/com/android/internal/view/SurfaceFlingerVsyncChoreographer.java
+++ /dev/null
@@ -1,94 +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.internal.view;
-
-import android.os.Handler;
-import android.os.Message;
-import android.view.Choreographer;
-import android.view.Display;
-
-/**
- * Utility class to schedule things at vsync-sf instead of vsync-app
- * @hide
- */
-public class SurfaceFlingerVsyncChoreographer {
-
-    private static final long ONE_MS_IN_NS = 1000000;
-    private static final long ONE_S_IN_NS = ONE_MS_IN_NS * 1000;
-
-    private final Handler mHandler;
-    private final Choreographer mChoreographer;
-
-    /**
-     * The offset between vsync-app and vsync-surfaceflinger. See
-     * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary.
-     */
-    private long mSurfaceFlingerOffsetMs;
-
-    public SurfaceFlingerVsyncChoreographer(Handler handler, Display display,
-            Choreographer choreographer) {
-        mHandler = handler;
-        mChoreographer = choreographer;
-        mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs(display);
-    }
-
-    public long getSurfaceFlingerOffsetMs() {
-        return mSurfaceFlingerOffsetMs;
-    }
-
-    /**
-     * This method calculates the offset between vsync-surfaceflinger and vsync-app. If vsync-app
-     * is a couple of milliseconds before vsync-sf, a touch or animation event that causes a surface
-     * flinger transaction are sometimes processed before the vsync-sf tick, and sometimes after,
-     * which leads to jank. Figure out this difference here and then post all the touch/animation
-     * events to start being processed at vsync-sf.
-     *
-     * @return The offset between vsync-app and vsync-sf, or 0 if vsync app happens after vsync-sf.
-     */
-    private long calculateAppSurfaceFlingerVsyncOffsetMs(Display display) {
-
-        // Calculate vsync offset from SurfaceFlinger.
-        // See frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:getDisplayConfigs
-        long vsyncPeriod = (long) (ONE_S_IN_NS / display.getRefreshRate());
-        long sfVsyncOffset = vsyncPeriod - (display.getPresentationDeadlineNanos() - ONE_MS_IN_NS);
-        return Math.max(0, (sfVsyncOffset - display.getAppVsyncOffsetNanos()) / ONE_MS_IN_NS);
-    }
-
-    public void scheduleAtSfVsync(Runnable r) {
-        final long delay = calculateDelay();
-        if (delay <= 0) {
-            r.run();
-        } else {
-            mHandler.postDelayed(r, delay);
-        }
-    }
-
-    public void scheduleAtSfVsync(Handler h, Message m) {
-        final long delay = calculateDelay();
-        if (delay <= 0) {
-            h.handleMessage(m);
-        } else {
-            m.setAsynchronous(true);
-            h.sendMessageDelayed(m, delay);
-        }
-    }
-
-    private long calculateDelay() {
-        final long sinceFrameStart = System.nanoTime() - mChoreographer.getLastFrameTimeNanos();
-        return mSurfaceFlingerOffsetMs - sinceFrameStart / 1000000;
-    }
-}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 523c749..2668420 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -40,7 +40,6 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.view.Gravity;
@@ -121,6 +120,7 @@
     private CachingIconView mConversationIconBadgeBg;
     private Icon mLargeIcon;
     private View mExpandButtonContainer;
+    private View mExpandButtonInnerContainer;
     private ViewGroup mExpandButtonAndContentContainer;
     private NotificationExpandButton mExpandButton;
     private MessagingLinearLayout mImageMessageContainer;
@@ -242,6 +242,7 @@
         mConversationHeader = findViewById(R.id.conversation_header);
         mContentContainer = findViewById(R.id.notification_action_list_margin_target);
         mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
+        mExpandButtonInnerContainer = findViewById(R.id.expand_button_inner_container);
         mExpandButton = findViewById(R.id.expand_button);
         mExpandButtonExpandedTopMargin = getResources().getDimensionPixelSize(
                 R.dimen.conversation_expand_button_top_margin_expanded);
@@ -385,14 +386,17 @@
 
     /** @hide */
     public void setUnreadCount(int unreadCount) {
-        mUnreadBadge.setVisibility(mIsCollapsed && unreadCount > 1 ? VISIBLE : GONE);
-        CharSequence text = unreadCount >= 100
-                ? getResources().getString(R.string.unread_convo_overflow, 99)
-                : String.format(Locale.getDefault(), "%d", unreadCount);
-        mUnreadBadge.setText(text);
-        mUnreadBadge.setBackgroundTintList(ColorStateList.valueOf(mLayoutColor));
-        boolean needDarkText = ColorUtils.calculateLuminance(mLayoutColor) > 0.5f;
-        mUnreadBadge.setTextColor(needDarkText ? Color.BLACK : Color.WHITE);
+        boolean visible = mIsCollapsed && unreadCount > 1;
+        mUnreadBadge.setVisibility(visible ? VISIBLE : GONE);
+        if (visible) {
+            CharSequence text = unreadCount >= 100
+                    ? getResources().getString(R.string.unread_convo_overflow, 99)
+                    : String.format(Locale.getDefault(), "%d", unreadCount);
+            mUnreadBadge.setText(text);
+            mUnreadBadge.setBackgroundTintList(ColorStateList.valueOf(mLayoutColor));
+            boolean needDarkText = ColorUtils.calculateLuminance(mLayoutColor) > 0.5f;
+            mUnreadBadge.setTextColor(needDarkText ? Color.BLACK : Color.WHITE);
+        }
     }
 
     private void addRemoteInputHistoryToMessages(
@@ -535,37 +539,26 @@
     }
 
     private void updateImageMessages() {
-        boolean displayExternalImage = false;
-        ArraySet<View> newMessages = new ArraySet<>();
-        if (mIsCollapsed) {
+        View newMessage = null;
+        if (mIsCollapsed && mGroups.size() > 0) {
 
-            // When collapsed, we're displaying all image messages in a dedicated container
-            // on the right of the layout instead of inline. Let's add all isolated images there
-            int imageIndex = 0;
-            for (int i = 0; i < mGroups.size(); i++) {
-                MessagingGroup messagingGroup = mGroups.get(i);
-                MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
-                if (isolatedMessage != null) {
-                    newMessages.add(isolatedMessage.getView());
-                    displayExternalImage = true;
-                    if (imageIndex
-                            != mImageMessageContainer.indexOfChild(isolatedMessage.getView())) {
-                        mImageMessageContainer.removeView(isolatedMessage.getView());
-                        mImageMessageContainer.addView(isolatedMessage.getView(), imageIndex);
-                    }
-                    imageIndex++;
-                }
+            // When collapsed, we're displaying the image message in a dedicated container
+            // on the right of the layout instead of inline. Let's add the isolated image there
+            MessagingGroup messagingGroup = mGroups.get(mGroups.size() -1);
+            MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
+            if (isolatedMessage != null) {
+                newMessage = isolatedMessage.getView();
             }
         }
         // Remove all messages that don't belong into the image layout
-        for (int i = 0; i < mImageMessageContainer.getChildCount(); i++) {
-            View child = mImageMessageContainer.getChildAt(i);
-            if (!newMessages.contains(child)) {
-                mImageMessageContainer.removeView(child);
-                i--;
+        View previousMessage = mImageMessageContainer.getChildAt(0);
+        if (previousMessage != newMessage) {
+            mImageMessageContainer.removeView(previousMessage);
+            if (newMessage != null) {
+                mImageMessageContainer.addView(newMessage);
             }
         }
-        mImageMessageContainer.setVisibility(displayExternalImage ? VISIBLE : GONE);
+        mImageMessageContainer.setVisibility(newMessage != null ? VISIBLE : GONE);
     }
 
     private void bindFacePile() {
@@ -1169,7 +1162,7 @@
         layoutParams.topMargin = topMargin;
         mExpandButton.setLayoutParams(layoutParams);
 
-        mExpandButtonContainer.setContentDescription(mContext.getText(contentDescriptionId));
+        mExpandButtonInnerContainer.setContentDescription(mContext.getText(contentDescriptionId));
     }
 
     private void updateContentEndPaddings() {
@@ -1213,7 +1206,7 @@
         mExpandable = expandable;
         if (expandable) {
             mExpandButtonContainer.setVisibility(VISIBLE);
-            mExpandButtonContainer.setOnClickListener(onClickListener);
+            mExpandButtonInnerContainer.setOnClickListener(onClickListener);
         } else {
             // TODO: handle content paddings to end of layout
             mExpandButtonContainer.setVisibility(GONE);
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index d40924b..21ca948 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -229,6 +229,7 @@
     private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>();
 
     private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>();
+    private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>();
 
     /**
      * Map of system pre-defined, uniquely named actors; keys are namespace,
@@ -394,6 +395,10 @@
         return mRollbackWhitelistedPackages;
     }
 
+    public Set<String> getWhitelistedStagedInstallers() {
+        return mWhitelistedStagedInstallers;
+    }
+
     public ArraySet<String> getAppDataIsolationWhitelistedApps() {
         return mAppDataIsolationWhitelistedApps;
     }
@@ -1137,6 +1142,20 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "whitelisted-staged-installer": {
+                        if (allowAppConfigs) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                            } else {
+                                mWhitelistedStagedInstallers.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     default: {
                         Slog.w(TAG, "Tag " + name + " is unknown in "
                                 + permFile + " at " + parser.getPositionDescription());
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b2ca0a7..1cfa12d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -29,11 +29,13 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_view_SurfaceSession.h>
+#include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <private/gui/ComposerService.h>
 #include <stdio.h>
 #include <system/graphics.h>
 #include <ui/ConfigStoreTypes.h>
@@ -624,6 +626,23 @@
     transaction->setFrameRate(ctrl, frameRate, static_cast<int8_t>(compatibility));
 }
 
+static jlong nativeAcquireFrameRateFlexibilityToken(JNIEnv* env, jclass clazz) {
+    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+    sp<IBinder> token;
+    status_t result = composer->acquireFrameRateFlexibilityToken(&token);
+    if (result < 0) {
+        ALOGE("Failed acquiring frame rate flexibility token: %s (%d)", strerror(-result), result);
+        return 0;
+    }
+    token->incStrong((void*)nativeAcquireFrameRateFlexibilityToken);
+    return reinterpret_cast<jlong>(token.get());
+}
+
+static void nativeReleaseFrameRateFlexibilityToken(JNIEnv* env, jclass clazz, jlong tokenLong) {
+    sp<IBinder> token(reinterpret_cast<IBinder*>(tokenLong));
+    token->decStrong((void*)nativeAcquireFrameRateFlexibilityToken);
+}
+
 static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
     const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
     jlongArray array = env->NewLongArray(displayIds.size());
@@ -1411,6 +1430,12 @@
 
     client->setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ, lightRadius);
 }
+
+static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
+    SurfaceControl *surfaceControl = reinterpret_cast<SurfaceControl*>(nativeObject);
+    return reinterpret_cast<jlong>(surfaceControl->getHandle().get());
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -1474,6 +1499,10 @@
             (void*)nativeSetShadowRadius },
     {"nativeSetFrameRate", "(JJFI)V",
             (void*)nativeSetFrameRate },
+    {"nativeAcquireFrameRateFlexibilityToken", "()J",
+            (void*)nativeAcquireFrameRateFlexibilityToken },
+    {"nativeReleaseFrameRateFlexibilityToken", "(J)V",
+            (void*)nativeReleaseFrameRateFlexibilityToken },
     {"nativeGetPhysicalDisplayIds", "()[J",
             (void*)nativeGetPhysicalDisplayIds },
     {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
@@ -1583,6 +1612,8 @@
             (void*)nativeMirrorSurface },
     {"nativeSetGlobalShadowSettings", "([F[FFFF)V",
             (void*)nativeSetGlobalShadowSettings },
+    {"nativeGetHandle", "(J)J",
+            (void*)nativeGetHandle },
 };
 
 int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index ea3c0fa..4b30359 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -110,7 +110,6 @@
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 using android::base::GetBoolProperty;
-using android::base::GetProperty;
 
 #define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
                               append(StringPrintf(__VA_ARGS__))
@@ -170,18 +169,6 @@
 
 static constexpr int DEFAULT_DATA_DIR_PERMISSION = 0751;
 
-/**
- * Property to control if app data isolation is enabled.
- */
-static const std::string ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
-    "persist.zygote.app_data_isolation";
-
-/**
- * Property to enable app data isolation for sdcard obb or data in vold.
- */
-static const std::string ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
-    "persist.sys.vold_app_data_isolation_enabled";
-
 static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000;
 static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF;
 
@@ -1319,20 +1306,13 @@
  * be decrypted after storage is decrypted.
  *
  */
-static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
-    uid_t uid, const char* process_name, jstring managed_nice_name,
-    fail_fn_t fail_fn) {
+static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_data_info_list,
+    uid_t uid, const char* process_name,
+    jstring managed_nice_name, fail_fn_t fail_fn) {
 
   const userid_t userId = multiuser_get_user_id(uid);
 
-  auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
-
-  int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
-  // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
-  if ((size % 3) != 0) {
-    fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size));
-  }
-  ensureInAppMountNamespace(fail_fn);
+  int size = merged_data_info_list.size();
 
   // Mount tmpfs on all possible data directories, so app no longer see the original apps data.
   char internalCePath[PATH_MAX];
@@ -1377,14 +1357,10 @@
   bool legacySymlinkCreated = false;
 
   for (int i = 0; i < size; i += 3) {
-    jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
-    std::string packageName = extract_fn(package_str).value();
+    std::string const & packageName = merged_data_info_list[i];
+    std::string const & volUuid  = merged_data_info_list[i + 1];
+    std::string const & inode = merged_data_info_list[i + 2];
 
-    jstring vol_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 1));
-    std::string volUuid = extract_fn(vol_str).value();
-
-    jstring inode_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 2));
-    std::string inode = extract_fn(inode_str).value();
     std::string::size_type sz;
     long long ceDataInode = std::stoll(inode, &sz);
 
@@ -1482,6 +1458,48 @@
   freecon(dataDataContext);
 }
 
+static void insertPackagesToMergedList(JNIEnv* env,
+  std::vector<std::string>& merged_data_info_list,
+  jobjectArray data_info_list, const char* process_name,
+  jstring managed_nice_name, fail_fn_t fail_fn) {
+
+  auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+
+  int size = (data_info_list != nullptr) ? env->GetArrayLength(data_info_list) : 0;
+  // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
+  if ((size % 3) != 0) {
+    fail_fn(CREATE_ERROR("Wrong data_info_list size %d", size));
+  }
+
+  for (int i = 0; i < size; i += 3) {
+    jstring package_str = (jstring) (env->GetObjectArrayElement(data_info_list, i));
+    std::string packageName = extract_fn(package_str).value();
+    merged_data_info_list.push_back(packageName);
+
+    jstring vol_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 1));
+    std::string volUuid = extract_fn(vol_str).value();
+    merged_data_info_list.push_back(volUuid);
+
+    jstring inode_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 2));
+    std::string inode = extract_fn(inode_str).value();
+    merged_data_info_list.push_back(inode);
+  }
+}
+
+static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
+    jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name,
+    jstring managed_nice_name, fail_fn_t fail_fn) {
+
+  ensureInAppMountNamespace(fail_fn);
+  std::vector<std::string> merged_data_info_list;
+  insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list,
+          process_name, managed_nice_name, fail_fn);
+  insertPackagesToMergedList(env, merged_data_info_list, whitelisted_data_info_list,
+          process_name, managed_nice_name, fail_fn);
+
+  isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+}
+
 /**
  * Like isolateAppData(), isolate jit profile directories, so apps don't see what
  * other apps are installed by reading content inside /data/misc/profiles/cur.
@@ -1594,7 +1612,9 @@
                              jstring managed_nice_name, bool is_system_server,
                              bool is_child_zygote, jstring managed_instruction_set,
                              jstring managed_app_data_dir, bool is_top_app,
-                             jobjectArray pkg_data_info_list, bool mount_storage_dirs) {
+                             jobjectArray pkg_data_info_list,
+                             jobjectArray whitelisted_data_info_list,
+                             bool mount_data_dirs, bool mount_storage_dirs) {
   const char* process_name = is_system_server ? "system_server" : "zygote";
   auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
   auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1628,9 +1648,9 @@
   // give a null in same_uid_pkgs and private_volumes so they don't need app data isolation.
   // Isolated process / webview / app zygote should be gated by SELinux and file permission
   // so they can't even traverse CE / DE directories.
-  if (pkg_data_info_list != nullptr
-      && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
-    isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+  if (mount_data_dirs) {
+    isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list,
+            uid, process_name, managed_nice_name, fail_fn);
     isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
   }
   if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) {
@@ -2003,7 +2023,8 @@
         jint mount_external, jstring se_info, jstring nice_name,
         jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
         jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
-        jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
+        jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+        jboolean mount_data_dirs, jboolean mount_storage_dirs) {
     jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
     if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -2041,6 +2062,8 @@
                        mount_external, se_info, nice_name, false,
                        is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
                        is_top_app == JNI_TRUE, pkg_data_info_list,
+                       whitelisted_data_info_list,
+                       mount_data_dirs == JNI_TRUE,
                        mount_storage_dirs == JNI_TRUE);
     }
     return pid;
@@ -2076,7 +2099,8 @@
                        permitted_capabilities, effective_capabilities,
                        MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
                        false, nullptr, nullptr, /* is_top_app= */ false,
-                       /* pkg_data_info_list */ nullptr, false);
+                       /* pkg_data_info_list */ nullptr,
+                       /* whitelisted_data_info_list */ nullptr, false, false);
   } else if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
@@ -2206,15 +2230,16 @@
     jint runtime_flags, jobjectArray rlimits,
     jint mount_external, jstring se_info, jstring nice_name,
     jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
-    jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
+    jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+    jboolean mount_data_dirs, jboolean mount_storage_dirs) {
   jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
   SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
                    capabilities, capabilities,
                    mount_external, se_info, nice_name, false,
                    is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
-                   is_top_app == JNI_TRUE, pkg_data_info_list,
-                   mount_storage_dirs == JNI_TRUE);
+                   is_top_app == JNI_TRUE, pkg_data_info_list, whitelisted_data_info_list,
+                   mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE);
 }
 
 /**
@@ -2405,10 +2430,19 @@
     return -1;
 }
 
+static jboolean com_android_internal_os_Zygote_nativeSupportsTaggedPointers(JNIEnv* env, jclass) {
+#ifdef __aarch64__
+  int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+  return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
+#else
+  return false;
+#endif
+}
+
 static const JNINativeMethod gMethods[] = {
         {"nativeForkAndSpecialize",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
-         "String;Z[Ljava/lang/String;Z)I",
+         "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I",
          (void*)com_android_internal_os_Zygote_nativeForkAndSpecialize},
         {"nativeForkSystemServer", "(II[II[[IJJ)I",
          (void*)com_android_internal_os_Zygote_nativeForkSystemServer},
@@ -2421,7 +2455,7 @@
         {"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
         {"nativeSpecializeAppProcess",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
-         "String;Z[Ljava/lang/String;Z)V",
+         "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
          (void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},
         {"nativeInitNativeState", "(Z)V",
          (void*)com_android_internal_os_Zygote_nativeInitNativeState},
@@ -2440,6 +2474,8 @@
          (void*)com_android_internal_os_Zygote_nativeBoostUsapPriority},
         {"nativeParseSigChld", "([BI[I)I",
          (void*)com_android_internal_os_Zygote_nativeParseSigChld},
+        {"nativeSupportsTaggedPointers", "()Z",
+         (void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
 };
 
 int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ab57e3d..6321651 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2669,4 +2669,9 @@
     // Open: Settings > Sound > Do Not Disturb > Apps > <Choose App>
     // OS: R
     DND_APPS_BYPASSING = 1840;
+
+    // OPEN: Settings > Battery > Advanced battery option
+    // CATEGORY: SETTINGS
+    // OS: R
+    FUELGAUGE_ADVANCED_BATTERY_OPTION = 1842;
 }
diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto
index 76f8f0f..61b9b25 100644
--- a/core/proto/android/stats/dnsresolver/dns_resolver.proto
+++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto
@@ -62,6 +62,13 @@
     NS_R_NOTAUTH = 9;   // Not authoritative for zone
     NS_R_NOTZONE = 10;  // Zone of record different from zone section
     NS_R_MAX = 11;
+    // Define rcode=12~15(UNASSIGNED) in rcode enum type.
+    // Some DNS Servers might return undefined code to devices.
+    // Without the enum definition, that would be noise for our dashboard.
+    NS_R_UNASSIGNED12 = 12; // Unassigned
+    NS_R_UNASSIGNED13 = 13; // Unassigned
+    NS_R_UNASSIGNED14 = 14; // Unassigned
+    NS_R_UNASSIGNED15 = 15; // Unassigned
     // The following are EDNS extended rcodes
     NS_R_BADVERS = 16;
     // The following are TSIG errors
@@ -170,12 +177,22 @@
     NT_BLUETOOTH = 3;
     // Indicates this network uses an Ethernet transport.
     NT_ETHERNET = 4;
-    // Indicates this network uses a VPN transport.
-    NT_VPN = 5;
+    // Indicates this network uses a VPN transport, now deprecated.
+    NT_VPN = 5 [deprecated=true];
     // Indicates this network uses a Wi-Fi Aware transport.
     NT_WIFI_AWARE = 6;
     // Indicates this network uses a LoWPAN transport.
     NT_LOWPAN = 7;
+    // Indicates this network uses a Cellular+VPN transport.
+    NT_CELLULAR_VPN = 8;
+    // Indicates this network uses a Wi-Fi+VPN transport.
+    NT_WIFI_VPN = 9;
+    // Indicates this network uses a Bluetooth+VPN transport.
+    NT_BLUETOOTH_VPN = 10;
+    // Indicates this network uses an Ethernet+VPN transport.
+    NT_ETHERNET_VPN = 11;
+    // Indicates this network uses a Wi-Fi+Cellular+VPN transport.
+    NT_WIFI_CELLULAR_VPN = 12;
 }
 
 enum CacheStatus{
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index b9ca292..e986b18 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -245,6 +245,7 @@
                     android:id="@+id/notification_messaging"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:minHeight="@dimen/notification_text_size"
                     android:spacing="@dimen/notification_messaging_spacing"
                     android:clipToPadding="false"
                     android:clipChildren="false"
@@ -263,53 +264,65 @@
         <include layout="@layout/notification_material_action_list" />
     </com.android.internal.widget.RemeasuringLinearLayout>
 
-    <!--This is dynamically placed between here and at the end of the layout-->
-    <LinearLayout
+    <!--This is dynamically placed between here and at the end of the layout. It starts here since
+        only FrameLayout layout params have gravity-->
+    <FrameLayout
         android:id="@+id/expand_button_container"
         android:layout_width="wrap_content"
-        android:layout_height="@dimen/conversation_expand_button_size"
+        android:layout_height="match_parent"
         android:layout_gravity="end|top"
-        android:paddingStart="16dp"
-        android:orientation="horizontal"
-        android:paddingEnd="@dimen/notification_content_margin_end"
-        android:clipToPadding="false"
         android:clipChildren="false"
-        >
-        <!-- Images -->
-        <com.android.internal.widget.MessagingLinearLayout
-            android:id="@+id/conversation_image_message_container"
-            android:forceHasOverlappingRendering="false"
-            android:layout_width="40dp"
-            android:layout_height="40dp"
-            android:layout_marginEnd="11dp"
-            android:spacing="0dp"
-            android:layout_gravity="center"
+        android:clipToPadding="false">
+        <!--This layout makes sure that we can nicely center the expand content in the
+            collapsed layout while the parent makes sure that we're never laid out bigger
+            than the messaging content.-->
+        <LinearLayout
+            android:id="@+id/expand_button_inner_container"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/conversation_expand_button_size"
+            android:paddingStart="16dp"
+            android:orientation="horizontal"
+            android:layout_gravity="end|top"
+            android:paddingEnd="@dimen/notification_content_margin_end"
             android:clipToPadding="false"
             android:clipChildren="false"
-            />
-        <!-- Unread Count -->
-        <TextView
-            android:id="@+id/conversation_unread_count"
-            android:layout_width="33sp"
-            android:layout_height="wrap_content"
-            android:layout_marginEnd="11dp"
-            android:layout_gravity="center"
-            android:gravity="center"
-            android:padding="2dp"
-            android:visibility="gone"
-            android:textAppearance="@style/TextAppearance.DeviceDefault.Notification"
-            android:textColor="#FFFFFF"
-            android:textSize="12sp"
-            android:background="@drawable/conversation_unread_bg"
-        />
-        <com.android.internal.widget.NotificationExpandButton
-            android:id="@+id/expand_button"
-            android:layout_width="@dimen/notification_header_expand_icon_size"
-            android:layout_height="@dimen/notification_header_expand_icon_size"
-            android:layout_gravity="center"
-            android:drawable="@drawable/ic_expand_notification"
-            android:clickable="false"
-            android:importantForAccessibility="no"
-        />
-    </LinearLayout>
+            >
+            <!-- Images -->
+            <com.android.internal.widget.MessagingLinearLayout
+                android:id="@+id/conversation_image_message_container"
+                android:forceHasOverlappingRendering="false"
+                android:layout_width="40dp"
+                android:layout_height="40dp"
+                android:layout_marginEnd="11dp"
+                android:spacing="0dp"
+                android:layout_gravity="center"
+                android:clipToPadding="false"
+                android:clipChildren="false"
+                />
+            <!-- Unread Count -->
+            <TextView
+                android:id="@+id/conversation_unread_count"
+                android:layout_width="33sp"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="11dp"
+                android:layout_gravity="center"
+                android:gravity="center"
+                android:padding="2dp"
+                android:visibility="gone"
+                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification"
+                android:textColor="#FFFFFF"
+                android:textSize="12sp"
+                android:background="@drawable/conversation_unread_bg"
+                />
+            <com.android.internal.widget.NotificationExpandButton
+                android:id="@+id/expand_button"
+                android:layout_width="@dimen/notification_header_expand_icon_size"
+                android:layout_height="@dimen/notification_header_expand_icon_size"
+                android:layout_gravity="center"
+                android:drawable="@drawable/ic_expand_notification"
+                android:clickable="false"
+                android:importantForAccessibility="no"
+                />
+        </LinearLayout>
+    </FrameLayout>
 </com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index c7e1a21..5890bed 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -19,61 +19,66 @@
     android:id="@+id/resolver_empty_state"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
     android:gravity="center_horizontal"
     android:visibility="gone"
-    android:paddingTop="48dp"
     android:paddingStart="24dp"
     android:paddingEnd="24dp">
-    <ImageView
-        android:id="@+id/resolver_empty_state_icon"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:layout_centerHorizontal="true" />
-    <TextView
-        android:id="@+id/resolver_empty_state_title"
-        android:layout_below="@+id/resolver_empty_state_icon"
-        android:layout_marginTop="8dp"
-        android:layout_width="wrap_content"
+    <RelativeLayout
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:fontFamily="@string/config_headlineFontFamilyMedium"
-        android:textColor="@color/resolver_empty_state_text"
-        android:textSize="14sp"
-        android:layout_centerHorizontal="true" />
-    <TextView
-        android:id="@+id/resolver_empty_state_subtitle"
-        android:layout_below="@+id/resolver_empty_state_title"
-        android:layout_marginTop="4dp"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textColor="@color/resolver_empty_state_text"
-        android:textSize="12sp"
-        android:gravity="center_horizontal"
-        android:layout_centerHorizontal="true" />
-    <Button
-        android:id="@+id/resolver_empty_state_button"
-        android:layout_below="@+id/resolver_empty_state_subtitle"
-        android:layout_marginTop="16dp"
-        android:text="@string/resolver_switch_on_work"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:fontFamily="@string/config_headlineFontFamilyMedium"
-        android:textSize="14sp"
-        android:textColor="?attr/colorAccent"
-        android:layout_centerHorizontal="true"
-        android:background="@drawable/resolver_turn_on_work_button_ripple_background"/>
-    <ProgressBar
-        android:id="@+id/resolver_empty_state_progress"
-        style="@style/Widget.Material.Light.ProgressBar"
-        android:visibility="gone"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:indeterminate="true"
-        android:layout_alignTop="@+id/resolver_empty_state_icon"
-        android:layout_alignBottom="@+id/resolver_empty_state_button"
-        android:layout_centerHorizontal="true"
-        android:layout_below="@+id/resolver_empty_state_subtitle"
-        android:indeterminateTint="?attr/colorAccent"/>
+        android:paddingTop="48dp"
+        android:paddingBottom="48dp"
+        android:gravity="center_horizontal">
+        <ImageView
+            android:id="@+id/resolver_empty_state_icon"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_centerHorizontal="true" />
+        <TextView
+            android:id="@+id/resolver_empty_state_title"
+            android:layout_below="@+id/resolver_empty_state_icon"
+            android:layout_marginTop="8dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@string/config_headlineFontFamilyMedium"
+            android:textColor="@color/resolver_empty_state_text"
+            android:textSize="14sp"
+            android:layout_centerHorizontal="true" />
+        <TextView
+            android:id="@+id/resolver_empty_state_subtitle"
+            android:layout_below="@+id/resolver_empty_state_title"
+            android:layout_marginTop="4dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/resolver_empty_state_text"
+            android:textSize="12sp"
+            android:gravity="center_horizontal"
+            android:layout_centerHorizontal="true" />
+        <Button
+            android:id="@+id/resolver_empty_state_button"
+            android:layout_below="@+id/resolver_empty_state_subtitle"
+            android:layout_marginTop="16dp"
+            android:text="@string/resolver_switch_on_work"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:fontFamily="@string/config_headlineFontFamilyMedium"
+            android:textSize="14sp"
+            android:textColor="?attr/colorAccent"
+            android:layout_centerHorizontal="true"
+            android:background="@drawable/resolver_turn_on_work_button_ripple_background"/>
+        <ProgressBar
+            android:id="@+id/resolver_empty_state_progress"
+            style="@style/Widget.Material.Light.ProgressBar"
+            android:visibility="gone"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:indeterminate="true"
+            android:layout_alignTop="@+id/resolver_empty_state_icon"
+            android:layout_alignBottom="@+id/resolver_empty_state_button"
+            android:layout_centerHorizontal="true"
+            android:layout_below="@+id/resolver_empty_state_subtitle"
+            android:indeterminateTint="?attr/colorAccent"/>
+    </RelativeLayout>
     <TextView android:id="@+id/empty"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 11dda41..ef1e8b7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3866,6 +3866,7 @@
   <java-symbol type="id" name="conversation_icon_badge_ring" />
   <java-symbol type="id" name="conversation_icon_badge_bg" />
   <java-symbol type="id" name="expand_button_container" />
+  <java-symbol type="id" name="expand_button_inner_container" />
   <java-symbol type="id" name="messaging_group_content_container" />
   <java-symbol type="id" name="expand_button_and_content_container" />
   <java-symbol type="id" name="conversation_header" />
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index b9c71a2..d432dda 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -32,6 +32,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -638,7 +639,31 @@
         });
     }
 
+    @Test
+    public void testFrameUpdateDuringAnimation() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
 
+            mController.onControlsChanged(createSingletonControl(ITYPE_IME));
+
+            // Pretend IME is calling
+            mController.show(ime(), true /* fromIme */);
+
+            InsetsState copy = new InsetsState(mController.getState(), true /* copySources */);
+            copy.getSource(ITYPE_IME).setFrame(0, 1, 2, 3);
+            copy.getSource(ITYPE_IME).setVisibleFrame(new Rect(4, 5, 6, 7));
+            mController.onStateChanged(copy);
+            assertNotEquals(new Rect(0, 1, 2, 3),
+                    mController.getState().getSource(ITYPE_IME).getFrame());
+            assertNotEquals(new Rect(4, 5, 6, 7),
+                    mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+            mController.cancelExistingAnimation();
+            assertEquals(new Rect(0, 1, 2, 3),
+                    mController.getState().getSource(ITYPE_IME).getFrame());
+            assertEquals(new Rect(4, 5, 6, 7),
+                    mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
 
     @Test
     public void testCaptionInsetsStateAssemble() {
@@ -671,8 +696,7 @@
 
         // Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will
         // attempt to release mLeash directly.
-        SurfaceControl copy = new SurfaceControl();
-        copy.copyFrom(mLeash);
+        SurfaceControl copy = new SurfaceControl(mLeash);
         return new InsetsSourceControl(type, copy, new Point());
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index bc0cdc1..583c751 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -1339,15 +1339,8 @@
                 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos =
                 createResolvedComponentsForTest(workProfileTargets);
-        when(sOverrides.workResolverListController.getUserStateIndependentResolversAsUser(
-                Mockito.isA(List.class),
-                Mockito.isA(UserHandle.class)))
-                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
         sOverrides.isQuietModeEnabled = true;
-        // When work profile is disabled, we get 0 results when we query the work profile
-        // intents.
-        setupResolverControllers(personalResolvedComponentInfos,
-                /* workResolvedComponentInfos */ new ArrayList<>());
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendTextIntent();
         sendIntent.setType("TestType");
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 0bf8663..eb39d58 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -614,15 +614,8 @@
                 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos =
                 createResolvedComponentsForTest(workProfileTargets);
-        when(sOverrides.workResolverListController.getUserStateIndependentResolversAsUser(
-                Mockito.isA(List.class),
-                Mockito.isA(UserHandle.class)))
-                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
         sOverrides.isQuietModeEnabled = true;
-        // When work profile is disabled, we get 0 results when we query the work profile
-        // intents.
-        setupResolverControllers(personalResolvedComponentInfos,
-                /* workResolvedComponentInfos */ new ArrayList<>());
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendImageIntent();
         sendIntent.setType("TestType");
 
diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp
index a2a2168..ca655f1 100644
--- a/core/xsd/vts/Android.bp
+++ b/core/xsd/vts/Android.bp
@@ -36,7 +36,7 @@
     ],
     test_suites: [
         "general-tests",
-        "vts-core"
+        "vts"
     ],
     test_config: "vts_permission_validate_test.xml",
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 59bdf3d..0389639 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -137,6 +137,7 @@
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
         <permission name="android.permission.PACKAGE_USAGE_STATS" />
+        <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.phone">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index b753704..07cf415 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -25,6 +25,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "-2101985723": {
+      "message": "Failed looking up window session=%s callers=%s",
+      "level": "WARN",
+      "group": "WM_ERROR",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "-2072089308": {
       "message": "Attempted to add window with token that is a sub-window: %s.  Aborting.",
       "level": "WARN",
@@ -655,12 +661,6 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-747671114": {
-      "message": "Failed looking up window callers=%s",
-      "level": "WARN",
-      "group": "WM_ERROR",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "-714291355": {
       "message": "Losing delayed focus: %s",
       "level": "INFO",
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ac2fd98..aa842ff 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -171,7 +171,6 @@
 
 cc_defaults {
     name: "android_graphics_apex",
-    host_supported: true,
     cflags: [
         "-Wno-unused-parameter",
         "-Wno-non-virtual-dtor",
@@ -231,7 +230,6 @@
 
 cc_defaults {
     name: "android_graphics_jni",
-    host_supported: true,
     cflags: [
         "-Wno-unused-parameter",
         "-Wno-non-virtual-dtor",
@@ -537,7 +535,11 @@
 
 cc_test {
     name: "hwui_unit_tests",
-    defaults: ["hwui_test_defaults"],
+    defaults: [
+        "hwui_test_defaults",
+        "android_graphics_apex",
+        "android_graphics_jni",
+    ],
 
     static_libs: [
         "libgmock",
@@ -549,6 +551,7 @@
 
     srcs: [
         "tests/unit/main.cpp",
+        "tests/unit/ABitmapTests.cpp",
         "tests/unit/CacheManagerTests.cpp",
         "tests/unit/CanvasContextTests.cpp",
         "tests/unit/CommonPoolTests.cpp",
diff --git a/libs/hwui/apex/android_bitmap.cpp b/libs/hwui/apex/android_bitmap.cpp
index b56a619..3780ba0 100644
--- a/libs/hwui/apex/android_bitmap.cpp
+++ b/libs/hwui/apex/android_bitmap.cpp
@@ -163,10 +163,9 @@
 
 void ABitmap_notifyPixelsChanged(ABitmap* bitmapHandle) {
     Bitmap* bitmap = TypeCast::toBitmap(bitmapHandle);
-    if (bitmap->isImmutable()) {
-        ALOGE("Attempting to modify an immutable Bitmap!");
+    if (!bitmap->isImmutable()) {
+        bitmap->notifyPixelsChanged();
     }
-    return bitmap->notifyPixelsChanged();
 }
 
 namespace {
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index 055075d..1ff1565 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -183,7 +183,7 @@
     }
 
     ~InvokeListener() override {
-        auto* env = get_env_or_die(mJvm);
+        auto* env = requireEnv(mJvm);
         env->DeleteWeakGlobalRef(mWeakRef);
     }
 
diff --git a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
index db5f6f6..b10540c 100644
--- a/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
+++ b/libs/hwui/jni/ByteBufferStreamAdaptor.cpp
@@ -9,24 +9,6 @@
 static jmethodID gByteBuffer_getMethodID;
 static jmethodID gByteBuffer_setPositionMethodID;
 
-/**
- * Helper method for accessing the JNI interface pointer.
- *
- * Image decoding (which this supports) is started on a thread that is already
- * attached to the Java VM. But an AnimatedImageDrawable continues decoding on
- * the AnimatedImageThread, which is not attached. This will attach if
- * necessary.
- */
-static JNIEnv* requireEnv(JavaVM* jvm) {
-    JNIEnv* env;
-    if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
-            LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
-        }
-    }
-    return env;
-}
-
 class ByteBufferStream : public SkStreamAsset {
 private:
     ByteBufferStream(JavaVM* jvm, jobject jbyteBuffer, size_t initialPosition, size_t length,
@@ -304,7 +286,7 @@
         auto* context = new release_proc_context{jvm, jbyteBuffer};
         auto releaseProc = [](const void*, void* context) {
             auto* c = reinterpret_cast<release_proc_context*>(context);
-            JNIEnv* env = get_env_or_die(c->jvm);
+            JNIEnv* env = requireEnv(c->jvm);
             env->DeleteGlobalRef(c->jbyteBuffer);
             delete c;
         };
diff --git a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
index 39483b5..f1c6b29 100644
--- a/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
+++ b/libs/hwui/jni/CreateJavaOutputStreamAdaptor.cpp
@@ -49,13 +49,13 @@
     }
 
     ~JavaInputStreamAdaptor() override {
-        auto* env = android::get_env_or_die(fJvm);
+        auto* env = android::requireEnv(fJvm);
         env->DeleteGlobalRef(fJavaInputStream);
         env->DeleteGlobalRef(fJavaByteArray);
     }
 
     size_t read(void* buffer, size_t size) override {
-        auto* env = android::get_env_or_die(fJvm);
+        auto* env = android::requireEnv(fJvm);
         if (!fSwallowExceptions && checkException(env)) {
             // Just in case the caller did not clear from a previous exception.
             return 0;
diff --git a/libs/hwui/jni/Utils.cpp b/libs/hwui/jni/Utils.cpp
index 17c194d..34fd668 100644
--- a/libs/hwui/jni/Utils.cpp
+++ b/libs/hwui/jni/Utils.cpp
@@ -159,3 +159,13 @@
     }
     return env;
 }
+
+JNIEnv* android::requireEnv(JavaVM* jvm) {
+    JNIEnv* env;
+    if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        if (jvm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+            LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+        }
+    }
+    return env;
+}
diff --git a/libs/hwui/jni/Utils.h b/libs/hwui/jni/Utils.h
index 8925517..f628cc3 100644
--- a/libs/hwui/jni/Utils.h
+++ b/libs/hwui/jni/Utils.h
@@ -78,6 +78,16 @@
 
 JNIEnv* get_env_or_die(JavaVM* jvm);
 
+/**
+ * Helper method for accessing the JNI interface pointer.
+ *
+ * Image decoding (which this supports) is started on a thread that is already
+ * attached to the Java VM. But an AnimatedImageDrawable continues decoding on
+ * the AnimatedImageThread, which is not attached. This will attach if
+ * necessary.
+ */
+JNIEnv* requireEnv(JavaVM* jvm);
+
 }; // namespace android
 
 #endif  // _ANDROID_GRAPHICS_UTILS_H_
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 41aa1ff..5088494 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -418,9 +418,9 @@
                 auto data = picture->serialize();
                 savePictureAsync(data, mCapturedFile);
                 mCaptureSequence = 0;
+                mCaptureMode = CaptureMode::None;
             }
         }
-        mCaptureMode = CaptureMode::None;
         mRecorder.reset();
     }
 }
diff --git a/libs/hwui/tests/unit/ABitmapTests.cpp b/libs/hwui/tests/unit/ABitmapTests.cpp
new file mode 100644
index 0000000..8e2f7e0
--- /dev/null
+++ b/libs/hwui/tests/unit/ABitmapTests.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "android/graphics/bitmap.h"
+#include "apex/TypeCast.h"
+#include "hwui/Bitmap.h"
+#include "tests/common/TestUtils.h"
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(ABitmap, notifyPixelsChanged) {
+    // generate a bitmap and its public API handle
+    sk_sp<Bitmap> bitmap(TestUtils::createBitmap(1, 1));
+    ABitmap* abmp = android::TypeCast::toABitmap(bitmap.get());
+
+    // verify that notification changes the genID
+    uint32_t genID = bitmap->getGenerationID();
+    ABitmap_notifyPixelsChanged(abmp);
+    ASSERT_TRUE(bitmap->getGenerationID() != genID);
+
+    // mark the bitmap as immutable
+    ASSERT_FALSE(bitmap->isImmutable());
+    bitmap->setImmutable();
+    ASSERT_TRUE(bitmap->isImmutable());
+
+    // attempt to notify that the pixels have changed
+    genID = bitmap->getGenerationID();
+    ABitmap_notifyPixelsChanged(abmp);
+    ASSERT_TRUE(bitmap->getGenerationID() == genID);
+}
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 90bcd1c..1208062 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -403,3 +403,40 @@
     renderThread.destroyRenderingContext();
     EXPECT_FALSE(pipeline->isSurfaceReady());
 }
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
+    // create a pipeline and add a picture callback
+    auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
+    int callbackCount = 0;
+    pipeline->setPictureCapturedCallback(
+            [&callbackCount](sk_sp<SkPicture>&& picture) { callbackCount += 1; });
+
+    // create basic red frame and render it
+    auto redNode = TestUtils::createSkiaNode(
+            0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
+                redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
+            });
+    LayerUpdateQueue layerUpdateQueue;
+    SkRect dirty = SkRectMakeLargest();
+    std::vector<sp<RenderNode>> renderNodes;
+    renderNodes.push_back(redNode);
+    bool opaque = true;
+    android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
+    auto surface = SkSurface::MakeRasterN32Premul(1, 1);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+                          SkMatrix::I());
+
+    // verify the callback was called
+    EXPECT_EQ(1, callbackCount);
+
+    // render a second frame and check the callback count
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+                          SkMatrix::I());
+    EXPECT_EQ(2, callbackCount);
+
+    // unset the callback, render another frame, check callback was not invoked
+    pipeline->setPictureCapturedCallback(nullptr);
+    pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
+                          SkMatrix::I());
+    EXPECT_EQ(2, callbackCount);
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 4150926..75ea0cb 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -47,10 +47,10 @@
     Location getLastLocation(in LocationRequest request, String packageName, String featureId);
     boolean getCurrentLocation(in LocationRequest request,
             in ICancellationSignal cancellationSignal, in ILocationListener listener,
-            String packageName, String featureId);
+            String packageName, String featureId, String listenerId);
 
     void requestLocationUpdates(in LocationRequest request, in ILocationListener listener,
-            in PendingIntent intent, String packageName, String featureId);
+            in PendingIntent intent, String packageName, String featureId, String listenerId);
     void removeUpdates(in ILocationListener listener, in PendingIntent intent);
 
     void requestGeofence(in LocationRequest request, in Geofence geofence,
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index fcbd3e5..d1b41df 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -718,7 +718,7 @@
             currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
         }
 
-        GetCurrentLocationTransport listenerTransport = new GetCurrentLocationTransport(executor,
+        GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor,
                 consumer);
 
         if (cancellationSignal != null) {
@@ -729,14 +729,15 @@
 
         try {
             if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
-                    listenerTransport, mContext.getPackageName(), mContext.getAttributionTag())) {
-                listenerTransport.register(mContext.getSystemService(AlarmManager.class),
+                    transport, mContext.getPackageName(), mContext.getAttributionTag(),
+                    transport.getListenerId())) {
+                transport.register(mContext.getSystemService(AlarmManager.class),
                         remoteCancellationSignal);
                 if (cancellationSignal != null) {
-                    cancellationSignal.setOnCancelListener(listenerTransport::cancel);
+                    cancellationSignal.setOnCancelListener(transport::cancel);
                 }
             } else {
-                listenerTransport.fail();
+                transport.fail();
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1175,7 +1176,8 @@
             boolean registered = false;
             try {
                 mService.requestLocationUpdates(locationRequest, transport, null,
-                        mContext.getPackageName(), mContext.getAttributionTag());
+                        mContext.getPackageName(), mContext.getAttributionTag(),
+                        transport.getListenerId());
                 registered = true;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -1220,7 +1222,7 @@
 
         try {
             mService.requestLocationUpdates(locationRequest, null, pendingIntent,
-                    mContext.getPackageName(), mContext.getAttributionTag());
+                    mContext.getPackageName(), mContext.getAttributionTag(), null);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2558,6 +2560,10 @@
             mRemoteCancellationSignal = null;
         }
 
+        public String getListenerId() {
+            return mConsumer.getClass().getName() + "@" + System.identityHashCode(mConsumer);
+        }
+
         public synchronized void register(AlarmManager alarmManager,
                 ICancellationSignal remoteCancellationSignal) {
             if (mConsumer == null) {
@@ -2683,6 +2689,10 @@
             return mListener;
         }
 
+        public String getListenerId() {
+            return mListener.getClass().getName() + "@" + System.identityHashCode(mListener);
+        }
+
         public void register(@NonNull Executor executor) {
             Preconditions.checkArgument(executor != null, "invalid null executor");
             mExecutor = executor;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 155eb1c..c11762b 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -44,46 +44,56 @@
 /**
  * @hide
  */
+@TestApi
 public class AudioSystem
 {
     private static final boolean DEBUG_VOLUME = false;
 
     private static final String TAG = "AudioSystem";
+
+    // private constructor to prevent instantiating AudioSystem
+    private AudioSystem() {
+        throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
+    }
+
     /* These values must be kept in sync with system/audio.h */
     /*
      * If these are modified, please also update Settings.System.VOLUME_SETTINGS
      * and attrs.xml and AudioManager.java.
      */
-    /** Used to identify the default audio stream volume */
+    /** @hide Used to identify the default audio stream volume */
+    @TestApi
     public static final int STREAM_DEFAULT = -1;
-    /** Used to identify the volume of audio streams for phone calls */
+    /** @hide Used to identify the volume of audio streams for phone calls */
     public static final int STREAM_VOICE_CALL = 0;
-    /** Used to identify the volume of audio streams for system sounds */
+    /** @hide Used to identify the volume of audio streams for system sounds */
     public static final int STREAM_SYSTEM = 1;
-    /** Used to identify the volume of audio streams for the phone ring and message alerts */
+    /** @hide Used to identify the volume of audio streams for the phone ring and message alerts */
     public static final int STREAM_RING = 2;
-    /** Used to identify the volume of audio streams for music playback */
+    /** @hide Used to identify the volume of audio streams for music playback */
     public static final int STREAM_MUSIC = 3;
-    /** Used to identify the volume of audio streams for alarms */
+    /** @hide Used to identify the volume of audio streams for alarms */
     public static final int STREAM_ALARM = 4;
-    /** Used to identify the volume of audio streams for notifications */
+    /** @hide Used to identify the volume of audio streams for notifications */
     public static final int STREAM_NOTIFICATION = 5;
-    /** Used to identify the volume of audio streams for phone calls when connected on bluetooth */
+    /** @hide
+     *  Used to identify the volume of audio streams for phone calls when connected on bluetooth */
     public static final int STREAM_BLUETOOTH_SCO = 6;
-    /** Used to identify the volume of audio streams for enforced system sounds in certain
+    /** @hide Used to identify the volume of audio streams for enforced system sounds in certain
      * countries (e.g camera in Japan) */
     @UnsupportedAppUsage
     public static final int STREAM_SYSTEM_ENFORCED = 7;
-    /** Used to identify the volume of audio streams for DTMF tones */
+    /** @hide Used to identify the volume of audio streams for DTMF tones */
     public static final int STREAM_DTMF = 8;
-    /** Used to identify the volume of audio streams exclusively transmitted through the
+    /** @hide Used to identify the volume of audio streams exclusively transmitted through the
      *  speaker (TTS) of the device */
     public static final int STREAM_TTS = 9;
-    /** Used to identify the volume of audio streams for accessibility prompts */
+    /** @hide Used to identify the volume of audio streams for accessibility prompts */
     public static final int STREAM_ACCESSIBILITY = 10;
-    /** Used to identify the volume of audio streams for virtual assistant */
+    /** @hide Used to identify the volume of audio streams for virtual assistant */
     public static final int STREAM_ASSISTANT = 11;
     /**
+     * @hide
      * @deprecated Use {@link #numStreamTypes() instead}
      */
     public static final int NUM_STREAMS = 5;
@@ -96,9 +106,16 @@
 
     // Expose only the getter method publicly so we can change it in the future
     private static final int NUM_STREAM_TYPES = 12;
+
+    /**
+     * @hide
+     * @return total number of stream types
+     */
     @UnsupportedAppUsage
+    @TestApi
     public static final int getNumStreamTypes() { return NUM_STREAM_TYPES; }
 
+    /** @hide */
     public static final String[] STREAM_NAMES = new String[] {
         "STREAM_VOICE_CALL",
         "STREAM_SYSTEM",
@@ -114,7 +131,8 @@
         "STREAM_ASSISTANT"
     };
 
-    /*
+    /**
+     * @hide
      * Sets the microphone mute on or off.
      *
      * @param on set <var>true</var> to mute the microphone;
@@ -124,7 +142,8 @@
     @UnsupportedAppUsage
     public static native int muteMicrophone(boolean on);
 
-    /*
+    /**
+     * @hide
      * Checks whether the microphone mute is on or off.
      *
      * @return true if microphone is muted, false if it's not
@@ -133,15 +152,24 @@
     public static native boolean isMicrophoneMuted();
 
     /* modes for setPhoneState, must match AudioSystem.h audio_mode */
+    /** @hide */
     public static final int MODE_INVALID            = -2;
+    /** @hide */
     public static final int MODE_CURRENT            = -1;
+    /** @hide */
     public static final int MODE_NORMAL             = 0;
+    /** @hide */
     public static final int MODE_RINGTONE           = 1;
+    /** @hide */
     public static final int MODE_IN_CALL            = 2;
+    /** @hide */
     public static final int MODE_IN_COMMUNICATION   = 3;
+    /** @hide */
     public static final int MODE_CALL_SCREENING     = 4;
+    /** @hide */
     public static final int NUM_MODES               = 5;
 
+    /** @hide */
     public static String modeToString(int mode) {
         switch (mode) {
             case MODE_CURRENT: return "MODE_CURRENT";
@@ -156,15 +184,23 @@
     }
 
     /* Formats for A2DP codecs, must match system/audio-base.h audio_format_t */
+    /** @hide */
     public static final int AUDIO_FORMAT_INVALID        = 0xFFFFFFFF;
+    /** @hide */
     public static final int AUDIO_FORMAT_DEFAULT        = 0;
+    /** @hide */
     public static final int AUDIO_FORMAT_AAC            = 0x04000000;
+    /** @hide */
     public static final int AUDIO_FORMAT_SBC            = 0x1F000000;
+    /** @hide */
     public static final int AUDIO_FORMAT_APTX           = 0x20000000;
+    /** @hide */
     public static final int AUDIO_FORMAT_APTX_HD        = 0x21000000;
+    /** @hide */
     public static final int AUDIO_FORMAT_LDAC           = 0x23000000;
 
     /**
+     * @hide
      * Convert audio format enum values to Bluetooth codec values
      */
     public static int audioFormatToBluetoothSourceCodec(int audioFormat) {
@@ -179,25 +215,27 @@
     }
 
     /* Routing bits for the former setRouting/getRouting API */
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_EARPIECE          = (1 << 0);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_SPEAKER           = (1 << 1);
-    /** @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */
+    /** @hide @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */
     @Deprecated public static final int ROUTE_BLUETOOTH = (1 << 2);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_BLUETOOTH_SCO     = (1 << 2);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_HEADSET           = (1 << 3);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_BLUETOOTH_A2DP    = (1 << 4);
-    /** @deprecated */
+    /** @hide @deprecated */
     @Deprecated public static final int ROUTE_ALL               = 0xFFFFFFFF;
 
     // Keep in sync with system/media/audio/include/system/audio.h
+    /**  @hide */
     public static final int AUDIO_SESSION_ALLOCATE = 0;
 
-    /*
+    /**
+     * @hide
      * Checks whether the specified stream type is active.
      *
      * return true if any track playing on this stream is active.
@@ -205,7 +243,8 @@
     @UnsupportedAppUsage
     public static native boolean isStreamActive(int stream, int inPastMs);
 
-    /*
+    /**
+     * @hide
      * Checks whether the specified stream type is active on a remotely connected device. The notion
      * of what constitutes a remote device is enforced by the audio policy manager of the platform.
      *
@@ -213,7 +252,8 @@
      */
     public static native boolean isStreamActiveRemotely(int stream, int inPastMs);
 
-    /*
+    /**
+     * @hide
      * Checks whether the specified audio source is active.
      *
      * return true if any recorder using this source is currently recording
@@ -221,23 +261,27 @@
     @UnsupportedAppUsage
     public static native boolean isSourceActive(int source);
 
-    /*
+    /**
+     * @hide
      * Returns a new unused audio session ID
      */
     public static native int newAudioSessionId();
 
-    /*
+    /**
+     * @hide
      * Returns a new unused audio player ID
      */
     public static native int newAudioPlayerId();
 
     /**
+     * @hide
      * Returns a new unused audio recorder ID
      */
     public static native int newAudioRecorderId();
 
 
-    /*
+    /**
+     * @hide
      * Sets a group generic audio configuration parameters. The use of these parameters
      * are platform dependent, see libaudio
      *
@@ -247,7 +291,8 @@
     @UnsupportedAppUsage
     public static native int setParameters(String keyValuePairs);
 
-    /*
+    /**
+     * @hide
      * Gets a group generic audio configuration parameters. The use of these parameters
      * are platform dependent, see libaudio
      *
@@ -259,16 +304,16 @@
     public static native String getParameters(String keys);
 
     // These match the enum AudioError in frameworks/base/core/jni/android_media_AudioSystem.cpp
-    /* Command sucessful or Media server restarted. see ErrorCallback */
+    /** @hide Command successful or Media server restarted. see ErrorCallback */
     public static final int AUDIO_STATUS_OK = 0;
-    /* Command failed or unspecified audio error.  see ErrorCallback */
+    /** @hide Command failed or unspecified audio error.  see ErrorCallback */
     public static final int AUDIO_STATUS_ERROR = 1;
-    /* Media server died. see ErrorCallback */
+    /** @hide Media server died. see ErrorCallback */
     public static final int AUDIO_STATUS_SERVER_DIED = 100;
 
-    private static ErrorCallback mErrorCallback;
+    private static ErrorCallback sErrorCallback;
 
-    /*
+    /** @hide
      * Handles the audio error callback.
      */
     public interface ErrorCallback
@@ -283,7 +328,8 @@
         void onError(int error);
     };
 
-    /*
+    /**
+     * @hide
      * Registers a callback to be invoked when an error occurs.
      * @param cb the callback to run
      */
@@ -291,7 +337,7 @@
     public static void setErrorCallback(ErrorCallback cb)
     {
         synchronized (AudioSystem.class) {
-            mErrorCallback = cb;
+            sErrorCallback = cb;
             if (cb != null) {
                 cb.onError(checkAudioFlinger());
             }
@@ -303,8 +349,8 @@
     {
         ErrorCallback errorCallback = null;
         synchronized (AudioSystem.class) {
-            if (mErrorCallback != null) {
-                errorCallback = mErrorCallback;
+            if (sErrorCallback != null) {
+                errorCallback = sErrorCallback;
             }
         }
         if (errorCallback != null) {
@@ -313,6 +359,7 @@
     }
 
     /**
+     * @hide
      * Handles events from the audio policy manager about dynamic audio policies
      * @see android.media.audiopolicy.AudioPolicy
      */
@@ -326,6 +373,7 @@
 
     private static DynamicPolicyCallback sDynPolicyCallback;
 
+    /** @hide */
     public static void setDynamicPolicyCallback(DynamicPolicyCallback cb)
     {
         synchronized (AudioSystem.class) {
@@ -355,6 +403,7 @@
     }
 
     /**
+     * @hide
      * Handles events from the audio policy manager about recording events
      * @see android.media.AudioManager.AudioRecordingCallback
      */
@@ -386,6 +435,7 @@
 
     private static AudioRecordingCallback sRecordingCallback;
 
+    /** @hide */
     public static void setRecordingCallback(AudioRecordingCallback cb) {
         synchronized (AudioSystem.class) {
             sRecordingCallback = cb;
@@ -434,13 +484,21 @@
      * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...)
      * Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h
      */
+    /** @hide */
     public static final int SUCCESS            = 0;
+    /** @hide */
     public static final int ERROR              = -1;
+    /** @hide */
     public static final int BAD_VALUE          = -2;
+    /** @hide */
     public static final int INVALID_OPERATION  = -3;
+    /** @hide */
     public static final int PERMISSION_DENIED  = -4;
+    /** @hide */
     public static final int NO_INIT            = -5;
+    /** @hide */
     public static final int DEAD_OBJECT        = -6;
+    /** @hide */
     public static final int WOULD_BLOCK        = -7;
 
     /** @hide */
@@ -458,6 +516,7 @@
     public @interface AudioSystemError {}
 
     /**
+     * @hide
      * Convert an int error value to its String value for readability.
      * Accepted error values are the java AudioSystem errors, matching android_media_AudioErrors.h,
      * which map onto the native status_t type.
@@ -494,74 +553,113 @@
     //
     // audio device definitions: must be kept in sync with values in system/core/audio.h
     //
-
+    /** @hide */
     public static final int DEVICE_NONE = 0x0;
     // reserved bits
+    /** @hide */
     public static final int DEVICE_BIT_IN = 0x80000000;
+    /** @hide */
     public static final int DEVICE_BIT_DEFAULT = 0x40000000;
     // output devices, be sure to update AudioManager.java also
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_EARPIECE = 0x1;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_SPEAKER = 0x2;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_WIRED_HEADSET = 0x4;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
+    /** @hide */
     public static final int DEVICE_OUT_HDMI = DEVICE_OUT_AUX_DIGITAL;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_USB_ACCESSORY = 0x2000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_USB_DEVICE = 0x4000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_REMOTE_SUBMIX = 0x8000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_TELEPHONY_TX = 0x10000;
+    /** @hide */
     public static final int DEVICE_OUT_LINE = 0x20000;
+    /** @hide */
     public static final int DEVICE_OUT_HDMI_ARC = 0x40000;
+    /** @hide */
     public static final int DEVICE_OUT_SPDIF = 0x80000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_FM = 0x100000;
+    /** @hide */
     public static final int DEVICE_OUT_AUX_LINE = 0x200000;
+    /** @hide */
     public static final int DEVICE_OUT_SPEAKER_SAFE = 0x400000;
+    /** @hide */
     public static final int DEVICE_OUT_IP = 0x800000;
+    /** @hide */
     public static final int DEVICE_OUT_BUS = 0x1000000;
+    /** @hide */
     public static final int DEVICE_OUT_PROXY = 0x2000000;
+    /** @hide */
     public static final int DEVICE_OUT_USB_HEADSET = 0x4000000;
+    /** @hide */
     public static final int DEVICE_OUT_HEARING_AID = 0x8000000;
 
+    /** @hide */
     public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
 
     // Deprecated in R because multiple device types are no longer accessed as a bit mask.
     // Removing this will get lint warning about changing hidden apis.
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_OUT_ALL_USB = (DEVICE_OUT_USB_ACCESSORY |
                                                   DEVICE_OUT_USB_DEVICE |
                                                   DEVICE_OUT_USB_HEADSET);
 
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_A2DP_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_SCO_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_USB_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_OUT_ALL_HDMI_SYSTEM_AUDIO_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_ALL_HDMI_SYSTEM_AUDIO_AND_SPEAKER_SET;
     static {
         DEVICE_OUT_ALL_SET = new HashSet<>();
@@ -621,53 +719,85 @@
     }
 
     // input devices
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_COMMUNICATION = DEVICE_BIT_IN | 0x1;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_AMBIENT = DEVICE_BIT_IN | 0x2;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_BUILTIN_MIC = DEVICE_BIT_IN | 0x4;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = DEVICE_BIT_IN | 0x8;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_WIRED_HEADSET = DEVICE_BIT_IN | 0x10;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_AUX_DIGITAL = DEVICE_BIT_IN | 0x20;
+    /** @hide */
     public static final int DEVICE_IN_HDMI = DEVICE_IN_AUX_DIGITAL;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_VOICE_CALL = DEVICE_BIT_IN | 0x40;
+    /** @hide */
     public static final int DEVICE_IN_TELEPHONY_RX = DEVICE_IN_VOICE_CALL;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_BACK_MIC = DEVICE_BIT_IN | 0x80;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_REMOTE_SUBMIX = DEVICE_BIT_IN | 0x100;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_ANLG_DOCK_HEADSET = DEVICE_BIT_IN | 0x200;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_DGTL_DOCK_HEADSET = DEVICE_BIT_IN | 0x400;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_USB_ACCESSORY = DEVICE_BIT_IN | 0x800;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_USB_DEVICE = DEVICE_BIT_IN | 0x1000;
+    /** @hide */
     public static final int DEVICE_IN_FM_TUNER = DEVICE_BIT_IN | 0x2000;
+    /** @hide */
     public static final int DEVICE_IN_TV_TUNER = DEVICE_BIT_IN | 0x4000;
+    /** @hide */
     public static final int DEVICE_IN_LINE = DEVICE_BIT_IN | 0x8000;
+    /** @hide */
     public static final int DEVICE_IN_SPDIF = DEVICE_BIT_IN | 0x10000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_BLUETOOTH_A2DP = DEVICE_BIT_IN | 0x20000;
+    /** @hide */
     public static final int DEVICE_IN_LOOPBACK = DEVICE_BIT_IN | 0x40000;
+    /** @hide */
     public static final int DEVICE_IN_IP = DEVICE_BIT_IN | 0x80000;
+    /** @hide */
     public static final int DEVICE_IN_BUS = DEVICE_BIT_IN | 0x100000;
+    /** @hide */
     public static final int DEVICE_IN_PROXY = DEVICE_BIT_IN | 0x1000000;
+    /** @hide */
     public static final int DEVICE_IN_USB_HEADSET = DEVICE_BIT_IN | 0x2000000;
+    /** @hide */
     public static final int DEVICE_IN_BLUETOOTH_BLE = DEVICE_BIT_IN | 0x4000000;
+    /** @hide */
     public static final int DEVICE_IN_HDMI_ARC = DEVICE_BIT_IN | 0x8000000;
+    /** @hide */
     public static final int DEVICE_IN_ECHO_REFERENCE = DEVICE_BIT_IN | 0x10000000;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT;
 
+    /** @hide */
     public static final Set<Integer> DEVICE_IN_ALL_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_IN_ALL_SCO_SET;
+    /** @hide */
     public static final Set<Integer> DEVICE_IN_ALL_USB_SET;
     static {
         DEVICE_IN_ALL_SET = new HashSet<>();
@@ -708,15 +838,19 @@
         DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET);
     }
 
+    /** @hide */
     public static final String LEGACY_REMOTE_SUBMIX_ADDRESS = "0";
 
     // device states, must match AudioSystem::device_connection_state
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_STATE_UNAVAILABLE = 0;
+    /** @hide */
     @UnsupportedAppUsage
     public static final int DEVICE_STATE_AVAILABLE = 1;
     private static final int NUM_DEVICE_STATES = 1;
 
+    /** @hide */
     public static String deviceStateToString(int state) {
         switch (state) {
             case DEVICE_STATE_UNAVAILABLE: return "DEVICE_STATE_UNAVAILABLE";
@@ -725,63 +859,65 @@
         }
     }
 
-    public static final String DEVICE_OUT_EARPIECE_NAME = "earpiece";
-    public static final String DEVICE_OUT_SPEAKER_NAME = "speaker";
-    public static final String DEVICE_OUT_WIRED_HEADSET_NAME = "headset";
-    public static final String DEVICE_OUT_WIRED_HEADPHONE_NAME = "headphone";
-    public static final String DEVICE_OUT_BLUETOOTH_SCO_NAME = "bt_sco";
-    public static final String DEVICE_OUT_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs";
-    public static final String DEVICE_OUT_BLUETOOTH_SCO_CARKIT_NAME = "bt_sco_carkit";
-    public static final String DEVICE_OUT_BLUETOOTH_A2DP_NAME = "bt_a2dp";
+    /** @hide */ public static final String DEVICE_OUT_EARPIECE_NAME = "earpiece";
+    /** @hide */ public static final String DEVICE_OUT_SPEAKER_NAME = "speaker";
+    /** @hide */ public static final String DEVICE_OUT_WIRED_HEADSET_NAME = "headset";
+    /** @hide */ public static final String DEVICE_OUT_WIRED_HEADPHONE_NAME = "headphone";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_SCO_NAME = "bt_sco";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_SCO_CARKIT_NAME = "bt_sco_carkit";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_A2DP_NAME = "bt_a2dp";
+    /** @hide */
     public static final String DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES_NAME = "bt_a2dp_hp";
-    public static final String DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME = "bt_a2dp_spk";
-    public static final String DEVICE_OUT_AUX_DIGITAL_NAME = "aux_digital";
-    public static final String DEVICE_OUT_HDMI_NAME = "hdmi";
-    public static final String DEVICE_OUT_ANLG_DOCK_HEADSET_NAME = "analog_dock";
-    public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock";
-    public static final String DEVICE_OUT_USB_ACCESSORY_NAME = "usb_accessory";
-    public static final String DEVICE_OUT_USB_DEVICE_NAME = "usb_device";
-    public static final String DEVICE_OUT_REMOTE_SUBMIX_NAME = "remote_submix";
-    public static final String DEVICE_OUT_TELEPHONY_TX_NAME = "telephony_tx";
-    public static final String DEVICE_OUT_LINE_NAME = "line";
-    public static final String DEVICE_OUT_HDMI_ARC_NAME = "hmdi_arc";
-    public static final String DEVICE_OUT_SPDIF_NAME = "spdif";
-    public static final String DEVICE_OUT_FM_NAME = "fm_transmitter";
-    public static final String DEVICE_OUT_AUX_LINE_NAME = "aux_line";
-    public static final String DEVICE_OUT_SPEAKER_SAFE_NAME = "speaker_safe";
-    public static final String DEVICE_OUT_IP_NAME = "ip";
-    public static final String DEVICE_OUT_BUS_NAME = "bus";
-    public static final String DEVICE_OUT_PROXY_NAME = "proxy";
-    public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset";
-    public static final String DEVICE_OUT_HEARING_AID_NAME = "hearing_aid_out";
+    /** @hide */ public static final String DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER_NAME = "bt_a2dp_spk";
+    /** @hide */ public static final String DEVICE_OUT_AUX_DIGITAL_NAME = "aux_digital";
+    /** @hide */ public static final String DEVICE_OUT_HDMI_NAME = "hdmi";
+    /** @hide */ public static final String DEVICE_OUT_ANLG_DOCK_HEADSET_NAME = "analog_dock";
+    /** @hide */ public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock";
+    /** @hide */ public static final String DEVICE_OUT_USB_ACCESSORY_NAME = "usb_accessory";
+    /** @hide */ public static final String DEVICE_OUT_USB_DEVICE_NAME = "usb_device";
+    /** @hide */ public static final String DEVICE_OUT_REMOTE_SUBMIX_NAME = "remote_submix";
+    /** @hide */ public static final String DEVICE_OUT_TELEPHONY_TX_NAME = "telephony_tx";
+    /** @hide */ public static final String DEVICE_OUT_LINE_NAME = "line";
+    /** @hide */ public static final String DEVICE_OUT_HDMI_ARC_NAME = "hmdi_arc";
+    /** @hide */ public static final String DEVICE_OUT_SPDIF_NAME = "spdif";
+    /** @hide */ public static final String DEVICE_OUT_FM_NAME = "fm_transmitter";
+    /** @hide */ public static final String DEVICE_OUT_AUX_LINE_NAME = "aux_line";
+    /** @hide */ public static final String DEVICE_OUT_SPEAKER_SAFE_NAME = "speaker_safe";
+    /** @hide */ public static final String DEVICE_OUT_IP_NAME = "ip";
+    /** @hide */ public static final String DEVICE_OUT_BUS_NAME = "bus";
+    /** @hide */ public static final String DEVICE_OUT_PROXY_NAME = "proxy";
+    /** @hide */ public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset";
+    /** @hide */ public static final String DEVICE_OUT_HEARING_AID_NAME = "hearing_aid_out";
 
-    public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
-    public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
-    public static final String DEVICE_IN_BUILTIN_MIC_NAME = "mic";
-    public static final String DEVICE_IN_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs";
-    public static final String DEVICE_IN_WIRED_HEADSET_NAME = "headset";
-    public static final String DEVICE_IN_AUX_DIGITAL_NAME = "aux_digital";
-    public static final String DEVICE_IN_TELEPHONY_RX_NAME = "telephony_rx";
-    public static final String DEVICE_IN_BACK_MIC_NAME = "back_mic";
-    public static final String DEVICE_IN_REMOTE_SUBMIX_NAME = "remote_submix";
-    public static final String DEVICE_IN_ANLG_DOCK_HEADSET_NAME = "analog_dock";
-    public static final String DEVICE_IN_DGTL_DOCK_HEADSET_NAME = "digital_dock";
-    public static final String DEVICE_IN_USB_ACCESSORY_NAME = "usb_accessory";
-    public static final String DEVICE_IN_USB_DEVICE_NAME = "usb_device";
-    public static final String DEVICE_IN_FM_TUNER_NAME = "fm_tuner";
-    public static final String DEVICE_IN_TV_TUNER_NAME = "tv_tuner";
-    public static final String DEVICE_IN_LINE_NAME = "line";
-    public static final String DEVICE_IN_SPDIF_NAME = "spdif";
-    public static final String DEVICE_IN_BLUETOOTH_A2DP_NAME = "bt_a2dp";
-    public static final String DEVICE_IN_LOOPBACK_NAME = "loopback";
-    public static final String DEVICE_IN_IP_NAME = "ip";
-    public static final String DEVICE_IN_BUS_NAME = "bus";
-    public static final String DEVICE_IN_PROXY_NAME = "proxy";
-    public static final String DEVICE_IN_USB_HEADSET_NAME = "usb_headset";
-    public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble";
-    public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference";
-    public static final String DEVICE_IN_HDMI_ARC_NAME = "hdmi_arc";
+    /** @hide */ public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
+    /** @hide */ public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
+    /** @hide */ public static final String DEVICE_IN_BUILTIN_MIC_NAME = "mic";
+    /** @hide */ public static final String DEVICE_IN_BLUETOOTH_SCO_HEADSET_NAME = "bt_sco_hs";
+    /** @hide */ public static final String DEVICE_IN_WIRED_HEADSET_NAME = "headset";
+    /** @hide */ public static final String DEVICE_IN_AUX_DIGITAL_NAME = "aux_digital";
+    /** @hide */ public static final String DEVICE_IN_TELEPHONY_RX_NAME = "telephony_rx";
+    /** @hide */ public static final String DEVICE_IN_BACK_MIC_NAME = "back_mic";
+    /** @hide */ public static final String DEVICE_IN_REMOTE_SUBMIX_NAME = "remote_submix";
+    /** @hide */ public static final String DEVICE_IN_ANLG_DOCK_HEADSET_NAME = "analog_dock";
+    /** @hide */ public static final String DEVICE_IN_DGTL_DOCK_HEADSET_NAME = "digital_dock";
+    /** @hide */ public static final String DEVICE_IN_USB_ACCESSORY_NAME = "usb_accessory";
+    /** @hide */ public static final String DEVICE_IN_USB_DEVICE_NAME = "usb_device";
+    /** @hide */ public static final String DEVICE_IN_FM_TUNER_NAME = "fm_tuner";
+    /** @hide */ public static final String DEVICE_IN_TV_TUNER_NAME = "tv_tuner";
+    /** @hide */ public static final String DEVICE_IN_LINE_NAME = "line";
+    /** @hide */ public static final String DEVICE_IN_SPDIF_NAME = "spdif";
+    /** @hide */ public static final String DEVICE_IN_BLUETOOTH_A2DP_NAME = "bt_a2dp";
+    /** @hide */ public static final String DEVICE_IN_LOOPBACK_NAME = "loopback";
+    /** @hide */ public static final String DEVICE_IN_IP_NAME = "ip";
+    /** @hide */ public static final String DEVICE_IN_BUS_NAME = "bus";
+    /** @hide */ public static final String DEVICE_IN_PROXY_NAME = "proxy";
+    /** @hide */ public static final String DEVICE_IN_USB_HEADSET_NAME = "usb_headset";
+    /** @hide */ public static final String DEVICE_IN_BLUETOOTH_BLE_NAME = "bt_ble";
+    /** @hide */ public static final String DEVICE_IN_ECHO_REFERENCE_NAME = "echo_reference";
+    /** @hide */ public static final String DEVICE_IN_HDMI_ARC_NAME = "hdmi_arc";
 
+    /** @hide */
     @UnsupportedAppUsage
     public static String getOutputDeviceName(int device)
     {
@@ -848,6 +984,7 @@
         }
     }
 
+    /** @hide */
     public static String getInputDeviceName(int device)
     {
         switch(device) {
@@ -910,6 +1047,7 @@
     }
 
     /**
+     * @hide
      * Returns a human readable name for a given device type
      * @param device a native device type, NOT an AudioDeviceInfo type
      * @return a string describing the device type
@@ -922,35 +1060,31 @@
     }
 
     // phone state, match audio_mode???
-    public static final int PHONE_STATE_OFFCALL = 0;
-    public static final int PHONE_STATE_RINGING = 1;
-    public static final int PHONE_STATE_INCALL = 2;
+    /** @hide */ public static final int PHONE_STATE_OFFCALL = 0;
+    /** @hide */ public static final int PHONE_STATE_RINGING = 1;
+    /** @hide */ public static final int PHONE_STATE_INCALL = 2;
 
     // device categories config for setForceUse, must match audio_policy_forced_cfg_t
-    @UnsupportedAppUsage
-    public static final int FORCE_NONE = 0;
-    public static final int FORCE_SPEAKER = 1;
-    public static final int FORCE_HEADPHONES = 2;
-    public static final int FORCE_BT_SCO = 3;
-    public static final int FORCE_BT_A2DP = 4;
-    public static final int FORCE_WIRED_ACCESSORY = 5;
-    @UnsupportedAppUsage
-    public static final int FORCE_BT_CAR_DOCK = 6;
-    @UnsupportedAppUsage
-    public static final int FORCE_BT_DESK_DOCK = 7;
-    @UnsupportedAppUsage
-    public static final int FORCE_ANALOG_DOCK = 8;
-    @UnsupportedAppUsage
-    public static final int FORCE_DIGITAL_DOCK = 9;
-    public static final int FORCE_NO_BT_A2DP = 10;
-    public static final int FORCE_SYSTEM_ENFORCED = 11;
-    public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12;
-    public static final int FORCE_ENCODED_SURROUND_NEVER = 13;
-    public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14;
-    public static final int FORCE_ENCODED_SURROUND_MANUAL = 15;
-    public static final int NUM_FORCE_CONFIG = 16;
-    public static final int FORCE_DEFAULT = FORCE_NONE;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_NONE = 0;
+    /** @hide */ public static final int FORCE_SPEAKER = 1;
+    /** @hide */ public static final int FORCE_HEADPHONES = 2;
+    /** @hide */ public static final int FORCE_BT_SCO = 3;
+    /** @hide */ public static final int FORCE_BT_A2DP = 4;
+    /** @hide */ public static final int FORCE_WIRED_ACCESSORY = 5;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_BT_CAR_DOCK = 6;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_BT_DESK_DOCK = 7;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_ANALOG_DOCK = 8;
+    /** @hide */ @UnsupportedAppUsage public static final int FORCE_DIGITAL_DOCK = 9;
+    /** @hide */ public static final int FORCE_NO_BT_A2DP = 10;
+    /** @hide */ public static final int FORCE_SYSTEM_ENFORCED = 11;
+    /** @hide */ public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12;
+    /** @hide */ public static final int FORCE_ENCODED_SURROUND_NEVER = 13;
+    /** @hide */ public static final int FORCE_ENCODED_SURROUND_ALWAYS = 14;
+    /** @hide */ public static final int FORCE_ENCODED_SURROUND_MANUAL = 15;
+    /** @hide */ public static final int NUM_FORCE_CONFIG = 16;
+    /** @hide */ public static final int FORCE_DEFAULT = FORCE_NONE;
 
+    /** @hide */
     public static String forceUseConfigToString(int config) {
         switch (config) {
             case FORCE_NONE: return "FORCE_NONE";
@@ -974,16 +1108,17 @@
     }
 
     // usage for setForceUse, must match audio_policy_force_use_t
-    public static final int FOR_COMMUNICATION = 0;
-    public static final int FOR_MEDIA = 1;
-    public static final int FOR_RECORD = 2;
-    public static final int FOR_DOCK = 3;
-    public static final int FOR_SYSTEM = 4;
-    public static final int FOR_HDMI_SYSTEM_AUDIO = 5;
-    public static final int FOR_ENCODED_SURROUND = 6;
-    public static final int FOR_VIBRATE_RINGING = 7;
+    /** @hide */ public static final int FOR_COMMUNICATION = 0;
+    /** @hide */ public static final int FOR_MEDIA = 1;
+    /** @hide */ public static final int FOR_RECORD = 2;
+    /** @hide */ public static final int FOR_DOCK = 3;
+    /** @hide */ public static final int FOR_SYSTEM = 4;
+    /** @hide */ public static final int FOR_HDMI_SYSTEM_AUDIO = 5;
+    /** @hide */ public static final int FOR_ENCODED_SURROUND = 6;
+    /** @hide */ public static final int FOR_VIBRATE_RINGING = 7;
     private static final int NUM_FORCE_USE = 8;
 
+    /** @hide */
     public static String forceUseUsageToString(int usage) {
         switch (usage) {
             case FOR_COMMUNICATION: return "FOR_COMMUNICATION";
@@ -998,7 +1133,7 @@
         }
     }
 
-    /** Wrapper for native methods called from AudioService */
+    /** @hide Wrapper for native methods called from AudioService */
     public static int setStreamVolumeIndexAS(int stream, int index, int device) {
         if (DEBUG_VOLUME) {
             Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream]
@@ -1008,10 +1143,11 @@
     }
 
     // usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t
-    public static final int SYNC_EVENT_NONE = 0;
-    public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1;
+    /** @hide */ public static final int SYNC_EVENT_NONE = 0;
+    /** @hide */ public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1;
 
     /**
+     * @hide
      * @return command completion status, one of {@link #AUDIO_STATUS_OK},
      *     {@link #AUDIO_STATUS_ERROR} or {@link #AUDIO_STATUS_SERVER_DIED}
      */
@@ -1019,12 +1155,15 @@
     public static native int setDeviceConnectionState(int device, int state,
                                                       String device_address, String device_name,
                                                       int codecFormat);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int getDeviceConnectionState(int device, String device_address);
+    /** @hide */
     public static native int handleDeviceConfigChange(int device,
                                                       String device_address,
                                                       String device_name,
                                                       int codecFormat);
+    /** @hide */
     @UnsupportedAppUsage
     public static int setPhoneState(int state) {
         Log.w(TAG, "Do not use this method! Use AudioManager.setMode() instead.");
@@ -1038,14 +1177,18 @@
      * @return command completion status.
      */
     public static native int setPhoneState(int state, int uid);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int setForceUse(int usage, int config);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int getForceUse(int usage);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int initStreamVolume(int stream, int indexMin, int indexMax);
     @UnsupportedAppUsage
     private static native int setStreamVolumeIndex(int stream, int index, int device);
+    /** @hide */
     public static native int getStreamVolumeIndex(int stream, int device);
     /**
      * @hide
@@ -1082,16 +1225,22 @@
      */
     public static native int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attributes);
 
+    /** @hide */
     public static native int setMasterVolume(float value);
+    /** @hide */
     public static native float getMasterVolume();
+    /** @hide */
     @UnsupportedAppUsage
     public static native int setMasterMute(boolean mute);
+    /** @hide */
     @UnsupportedAppUsage
     public static native boolean getMasterMute();
+    /** @hide */
     @UnsupportedAppUsage
     public static native int getDevicesForStream(int stream);
 
     /**
+     * @hide
      * Do not use directly, see {@link AudioManager#getDevicesForAttributes(AudioAttributes)}
      * Get the audio devices that would be used for the routing of the given audio attributes.
      * @param attributes the {@link AudioAttributes} for which the routing is being queried
@@ -1136,31 +1285,43 @@
     /** @hide returns master balance value in range -1.f -> 1.f, where 0.f is dead center. */
     @TestApi
     public static native float getMasterBalance();
-    /** @hide changes the audio balance of the device. */
+    /** @hide Changes the audio balance of the device. */
     @TestApi
     public static native int setMasterBalance(float balance);
 
     // helpers for android.media.AudioManager.getProperty(), see description there for meaning
+    /** @hide */
     @UnsupportedAppUsage(trackingBug = 134049522)
     public static native int getPrimaryOutputSamplingRate();
+    /** @hide */
     @UnsupportedAppUsage(trackingBug = 134049522)
     public static native int getPrimaryOutputFrameCount();
+    /** @hide */
     @UnsupportedAppUsage
     public static native int getOutputLatency(int stream);
 
+    /** @hide */
     public static native int setLowRamDevice(boolean isLowRamDevice, long totalMemory);
+    /** @hide */
     @UnsupportedAppUsage
     public static native int checkAudioFlinger();
 
+    /** @hide */
     public static native int listAudioPorts(ArrayList<AudioPort> ports, int[] generation);
+    /** @hide */
     public static native int createAudioPatch(AudioPatch[] patch,
                                             AudioPortConfig[] sources, AudioPortConfig[] sinks);
+    /** @hide */
     public static native int releaseAudioPatch(AudioPatch patch);
+    /** @hide */
     public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation);
+    /** @hide */
     public static native int setAudioPortConfig(AudioPortConfig config);
 
+    /** @hide */
     public static native int startAudioSource(AudioPortConfig config,
                                               AudioAttributes audioAttributes);
+    /** @hide */
     public static native int stopAudioSource(int handle);
 
     // declare this instance as having a dynamic policy callback handler
@@ -1169,36 +1330,42 @@
     private static native final void native_register_recording_callback();
 
     // must be kept in sync with value in include/system/audio.h
-    public static final int AUDIO_HW_SYNC_INVALID = 0;
+    /** @hide */ public static final int AUDIO_HW_SYNC_INVALID = 0;
 
+    /** @hide */
     public static native int getAudioHwSyncForSession(int sessionId);
 
+    /** @hide */
     public static native int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register);
 
-    /** see AudioPolicy.setUidDeviceAffinities() */
+    /** @hide see AudioPolicy.setUidDeviceAffinities() */
     public static native int setUidDeviceAffinities(int uid, @NonNull int[] types,
             @NonNull String[] addresses);
 
-    /** see AudioPolicy.removeUidDeviceAffinities() */
+    /** @hide see AudioPolicy.removeUidDeviceAffinities() */
     public static native int removeUidDeviceAffinities(int uid);
 
-    /** see AudioPolicy.setUserIdDeviceAffinities() */
+    /** @hide see AudioPolicy.setUserIdDeviceAffinities() */
     public static native int setUserIdDeviceAffinities(int userId, @NonNull int[] types,
             @NonNull String[] addresses);
 
-    /** see AudioPolicy.removeUserIdDeviceAffinities() */
+    /** @hide see AudioPolicy.removeUserIdDeviceAffinities() */
     public static native int removeUserIdDeviceAffinities(int userId);
 
+    /** @hide */
     public static native int systemReady();
 
+    /** @hide */
     public static native float getStreamVolumeDB(int stream, int index, int device);
 
     /**
+     * @hide
      * Communicate supported system usages to audio policy service.
      */
     public static native int setSupportedSystemUsages(int[] systemUsages);
 
     /**
+     * @hide
      * @see AudioManager#setAllowedCapturePolicy()
      */
     public static native int setAllowedCapturePolicy(int uid, int flags);
@@ -1212,45 +1379,57 @@
     private static native boolean native_is_offload_supported(int encoding, int sampleRate,
             int channelMask, int channelIndexMask, int streamType);
 
+    /** @hide */
     public static native int getMicrophones(ArrayList<MicrophoneInfo> microphonesInfo);
 
+    /** @hide */
     public static native int getSurroundFormats(Map<Integer, Boolean> surroundFormats,
                                                 boolean reported);
 
     /**
+     * @hide
      * Returns a list of audio formats (codec) supported on the A2DP offload path.
      */
     public static native int getHwOffloadEncodingFormatsSupportedForA2DP(
             ArrayList<Integer> formatList);
 
+    /** @hide */
     public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled);
 
     /**
+     * @hide
      * Communicate UID of active assistant to audio policy service.
      */
     public static native int setAssistantUid(int uid);
+
     /**
+     * @hide
      * Communicate UIDs of active accessibility services to audio policy service.
      */
     public static native int setA11yServicesUids(int[] uids);
+
     /**
+     * @hide
      * Communicate UID of current InputMethodService to audio policy service.
      */
     public static native int setCurrentImeUid(int uid);
 
 
     /**
+     * @hide
      * @see AudioManager#isHapticPlaybackSupported()
      */
     public static native boolean isHapticPlaybackSupported();
 
     /**
+     * @hide
      * Send audio HAL server process pids to native audioserver process for use
      * when generating audio HAL servers tombstones
      */
     public static native int setAudioHalPids(int[] pids);
 
     /**
+     * @hide
      * @see AudioManager#isCallScreeningModeSupported()
      */
     public static native boolean isCallScreeningModeSupported();
@@ -1258,6 +1437,7 @@
     // use case routing by product strategy
 
     /**
+     * @hide
      * Sets the preferred device to use for a given audio strategy in the audio policy engine
      * @param strategy the id of the strategy to configure
      * @param device the device type and address to route to when available
@@ -1270,6 +1450,7 @@
                 device.getAddress());
     }
     /**
+     * @hide
      * Set device routing per product strategy.
      * @param strategy the id of the strategy to configure
      * @param deviceType the native device type, NOT AudioDeviceInfo types
@@ -1280,6 +1461,7 @@
             int strategy, int deviceType, String deviceAddress);
 
     /**
+     * @hide
      * Remove preferred routing for the strategy
      * @param strategy the id of the strategy to configure
      * @return {@link #SUCCESS} if successfully removed
@@ -1287,6 +1469,7 @@
     public static native int removePreferredDeviceForStrategy(int strategy);
 
     /**
+     * @hide
      * Query previously set preferred device for a strategy
      * @param strategy the id of the strategy to query for
      * @param device an array of size 1 that will contain the preferred device, or null if
@@ -1300,6 +1483,7 @@
     // Items shared with audio service
 
     /**
+     * @hide
      * The delay before playing a sound. This small period exists so the user
      * can press another key (non-volume keys, too) to have it NOT be audible.
      * <p>
@@ -1308,6 +1492,7 @@
     public static final int PLAY_SOUND_DELAY = 300;
 
     /**
+     * @hide
      * Constant to identify a focus stack entry that is used to hold the focus while the phone
      * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
      * entering and exiting calls.
@@ -1315,6 +1500,7 @@
     public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
 
     /**
+     * @hide
      * @see AudioManager#setVibrateSetting(int, int)
      */
     public static int getValueForVibrateSetting(int existingValue, int vibrateType,
@@ -1330,10 +1516,12 @@
         return existingValue;
     }
 
+    /** @hide */
     public static int getDefaultStreamVolume(int streamType) {
         return DEFAULT_STREAM_VOLUME[streamType];
     }
 
+    /** @hide */
     public static int[] DEFAULT_STREAM_VOLUME = new int[] {
         4,  // STREAM_VOICE_CALL
         7,  // STREAM_SYSTEM
@@ -1349,20 +1537,22 @@
         5, // STREAM_ASSISTANT
     };
 
+    /** @hide */
     public static String streamToString(int stream) {
         if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
         if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
         return "UNKNOWN_STREAM_" + stream;
     }
 
-    /** The platform has no specific capabilities */
+    /** @hide The platform has no specific capabilities */
     public static final int PLATFORM_DEFAULT = 0;
-    /** The platform is voice call capable (a phone) */
+    /** @hide The platform is voice call capable (a phone) */
     public static final int PLATFORM_VOICE = 1;
-    /** The platform is a television or a set-top box */
+    /** @hide The platform is a television or a set-top box */
     public static final int PLATFORM_TELEVISION = 2;
 
     /**
+     * @hide
      * Return the platform type that this is running on. One of:
      * <ul>
      * <li>{@link #PLATFORM_VOICE}</li>
@@ -1392,6 +1582,7 @@
     }
 
     /**
+     * @hide
      * Return a set of audio device types from a bit mask audio device type, which may
      * represent multiple audio device types.
      * FIXME: Remove this when getting ride of bit mask usage of audio device types.
@@ -1409,6 +1600,7 @@
     }
 
     /**
+     * @hide
      * Return the intersection of two audio device types collections.
      */
     public static Set<Integer> intersectionAudioDeviceTypes(
@@ -1419,12 +1611,14 @@
     }
 
     /**
+     * @hide
      * Return true if the audio device types collection only contains the given device type.
      */
     public static boolean isSingleAudioDeviceType(@NonNull Set<Integer> types, int type) {
         return types.size() == 1 && types.contains(type);
     }
 
+    /** @hide */
     public static final int DEFAULT_MUTE_STREAMS_AFFECTED =
             (1 << STREAM_MUSIC) |
             (1 << STREAM_RING) |
@@ -1434,6 +1628,7 @@
             (1 << STREAM_BLUETOOTH_SCO);
 
     /**
+     * @hide
      * Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes.
      * Keep in sync with core/jni/android_media_DeviceCallback.h.
      */
diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java
index 88a8295..540955f 100644
--- a/media/java/android/media/MediaMetrics.java
+++ b/media/java/android/media/MediaMetrics.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Bundle;
 
@@ -24,6 +25,7 @@
 import java.nio.ByteOrder;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.util.Objects;
 
 /**
  * MediaMetrics is the Java interface to the MediaMetrics service.
@@ -50,6 +52,77 @@
     private static final Charset MEDIAMETRICS_CHARSET = StandardCharsets.UTF_8;
 
     /**
+     * Key interface.
+     *
+     * The presence of this {@code Key} interface on an object allows
+     * it to be used to set metrics.
+     *
+     * @param <T> type of value associated with {@code Key}.
+     */
+    public interface Key<T> {
+        /**
+         * Returns the internal name of the key.
+         */
+        @NonNull
+        String getName();
+
+        /**
+         * Returns the class type of the associated value.
+         */
+        @NonNull
+        Class<T> getValueClass();
+    }
+
+    /**
+     * Returns a Key object with the correct interface for MediaMetrics.
+     *
+     * @param name The name of the key.
+     * @param type The class type of the value represented by the key.
+     * @param <T> The type of value.
+     * @return a new key interface.
+     */
+    @NonNull
+    public static <T> Key<T> createKey(@NonNull String name, @NonNull Class<T> type) {
+        // Implementation specific.
+        return new Key<T>() {
+            private final String mName = name;
+            private final Class<T> mType = type;
+
+            @Override
+            @NonNull
+            public String getName() {
+                return mName;
+            }
+
+            @Override
+            @NonNull
+            public Class<T> getValueClass() {
+                return mType;
+            }
+
+            /**
+             * Return true if the name and the type of two objects are the same.
+             */
+            @Override
+            public boolean equals(Object obj) {
+                if (obj == this) {
+                    return true;
+                }
+                if (!(obj instanceof Key)) {
+                    return false;
+                }
+                Key<?> other = (Key<?>) obj;
+                return mName.equals(other.getName()) && mType.equals(other.getValueClass());
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(mName, mType);
+            }
+        };
+    }
+
+    /**
      * Item records properties and delivers to the MediaMetrics service
      *
      */
@@ -202,6 +275,28 @@
         }
 
         /**
+         * Sets a metrics typed key
+         * @param key
+         * @param value
+         * @param <T>
+         * @return
+         */
+        @NonNull
+        public <T> Item set(@NonNull Key<T> key, @Nullable T value) {
+            if (value instanceof Integer) {
+                putInt(key.getName(), (int) value);
+            } else if (value instanceof Long) {
+                putLong(key.getName(), (long) value);
+            } else if (value instanceof Double) {
+                putDouble(key.getName(), (double) value);
+            } else if (value instanceof String) {
+                putString(key.getName(), (String) value);
+            }
+            // if value is null, etc. no error is raised.
+            return this;
+        }
+
+        /**
          * Sets the property with key to an integer (32 bit) value.
          *
          * @param key
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 9ce895e..9913d23 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -20,12 +20,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.content.Context;
 import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.tuner.Tuner.Result;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
 
 /**
  * LNB (low-noise block downconverter) for satellite tuner.
@@ -145,7 +145,8 @@
 
     int mId;
     LnbCallback mCallback;
-    Context mContext;
+    Executor mExecutor;
+
 
     private native int nativeSetVoltage(int voltage);
     private native int nativeSetTone(int tone);
@@ -159,10 +160,20 @@
         mId = id;
     }
 
-    void setCallback(@Nullable LnbCallback callback) {
+    void setCallback(Executor executor, @Nullable LnbCallback callback) {
         mCallback = callback;
-        if (mCallback == null) {
-            return;
+        mExecutor = executor;
+    }
+
+    private void onEvent(int eventType) {
+        if (mExecutor != null && mCallback != null) {
+            mExecutor.execute(() -> mCallback.onEvent(eventType));
+        }
+    }
+
+    private void onDiseqcMessage(byte[] diseqcMessage) {
+        if (mExecutor != null && mCallback != null) {
+            mExecutor.execute(() -> mCallback.onDiseqcMessage(diseqcMessage));
         }
     }
 
@@ -218,6 +229,9 @@
      * Releases the LNB instance.
      */
     public void close() {
-        nativeClose();
+        int res = nativeClose();
+        if (res != Tuner.RESULT_SUCCESS) {
+            TunerUtils.throwExceptionForResult(res, "Failed to close LNB");
+        }
     }
 }
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index cf1f1b5..48aed34 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -45,6 +45,7 @@
 import android.media.tv.tunerresourcemanager.ResourceClientProfile;
 import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
 import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
+import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
 import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
 import android.media.tv.tunerresourcemanager.TunerLnbRequest;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
@@ -256,6 +257,36 @@
         mTunerResourceManager.registerClientProfile(
                 profile, new HandlerExecutor(mHandler), mResourceListener, clientId);
         mClientId = clientId[0];
+
+        setFrontendInfoList();
+        setLnbIds();
+    }
+
+    private void setFrontendInfoList() {
+        List<Integer> ids = nativeGetFrontendIds();
+        if (ids == null) {
+            return;
+        }
+        TunerFrontendInfo[] infos = new TunerFrontendInfo[ids.size()];
+        for (int i = 0; i < ids.size(); i++) {
+            int id = ids.get(i);
+            FrontendInfo frontendInfo = nativeGetFrontendInfo(id);
+            if (frontendInfo == null) {
+                continue;
+            }
+            TunerFrontendInfo tunerFrontendInfo = new TunerFrontendInfo(
+                    id, frontendInfo.getType(), frontendInfo.getExclusiveGroupId());
+            infos[i] = tunerFrontendInfo;
+        }
+        mTunerResourceManager.setFrontendInfoList(infos);
+    }
+
+    private void setLnbIds() {
+        int[] ids = nativeGetLnbIds();
+        if (ids == null) {
+            return;
+        }
+        mTunerResourceManager.setLnbInfoList(ids);
     }
 
     /**
@@ -358,7 +389,7 @@
     private native Filter nativeOpenFilter(int type, int subType, long bufferSize);
     private native TimeFilter nativeOpenTimeFilter();
 
-    private native List<Integer> nativeGetLnbIds();
+    private native int[] nativeGetLnbIds();
     private native Lnb nativeOpenLnbByHandle(int handle);
     private native Lnb nativeOpenLnbByName(String name);
 
@@ -829,6 +860,9 @@
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(cb, "LnbCallback must not be null");
         checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB);
+        if (mLnb != null) {
+            mLnb.setCallback(executor, cb);
+        }
         return mLnb;
     }
 
@@ -847,7 +881,11 @@
         Objects.requireNonNull(name, "LNB name must not be null");
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(cb, "LnbCallback must not be null");
-        return nativeOpenLnbByName(name);
+        mLnb = nativeOpenLnbByName(name);
+        if (mLnb != null) {
+            mLnb.setCallback(executor, cb);
+        }
+        return mLnb;
     }
 
     private boolean requestLnb() {
@@ -872,12 +910,6 @@
         return nativeOpenTimeFilter();
     }
 
-    private void onLnbEvent(int eventType) {
-        if (mHandler != null) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LNB_EVENT, eventType, 0));
-        }
-    }
-
     /**
      * Opens a Descrambler in tuner.
      *
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 547a492..c1589cf 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -137,6 +137,8 @@
             msg = "";
         }
         switch (r) {
+            case Tuner.RESULT_SUCCESS:
+                return;
             case Tuner.RESULT_INVALID_ARGUMENT:
                 throw new IllegalArgumentException(msg);
             case Tuner.RESULT_INVALID_STATE:
diff --git a/media/java/android/media/tv/tuner/filter/TimeFilter.java b/media/java/android/media/tv/tuner/filter/TimeFilter.java
index da77b50..93599e6 100644
--- a/media/java/android/media/tv/tuner/filter/TimeFilter.java
+++ b/media/java/android/media/tv/tuner/filter/TimeFilter.java
@@ -83,7 +83,7 @@
     @Result
     public int clearTimestamp() {
         int res = nativeClearTimestamp();
-        if (res == 0) {
+        if (res == Tuner.RESULT_SUCCESS) {
             mEnable = false;
         }
         return res;
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index 63a71e2..2c8899c 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -390,7 +390,7 @@
      * <li>If no Lnb system can be granted, the API would return false.
      * <ul>
      *
-     * <p><strong>Note:</strong> {@link #setLnbInfos(int[])} must be called before this request.
+     * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this request.
      *
      * @param request {@link TunerLnbRequest} information of the current request.
      * @param lnbId a one-element array to return the granted Lnb id.
@@ -479,7 +479,7 @@
      *
      * <p>Client must call this whenever it releases an Lnb.
      *
-     * <p><strong>Note:</strong> {@link #setLnbInfos(int[])} must be called before this release.
+     * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this release.
      *
      * @param lnbId the id of the released Tuner Lnb.
      */
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 312e5fe..ac7fe5d 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -155,6 +155,7 @@
     jmethodID onFilterEventID;
     jmethodID lnbInitID;
     jmethodID onLnbEventID;
+    jmethodID onLnbDiseqcMessageID;
     jmethodID onDvrRecordStatusID;
     jmethodID onDvrPlaybackStatusID;
     jmethodID descramblerInitID;
@@ -170,19 +171,31 @@
 
 namespace android {
 /////////////// LnbCallback ///////////////////////
-LnbCallback::LnbCallback(jweak tunerObj, LnbId id) : mObject(tunerObj), mId(id) {}
+LnbCallback::LnbCallback(jobject lnbObj, LnbId id) : mId(id) {
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    mLnb = env->NewWeakGlobalRef(lnbObj);
+}
 
 Return<void> LnbCallback::onEvent(LnbEventType lnbEventType) {
     ALOGD("LnbCallback::onEvent, type=%d", lnbEventType);
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(
-            mObject,
+            mLnb,
             gFields.onLnbEventID,
             (jint)lnbEventType);
     return Void();
 }
-Return<void> LnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& /*diseqcMessage*/) {
+Return<void> LnbCallback::onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage) {
     ALOGD("LnbCallback::onDiseqcMessage");
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jbyteArray array = env->NewByteArray(diseqcMessage.size());
+    env->SetByteArrayRegion(
+            array, 0, diseqcMessage.size(), reinterpret_cast<jbyte*>(diseqcMessage[0]));
+
+    env->CallVoidMethod(
+            mLnb,
+            gFields.onLnbDiseqcMessageID,
+            array);
     return Void();
 }
 
@@ -875,6 +888,10 @@
         return NULL;
     }
     mFe = fe;
+    mFeId = id;
+    if (mDemux != NULL) {
+        mDemux->setFrontendDataSource(mFeId);
+    }
     sp<FrontendCallback> feCb = new FrontendCallback(mObject, id);
     fe->setCallback(feCb);
 
@@ -1062,43 +1079,40 @@
             maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps);
 }
 
-jobject JTuner::getLnbIds() {
+jintArray JTuner::getLnbIds() {
     ALOGD("JTuner::getLnbIds()");
-    mTuner->getLnbIds([&](Result, const hidl_vec<FrontendId>& lnbIds) {
-        mLnbIds = lnbIds;
+    Result res;
+    hidl_vec<LnbId> lnbIds;
+    mTuner->getLnbIds([&](Result r, const hidl_vec<LnbId>& ids) {
+        lnbIds = ids;
+        res = r;
     });
-    if (mLnbIds.size() == 0) {
+    if (res != Result::SUCCESS || mLnbIds.size() == 0) {
         ALOGW("Lnb isn't available");
         return NULL;
     }
 
+    mLnbIds = lnbIds;
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    jclass arrayListClazz = env->FindClass("java/util/ArrayList");
-    jmethodID arrayListAdd = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z");
-    jobject obj = env->NewObject(arrayListClazz, env->GetMethodID(arrayListClazz, "<init>", "()V"));
 
-    jclass integerClazz = env->FindClass("java/lang/Integer");
-    jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
+    jintArray ids = env->NewIntArray(mLnbIds.size());
+    env->SetIntArrayRegion(ids, 0, mLnbIds.size(), reinterpret_cast<jint*>(&mLnbIds[0]));
 
-    for (int i=0; i < mLnbIds.size(); i++) {
-       jobject idObj = env->NewObject(integerClazz, intInit, mLnbIds[i]);
-       env->CallBooleanMethod(obj, arrayListAdd, idObj);
-    }
-    return obj;
+    return ids;
 }
 
 jobject JTuner::openLnbById(int id) {
     sp<ILnb> iLnbSp;
-    mTuner->openLnbById(id, [&](Result, const sp<ILnb>& lnb) {
+    Result r;
+    mTuner->openLnbById(id, [&](Result res, const sp<ILnb>& lnb) {
+        r = res;
         iLnbSp = lnb;
     });
-    if (iLnbSp == nullptr) {
+    if (r != Result::SUCCESS || iLnbSp == nullptr) {
         ALOGE("Failed to open lnb");
         return NULL;
     }
     mLnb = iLnbSp;
-    sp<LnbCallback> lnbCb = new LnbCallback(mObject, id);
-    mLnb->setCallback(lnbCb);
 
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     jobject lnbObj = env->NewObject(
@@ -1106,6 +1120,9 @@
             gFields.lnbInitID,
             (jint) id);
 
+    sp<LnbCallback> lnbCb = new LnbCallback(lnbObj, id);
+    mLnb->setCallback(lnbCb);
+
     sp<Lnb> lnbSp = new Lnb(iLnbSp, lnbObj);
     lnbSp->incStrong(lnbObj);
     env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get());
@@ -1129,14 +1146,15 @@
         return NULL;
     }
     mLnb = iLnbSp;
-    sp<LnbCallback> lnbCb = new LnbCallback(mObject, id);
-    mLnb->setCallback(lnbCb);
 
     jobject lnbObj = env->NewObject(
             env->FindClass("android/media/tv/tuner/Lnb"),
             gFields.lnbInitID,
             id);
 
+    sp<LnbCallback> lnbCb = new LnbCallback(lnbObj, id);
+    mLnb->setCallback(lnbCb);
+
     sp<Lnb> lnbSp = new Lnb(iLnbSp, lnbObj);
     lnbSp->incStrong(lnbObj);
     env->SetLongField(lnbObj, gFields.lnbContext, (jlong) lnbSp.get());
@@ -1206,12 +1224,21 @@
         return Result::SUCCESS;
     }
     Result res;
+    uint32_t id;
+    sp<IDemux> demuxSp;
     mTuner->openDemux([&](Result r, uint32_t demuxId, const sp<IDemux>& demux) {
-        mDemux = demux;
-        mDemuxId = demuxId;
+        demuxSp = demux;
+        id = demuxId;
         res = r;
         ALOGD("open demux, id = %d", demuxId);
     });
+    if (res == Result::SUCCESS) {
+        mDemux = demuxSp;
+        mDemuxId = id;
+        if (mFe != NULL) {
+            mDemux->setFrontendDataSource(mFeId);
+        }
+    }
     return res;
 }
 
@@ -2242,8 +2269,6 @@
 
     gFields.onFrontendEventID = env->GetMethodID(clazz, "onFrontendEvent", "(I)V");
 
-    gFields.onLnbEventID = env->GetMethodID(clazz, "onLnbEvent", "(I)V");
-
     jclass frontendClazz = env->FindClass("android/media/tv/tuner/Tuner$Frontend");
     gFields.frontendInitID =
             env->GetMethodID(frontendClazz, "<init>", "(Landroid/media/tv/tuner/Tuner;I)V");
@@ -2251,6 +2276,8 @@
     jclass lnbClazz = env->FindClass("android/media/tv/tuner/Lnb");
     gFields.lnbContext = env->GetFieldID(lnbClazz, "mNativeContext", "J");
     gFields.lnbInitID = env->GetMethodID(lnbClazz, "<init>", "(I)V");
+    gFields.onLnbEventID = env->GetMethodID(lnbClazz, "onEvent", "(I)V");
+    gFields.onLnbDiseqcMessageID = env->GetMethodID(lnbClazz, "onDiseqcMessage", "([B)V");
 
     jclass filterClazz = env->FindClass("android/media/tv/tuner/filter/Filter");
     gFields.filterContext = env->GetFieldID(filterClazz, "mNativeContext", "J");
@@ -2375,7 +2402,7 @@
     return tuner->getFrontendInfo(id);
 }
 
-static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
+static jintArray android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->getLnbIds();
 }
@@ -3204,8 +3231,14 @@
     return (jint) r;
 }
 
-static int android_media_tv_Tuner_close_lnb(JNIEnv*, jobject) {
-    return 0;
+static int android_media_tv_Tuner_close_lnb(JNIEnv* env, jobject lnb) {
+    sp<Lnb> lnbSp = getLnb(env, lnb);
+    Result r = lnbSp->getILnb()->close();
+    if (r == Result::SUCCESS) {
+        lnbSp->decStrong(lnb);
+        env->SetLongField(lnb, gFields.lnbContext, 0);
+    }
+    return (jint) r;
 }
 
 static void android_media_tv_Tuner_dvr_set_fd(JNIEnv *env, jobject dvr, jobject jfd) {
@@ -3379,8 +3412,7 @@
             (void *)android_media_tv_Tuner_open_filter },
     { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/filter/TimeFilter;",
             (void *)android_media_tv_Tuner_open_time_filter },
-    { "nativeGetLnbIds", "()Ljava/util/List;",
-            (void *)android_media_tv_Tuner_get_lnb_ids },
+    { "nativeGetLnbIds", "()[I", (void *)android_media_tv_Tuner_get_lnb_ids },
     { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;",
             (void *)android_media_tv_Tuner_open_lnb_by_handle },
     { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;",
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index e6f10b2..73fc38d 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -70,7 +70,7 @@
     LnbCallback(jweak tunerObj, LnbId id);
     virtual Return<void> onEvent(LnbEventType lnbEventType);
     virtual Return<void> onDiseqcMessage(const hidl_vec<uint8_t>& diseqcMessage);
-    jweak mObject;
+    jweak mLnb;
     LnbId mId;
 };
 
@@ -179,7 +179,7 @@
     int stopScan();
     int setLnb(int id);
     int setLna(bool enable);
-    jobject getLnbIds();
+    jintArray getLnbIds();
     jobject openLnbById(int id);
     jobject openLnbByName(jstring name);
     jobject openFilter(DemuxFilterType type, int bufferSize);
@@ -199,6 +199,7 @@
     static sp<ITuner> mTuner;
     hidl_vec<FrontendId> mFeIds;
     sp<IFrontend> mFe;
+    int mFeId;
     hidl_vec<LnbId> mLnbIds;
     sp<ILnb> mLnb;
     sp<IDemux> mDemux;
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 43e2918..0b56d05 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -32,6 +32,10 @@
     <!-- Disable normal notification rendering; we handle that ourselves -->
     <bool name="config_renderNotifications">false</bool>
 
+    <!-- Whether navigationBar touch events should be consumed before reaching the CarFacetButton \
+         when the notification panel is open. -->
+    <bool name="config_consumeNavigationBarTouchWhenNotificationPanelOpen">false</bool>
+
     <!-- Whether heads-up notifications should be shown when shade is open. -->
     <bool name="config_enableHeadsUpNotificationWhenNotificationShadeOpen">true</bool>
     <!-- Whether heads-up notifications should be shown on the bottom. If false, heads-up
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java
index f11eff8..d84b2f9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarComponentBinder.java
@@ -28,7 +28,6 @@
 @Module(includes = {
         DefaultActivityBinder.class,
         DefaultBroadcastReceiverBinder.class,
-        DefaultServiceBinder.class,
-        CarSystemUIBinder.class})
+        DefaultServiceBinder.class})
 public class CarComponentBinder {
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 5547fee..c275536 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -65,7 +65,7 @@
 import dagger.Provides;
 
 @Module(includes = {DividerModule.class})
-abstract class CarSystemUIModule {
+public abstract class CarSystemUIModule {
 
     @Singleton
     @Provides
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index 7d544c9..0e923f71 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -40,6 +40,6 @@
                 CarSystemUIModule.class,
                 CarSystemUIBinder.class
         })
-interface CarSystemUIRootComponent extends SystemUIRootComponent {
+public interface CarSystemUIRootComponent extends SystemUIRootComponent {
 
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 9d98479..1901a2d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -599,6 +599,11 @@
         }
     }
 
+    /** Returns {@code true} if the notification panel is expanded. */
+    public boolean isPanelExpanded() {
+        return mPanelExpanded;
+    }
+
     /** Sets the unseen count listener. */
     public void setOnUnseenCountUpdateListener(OnUnseenCountUpdateListener listener) {
         mUnseenCountUpdateListener = listener;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
index 1cfc832..110c2ee 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
@@ -20,7 +20,6 @@
 import android.content.res.Configuration;
 
 import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.CarServiceProvider;
 import com.android.systemui.navigationbar.car.CarNavigationBarController;
 import com.android.systemui.statusbar.car.PowerManagerHelper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -36,7 +35,6 @@
 
     private final CarNavigationBarController mCarNavigationBarController;
     private final NotificationPanelViewController mNotificationPanelViewController;
-    private final CarServiceProvider mCarServiceProvider;
     private final PowerManagerHelper mPowerManagerHelper;
     private final CarDeviceProvisionedController mCarDeviceProvisionedController;
     private final ConfigurationController mConfigurationController;
@@ -46,7 +44,6 @@
             CarNavigationBarController carNavigationBarController,
             NotificationPanelViewController notificationPanelViewController,
 
-            CarServiceProvider carServiceProvider,
             PowerManagerHelper powerManagerHelper,
 
             CarDeviceProvisionedController carDeviceProvisionedController,
@@ -54,7 +51,6 @@
     ) {
         mCarNavigationBarController = carNavigationBarController;
         mNotificationPanelViewController = notificationPanelViewController;
-        mCarServiceProvider = carServiceProvider;
         mPowerManagerHelper = powerManagerHelper;
         mCarDeviceProvisionedController = carDeviceProvisionedController;
         mConfigurationController = configurationController;
@@ -72,7 +68,17 @@
                 mNotificationPanelViewController.getNavBarNotificationTouchListener());
 
         mCarNavigationBarController.registerNotificationController(
-                () -> mNotificationPanelViewController.toggle());
+                new CarNavigationBarController.NotificationsShadeController() {
+                    @Override
+                    public void togglePanel() {
+                        mNotificationPanelViewController.toggle();
+                    }
+
+                    @Override
+                    public boolean isNotificationPanelOpen() {
+                        return mNotificationPanelViewController.isPanelExpanded();
+                    }
+                });
     }
 
     @Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
index 67e9da4..fbcd878 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
@@ -314,6 +314,9 @@
     public interface NotificationsShadeController {
         /** Toggles the visibility of the notifications shade. */
         void togglePanel();
+
+        /** Returns {@code true} if the panel is open. */
+        boolean isNotificationPanelOpen();
     }
 
     private void checkAllBars(boolean isSetUp) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
index 28da169..5b99f53 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
@@ -35,10 +35,11 @@
  * in a linear layout.
  */
 public class CarNavigationBarView extends LinearLayout {
+    private final boolean mConsumeTouchWhenPanelOpen;
+
     private View mNavButtons;
     private CarNavigationButton mNotificationsButton;
     private NotificationsShadeController mNotificationsShadeController;
-    private Context mContext;
     private View mLockScreenButtons;
     // used to wire in open/close gestures for notifications
     private OnTouchListener mStatusBarWindowTouchListener;
@@ -46,7 +47,8 @@
 
     public CarNavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mContext = context;
+        mConsumeTouchWhenPanelOpen = getResources().getBoolean(
+                R.bool.config_consumeNavigationBarTouchWhenNotificationPanelOpen);
     }
 
     @Override
@@ -77,9 +79,16 @@
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (mStatusBarWindowTouchListener != null) {
+            boolean shouldConsumeEvent = mNotificationsShadeController == null ? false
+                    : mNotificationsShadeController.isNotificationPanelOpen();
+
             // Forward touch events to the status bar window so it can drag
             // windows if required (Notification shade)
             mStatusBarWindowTouchListener.onTouch(this, ev);
+
+            if (mConsumeTouchWhenPanelOpen && shouldConsumeEvent) {
+                return true;
+            }
         }
         return super.onInterceptTouchEvent(ev);
     }
diff --git a/packages/CarSystemUI/tests/res/layout/car_navigation_bar_view_test.xml b/packages/CarSystemUI/tests/res/layout/car_navigation_bar_view_test.xml
new file mode 100644
index 0000000..b0ca8dc
--- /dev/null
+++ b/packages/CarSystemUI/tests/res/layout/car_navigation_bar_view_test.xml
@@ -0,0 +1,58 @@
+<!--
+  ~ 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.
+  -->
+
+<com.android.systemui.navigationbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/system_bar_background"
+    android:orientation="vertical">
+    <!--The 20dp padding is the difference between the background selected icon size and the ripple
+        that was chosen, thus it's a hack to make it look pretty and not an official margin value-->
+    <LinearLayout
+        android:id="@id/nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingStart="20dp"
+        android:paddingEnd="20dp"
+        android:gravity="center">
+
+        <com.android.systemui.navigationbar.car.CarNavigationButton
+            android:id="@+id/home"
+            style="@style/NavigationBarButton"
+            systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+            systemui:icon="@drawable/car_ic_overview"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+            systemui:selectedIcon="@drawable/car_ic_overview_selected"
+            systemui:highlightWhenSelected="true"
+        />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/lock_screen_nav_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingStart="@dimen/car_keyline_1"
+        android:paddingEnd="@dimen/car_keyline_1"
+        android:gravity="center"
+        android:visibility="gone"
+    />
+
+</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarViewTest.java
new file mode 100644
index 0000000..9e2131c
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarViewTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.navigationbar.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class CarNavigationBarViewTest extends SysuiTestCase {
+
+    private CarNavigationBarView mNavBarView;
+
+    @Mock
+    private CarNavigationBarController.NotificationsShadeController mNotificationsShadeController;
+
+    @Mock
+    private View.OnTouchListener mNavBarTouchListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @After
+    public void tearDown() {
+        getContext().getOrCreateTestableResources().addOverride(
+                R.bool.config_consumeNavigationBarTouchWhenNotificationPanelOpen, false);
+    }
+
+    @Test
+    public void dispatchTouch_shadeOpen_flagOff_doesNotConsumeTouch() {
+        getContext().getOrCreateTestableResources().addOverride(
+                R.bool.config_consumeNavigationBarTouchWhenNotificationPanelOpen, false);
+        when(mNotificationsShadeController.isNotificationPanelOpen()).thenReturn(true);
+        mNavBarView = (CarNavigationBarView) LayoutInflater.from(getContext()).inflate(
+                R.layout.car_navigation_bar_view_test, /* root= */ null);
+        mNavBarView.setNotificationsPanelController(mNotificationsShadeController);
+        mNavBarView.setStatusBarWindowTouchListener(mNavBarTouchListener);
+
+        boolean consume = mNavBarView.onInterceptTouchEvent(
+                MotionEvent.obtain(/* downTime= */ 200, /* eventTime= */ 300,
+                        MotionEvent.ACTION_MOVE, mNavBarView.getX(),
+                        mNavBarView.getY(), /* metaState= */ 0));
+
+        assertThat(consume).isFalse();
+    }
+
+    @Test
+    public void dispatchTouch_shadeOpen_flagOn_consumesTouch() {
+        getContext().getOrCreateTestableResources().addOverride(
+                R.bool.config_consumeNavigationBarTouchWhenNotificationPanelOpen, true);
+        when(mNotificationsShadeController.isNotificationPanelOpen()).thenReturn(true);
+        mNavBarView = (CarNavigationBarView) LayoutInflater.from(getContext()).inflate(
+                R.layout.car_navigation_bar_view_test, /* root= */ null);
+        mNavBarView.setNotificationsPanelController(mNotificationsShadeController);
+        mNavBarView.setStatusBarWindowTouchListener(mNavBarTouchListener);
+
+        boolean consume = mNavBarView.onInterceptTouchEvent(
+                MotionEvent.obtain(/* downTime= */ 200, /* eventTime= */ 300,
+                        MotionEvent.ACTION_MOVE, mNavBarView.getX(),
+                        mNavBarView.getY(), /* metaState= */ 0));
+
+        assertThat(consume).isTrue();
+    }
+}
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 1681fdb..126efcc 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -206,11 +206,11 @@
     <string name="enable_adb" msgid="8072776357237289039">"የUSB አራሚ"</string>
     <string name="enable_adb_summary" msgid="3711526030096574316">"USB ሲያያዝ የአርም ሁኔታ"</string>
     <string name="clear_adb_keys" msgid="3010148733140369917">"የዩ ኤስ ቢ ስህተት ማረም ፈቀዳዎችን ይሻሩ"</string>
-    <string name="enable_adb_wireless" msgid="6973226350963971018">"ገመድ-አልባ ማረም"</string>
+    <string name="enable_adb_wireless" msgid="6973226350963971018">"ገመድ-አልባ debugging"</string>
     <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Wi-Fi ሲገናኝ የማረም ሁነታ"</string>
     <string name="adb_wireless_error" msgid="721958772149779856">"ስህተት"</string>
-    <string name="adb_wireless_settings" msgid="2295017847215680229">"ገመድ-አልባ ማረም"</string>
-    <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"የሚገኙ መሣሪያዎችን ለመመልከትና ለመጠቀም ገመድ-አልባ ማረምን ያብሩ"</string>
+    <string name="adb_wireless_settings" msgid="2295017847215680229">"ገመድ-አልባ debugging"</string>
+    <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"የሚገኙ መሣሪያዎችን ለመመልከትና ለመጠቀም ገመድ-አልባ debuggingን ያብሩ"</string>
     <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"የQR ኮድን በመጠቀም መሣሪያን ያጣምሩ"</string>
     <string name="adb_pair_method_qrcode_summary" msgid="3729901496856458634">"የQR ኮድ መቃኛን በመጠቀም አዲስ መሣሪያዎችን ያጣምሩ"</string>
     <string name="adb_pair_method_code_title" msgid="1122590300445142904">"የማጣመሪያ ኮድን በመጠቀም መሣሪያን ያጣምሩ"</string>
@@ -300,8 +300,8 @@
     <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"የሃርድዌር ማቀላጠፊያን ማስተሳሰርን የሚገኝ ከሆነ ይጠቀሙ"</string>
     <string name="adb_warning_title" msgid="7708653449506485728">"የUSB ማረሚያ ይፈቀድ?"</string>
     <string name="adb_warning_message" msgid="8145270656419669221">"የUSB አድስ ለግንባታ አላማ ብቻ የታሰበ ነው። ከኮምፒዩተርህ ወደ መሳሪያህ ውሂብ ለመገልበጥ፣ መሣሪያህ ላይ ያለ ማሳወቂያ መተግበሪያዎችን መጫን፣ እና ማስታወሻ ውሂብ ማንበብ ለመጠቀም ይቻላል።"</string>
-    <string name="adbwifi_warning_title" msgid="727104571653031865">"ገመድ-አልባ ማረም ይፈቀድ?"</string>
-    <string name="adbwifi_warning_message" msgid="8005936574322702388">"ገመድ-አልባ ማረም ለግንባታ አላማዎች ብቻ የታሰበ ነው። ውሂብን ከኮምፒዩተርዎ ወደ መሳሪያዎ ለመቅዳት፣ መሣሪያዎ ላይ ያለማሳወቂያ መተግበሪያዎችን ለመጫን እና የምዝግብ ማስታወሻ ውሂብን ለማንበብ ይጠቀሙበት።"</string>
+    <string name="adbwifi_warning_title" msgid="727104571653031865">"ገመድ-አልባ debugging ይፈቀድ?"</string>
+    <string name="adbwifi_warning_message" msgid="8005936574322702388">"ገመድ-አልባ debugging ለግንባታ አላማዎች ብቻ የታሰበ ነው። ውሂብን ከኮምፒዩተርዎ ወደ መሳሪያዎ ለመቅዳት፣ መሣሪያዎ ላይ ያለማሳወቂያ መተግበሪያዎችን ለመጫን እና የምዝግብ ማስታወሻ ውሂብን ለማንበብ ይጠቀሙበት።"</string>
     <string name="adb_keys_warning_message" msgid="2968555274488101220">"የዩ ኤስ ቢ ማረም መዳረሻ ከዚህ ቀደም ፍቃድ ከሰጧቸው ኮምፒውተሮች ላይ ይሻሩ?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"የግንባታ ቅንብሮችን ፍቀድ?"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"እነዚህ ቅንብሮች  የታሰቡት ለግንባታ አጠቃቀም ብቻ ናቸው። መሳሪያህን እና በሱ ላይ ያሉትን መተግበሪያዎች እንዲበለሹ ወይም በትክክል እንዳይሰሩ ሊያደርጉ ይችላሉ።"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index abd1e03..88d4c03 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -232,8 +232,7 @@
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"‏عنوان IP والمنفذ"</string>
     <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"المسح الضوئي لرمز الاستجابة السريعة"</string>
     <string name="adb_wireless_qrcode_pairing_description" msgid="8578868049289910131">"‏إقران الجهاز من خلال شبكة Wi‑Fi عن طريق المسح الضوئي لرمز استجابة سريعة"</string>
-    <!-- no translation found for adb_wireless_no_network_msg (2365795244718494658) -->
-    <skip />
+    <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"‏يُرجى الاتصال بشبكة Wi-Fi."</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"‏adb، تصحيح الأخطاء، مطور برامج"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"اختصار تقرير الأخطاء"</string>
     <string name="bugreport_in_power_summary" msgid="1885529649381831775">"عرض زر في قائمة خيارات التشغيل لإعداد تقرير بالأخطاء"</string>
@@ -432,8 +431,7 @@
     <string name="power_discharge_by" msgid="4113180890060388350">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_discharge_by_only" msgid="92545648425937000">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g>."</string>
     <string name="power_discharge_by_only_short" msgid="5883041507426914446">"حتى <xliff:g id="TIME">%1$s</xliff:g>"</string>
-    <!-- no translation found for power_suggestion_battery_run_out (6332089307827787087) -->
-    <skip />
+    <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"قد ينفد شحن البطارية قبل <xliff:g id="TIME">%1$s</xliff:g>."</string>
     <string name="power_remaining_less_than_duration_only" msgid="5802195288324091585">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
     <string name="power_remaining_less_than_duration" msgid="1812668275239801236">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_remaining_more_than_subtext" msgid="7919119719242734848">"يتبقى أكثر من <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
@@ -515,32 +513,21 @@
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"مكبر صوت الهاتف"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string>
-    <!-- no translation found for help_label (3528360748637781274) -->
-    <skip />
-    <!-- no translation found for storage_category (2287342585424631813) -->
-    <skip />
-    <!-- no translation found for shared_data_title (1017034836800864953) -->
-    <skip />
-    <!-- no translation found for shared_data_summary (5516326713822885652) -->
-    <skip />
+    <string name="help_label" msgid="3528360748637781274">"المساعدة والتعليقات"</string>
+    <string name="storage_category" msgid="2287342585424631813">"مساحة التخزين"</string>
+    <string name="shared_data_title" msgid="1017034836800864953">"البيانات المشتركة"</string>
+    <string name="shared_data_summary" msgid="5516326713822885652">"عرض البيانات المشتركة وتعديلها"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"ما مِن بيانات مشتركة لهذا المستخدم."</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"حدث خطأ أثناء جلب البيانات المشتركة. يُرجى إعادة المحاولة."</string>
-    <!-- no translation found for blob_id_text (8680078988996308061) -->
-    <skip />
-    <!-- no translation found for blob_expires_text (7882727111491739331) -->
-    <skip />
+    <string name="blob_id_text" msgid="8680078988996308061">"معرّف البيانات المشتركة: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
+    <string name="blob_expires_text" msgid="7882727111491739331">"تنتهي صلاحيتها في <xliff:g id="DATE">%s</xliff:g>."</string>
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"حدث خطأ أثناء حذف البيانات المشتركة."</string>
     <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"ما مِن عمليات تأجير مطلوبة لهذه المعلومات المشتركة. هل تريد حذفها؟"</string>
-    <!-- no translation found for accessor_info_title (8289823651512477787) -->
-    <skip />
-    <!-- no translation found for accessor_no_description_text (7510967452505591456) -->
-    <skip />
-    <!-- no translation found for accessor_expires_text (4625619273236786252) -->
-    <skip />
-    <!-- no translation found for delete_blob_text (2819192607255625697) -->
-    <skip />
-    <!-- no translation found for delete_blob_confirmation_text (7807446938920827280) -->
-    <skip />
+    <string name="accessor_info_title" msgid="8289823651512477787">"التطبيقات التي تشارك البيانات"</string>
+    <string name="accessor_no_description_text" msgid="7510967452505591456">"لم يوفّر التطبيق وصفًا."</string>
+    <string name="accessor_expires_text" msgid="4625619273236786252">"تنتهي صلاحية العقد في <xliff:g id="DATE">%s</xliff:g>."</string>
+    <string name="delete_blob_text" msgid="2819192607255625697">"حذف البيانات المشتركة"</string>
+    <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"هل فعلاً تريد حذف هذه البيانات المشتركة؟"</string>
     <string name="user_add_user_item_summary" msgid="5748424612724703400">"المستخدمون لديهم تطبيقات ومحتوى خاص بهم"</string>
     <string name="user_add_profile_item_summary" msgid="5418602404308968028">"يمكنك تقييد الدخول إلى التطبيقات والمحتوى من حسابك"</string>
     <string name="user_add_user_item_title" msgid="2394272381086965029">"المستخدم"</string>
@@ -560,12 +547,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"معلومات الملف الشخصي"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"قبل أن تتمكن من إنشاء ملف شخصي مقيد، يلزمك إعداد تأمين للشاشة لحماية تطبيقاتك وبياناتك الشخصية."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"تعيين التأمين"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"التبديل إلى <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index a1c00a3..73a5e7c 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"প্ৰ\'ফাইলৰ তথ্য"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"আপুনি সীমিত প্ৰ\'ফাইল এটা সৃষ্টি কৰাৰ আগেয়ে, আপোনাৰ ব্যক্তিগত ডেটা আৰু এপবিলাকক সুৰক্ষিত কৰিবলৈ স্ক্ৰীণ লক এটা নিৰ্ধাৰণ কৰিব লাগিব।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"লক ছেট কৰক"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>লৈ সলনি কৰক"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 50451e1..4cb4017 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profil info"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Məhdudlaşdırılmış profil yaratmadan öncə, Siz tətbiqlərinizi və şəxsi datanızı qorumaq üçün ekran kilidi quraşdırmalısınız."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Kilid ayarlayın"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> adlı istifadəçiyə keçin"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 0e39d7f..839b85a 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -544,12 +544,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Podaci o profilu"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Da biste mogli da napravite ograničeni profil, treba da podesite zaključavanje ekrana da biste zaštitili aplikacije i lične podatke."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Podesi zaključavanje"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Pređi na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 328762c..cc0d286 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -545,12 +545,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Звесткi профiлю"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Перш чым вы зможаце стварыць профіль з абмежаваннямi, вам трэба наладзіць блакiроўку экрана для абароны сваiх дадаткаў і асабістай інфармацыі."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Усталёўка блакiроўкi"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Пераключыцца на карыстальніка <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Госць"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 690cc49..ef0951c 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Инф. за потр. профил"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Преди да можете да създадете потребителски профил с ограничена функционалност, трябва да настроите заключения екран, за да защитите приложенията и личните си данни."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Задаване на заключване"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Превключване към <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 017936f..1cd8eea 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"প্রোফাইল তথ্য"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"আপনি একটি সীমাবদ্ধযুক্ত প্রোফাইল তৈরি করার আগে, আপনাকে আপনার অ্যাপ্লিকেশন এবং ব্যক্তিগত ডেটা সুরক্ষিত করার জন্য একটি স্ক্রিন লক সেট-আপ করতে হবে।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"লক সেট করুন"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>-এ পাল্টান"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index a1b1afc..c72411c 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -544,8 +544,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Podaci o profilu"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prije nego vam se omogući kreiranje ograničenog profila, morate postaviti zaključavanje ekrana da biste zaštitili svoje aplikacije i lične podatke."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Postaviti zaključavanje"</string>
-    <string name="user_switch_to_user" msgid="6975428297154968543">"Prelazak na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
-    <string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string>
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Prebaci na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index a1b2da8..765d442 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Informació de perfil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Per crear un perfil restringit, has de configurar una pantalla de bloqueig per protegir les aplicacions i les dades personals."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Defineix un bloqueig"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Canvia a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index bbf29bb..ed2db3a 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -545,12 +545,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Informace o profilu"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Před vytvořením omezeného profilu je nutné nejprve nastavit zámek obrazovky k ochraně aplikací a dat."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nastavit zámek"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Přepnout na uživatele <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Host"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 20b7ae1..f682b15 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profiloplysninger"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan oprette en begrænset profil, skal du oprette en skærmlås for at beskytte dine apps og personlige data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurer låseskærmen"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Skift til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 1a28985..7918022 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profilinformationen"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Vor dem Erstellen eines eingeschränkten Profils musst du eine Displaysperre einrichten, um deine Apps und personenbezogenen Daten zu schützen."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Sperre einrichten"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Zu <xliff:g id="USER_NAME">%s</xliff:g> wechseln"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 9c1842a..b4d7c87 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Πληροφορίες προφίλ"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Προκειμένου να μπορέσετε να δημιουργήσετε ένα περιορισμένο προφίλ, θα πρέπει να δημιουργήσετε ένα κλείδωμα οθόνης για την προστασία των εφαρμογών και των προσωπικών δεδομένων σας."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Ορισμός κλειδώματος"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Εναλλαγή σε <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index cc3cd11..6cb975f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Datos del perfil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar un bloqueo de pantalla que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Configurar bloqueo"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index e85187b..d777723 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Información del perfil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar una pantalla de bloqueo que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index c9bd7a0..1fa4159 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profiili teave"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Enne piiratud profiili loomist peate seadistama lukustusekraani, et oma rakendusi ja isiklikke andmeid kaitsta."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Määra lukk"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Lülita kasutajale <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 42c702a..2700162 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -235,7 +235,7 @@
     <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Konektatu wifi-sare batera"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, araztu, gailua"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"Akatsen txostenerako lasterbidea"</string>
-    <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Bateriaren menuan, erakutsi akatsen txostena sortzeko botoia"</string>
+    <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Pizteko menuan, erakutsi akatsen txostena sortzeko botoia"</string>
     <string name="keep_screen_on" msgid="1187161672348797558">"Mantendu aktibo"</string>
     <string name="keep_screen_on_summary" msgid="1510731514101925829">"Pantaila ez da ezarriko inoiz inaktibo kargatu bitartean"</string>
     <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Gaitu Bluetooth HCI miatze-erregistroa"</string>
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profileko informazioa"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Profil murriztua sortu aurretik, aplikazioak eta datu pertsonalak babesteko, pantaila blokeatzeko metodo bat konfiguratu beharko duzu."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Ezarri blokeoa"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Aldatu <xliff:g id="USER_NAME">%s</xliff:g> erabiltzailera"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 2a284ee..1c32926 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"اطلاعات نمایه"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"قبل از ایجاد یک نمایه محدود، باید یک قفل صفحه را برای محافظت از برنامه‌ها و داده‌های شخصی خود تنظیم کنید."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"تنظیم قفل"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"رفتن به <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 76e8f5b..765e08e 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profiilin tiedot"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Ennen kuin voit luoda rajoitetun profiilin, määritä näytön lukitus, joka suojelee sovelluksiasi ja henkilökohtaisia tietojasi."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Aseta lukitus"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda tähän: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 4c599f0..d118e1f 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Informations de profil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 8e8f09d..f2dcbba 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Informations de profil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index e17da89..9ee490f 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Información do perfil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restrinxido, precisarás configurar un bloqueo da pantalla para protexer as túas aplicacións e datos persoais."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 207e07f..2fced2b 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"પ્રોફાઇલ માહિતી"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"તમે પ્રતિબંધિત પ્રોફાઇલ બનાવી શકો તે પહેલાં, તમારે તમારી ઍપ્લિકેશનો અને વ્યક્તિગત ડેટાની સુરક્ષા માટે એક લૉક સ્ક્રીન સેટ કરવાની જરૂર પડશે."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"લૉક સેટ કરો"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> પર સ્વિચ કરો"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 0c95313..cb41100 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -514,7 +514,7 @@
     <string name="shared_data_title" msgid="1017034836800864953">"शेयर किया गया डेटा"</string>
     <string name="shared_data_summary" msgid="5516326713822885652">"शेयर किए गए डेटा को देखें और उसमें बदलाव करें"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"इस उपयोगकर्ता के साथ किसी तरह का डेटा शेयर नहीं किया गया है."</string>
-    <string name="shared_data_query_failure_text" msgid="3489828881998773687">"शेयर किए गए इस डेटा को लाने में कोई गड़बड़ी हुई है. फिर से कोशिश करें."</string>
+    <string name="shared_data_query_failure_text" msgid="3489828881998773687">"शेयर किए गए डेटा को लाने में कोई गड़बड़ी हुई है. फिर से कोशिश करें."</string>
     <string name="blob_id_text" msgid="8680078988996308061">"शेयर किए गए डेटा का आईडी: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
     <string name="blob_expires_text" msgid="7882727111491739331">"डेटा का ऐक्सेस <xliff:g id="DATE">%s</xliff:g> को खत्म हो जाएगा"</string>
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"शेयर किए गए डेटा को मिटाने में कोई गड़बड़ी हुई."</string>
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"प्रोफ़ाइल की जानकारी"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"इससे पहले कि आप कोई प्रतिबंधित प्रोफ़ाइल बनाएं, आपको अपने ऐप्लिकेशन  और व्यक्तिगत डेटा की सुरक्षा करने के लिए एक स्क्रीन लॉक सेट करना होगा."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करें"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> पर जाएं"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d0fff21..bb2f17c 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profiladatok"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Mielőtt létrehozhatna egy korlátozott profilt, be kell állítania egy képernyőzárat, hogy megvédje alkalmazásait és személyes adatait."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Képernyőzár beállítása"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Váltás erre: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index a884c84..9cc883a 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Պրոֆիլի տեղեկություններ"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Նախքան դուք կկարողանաք ստեղծել սահմանափակ պրոֆիլ, դուք պետք է կարգավորեք էկրանի կողպումը` ձեր ծրագրերը և անձնական տվյալները պաշտպանելու համար:"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Կարգավորել կողպումը"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Անցնել <xliff:g id="USER_NAME">%s</xliff:g> պրոֆիլին"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 4022bab..d80eba2 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -88,7 +88,7 @@
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
     <string name="bluetooth_profile_pbap" msgid="7064307749579335765">"Berbagi kontak"</string>
     <string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"Gunakan untuk berbagi kontak"</string>
-    <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Berbagi sambungan internet"</string>
+    <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Berbagi koneksi internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
@@ -103,7 +103,7 @@
     <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"Tidak tersambung kepada server transfer file"</string>
     <string name="bluetooth_hid_profile_summary_connected" msgid="3923653977051684833">"Terhubung ke perangkat masukan"</string>
     <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"Terhubung ke perangkat untuk akses internet"</string>
-    <string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"Berbagi sambungan internet lokal dengan perangkat"</string>
+    <string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"Berbagi koneksi internet lokal dengan perangkat"</string>
     <string name="bluetooth_pan_profile_summary_use_for" msgid="7422039765025340313">"Digunakan untuk akses internet"</string>
     <string name="bluetooth_map_profile_summary_use_for" msgid="4453622103977592583">"Gunakan untuk peta"</string>
     <string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"Gunakan untuk akses SIM"</string>
@@ -169,11 +169,11 @@
     <string name="tts_install_data_title" msgid="1829942496472751703">"Instal data suara"</string>
     <string name="tts_install_data_summary" msgid="3608874324992243851">"Instal data suara yang dibutuhkan untuk sintesis suara"</string>
     <string name="tts_engine_security_warning" msgid="3372432853837988146">"Mesin sintesis suara ini mungkin dapat mengumpulkan semua teks yang akan diucapkan, termasuk di antaranya data pribadi seperti sandi dan nomor kartu kredit. Berasal dari <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> aplikasi. Gunakan metode masukan ini?"</string>
-    <string name="tts_engine_network_required" msgid="8722087649733906851">"Bahasa ini perlu sambungan jaringan yang bekerja untuk keluaran text-to-speech."</string>
+    <string name="tts_engine_network_required" msgid="8722087649733906851">"Bahasa ini perlu koneksi jaringan yang bekerja untuk keluaran text-to-speech."</string>
     <string name="tts_default_sample_string" msgid="6388016028292967973">"Ini adalah contoh sintesis suara"</string>
     <string name="tts_status_title" msgid="8190784181389278640">"Status bahasa default"</string>
     <string name="tts_status_ok" msgid="8583076006537547379">"<xliff:g id="LOCALE">%1$s</xliff:g> didukung sepenuhnya"</string>
-    <string name="tts_status_requires_network" msgid="8327617638884678896">"<xliff:g id="LOCALE">%1$s</xliff:g> membutuhkan sambungan jaringan"</string>
+    <string name="tts_status_requires_network" msgid="8327617638884678896">"<xliff:g id="LOCALE">%1$s</xliff:g> membutuhkan koneksi jaringan"</string>
     <string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> tidak didukung"</string>
     <string name="tts_status_checking" msgid="8026559918948285013">"Memeriksa…"</string>
     <string name="tts_engine_settings_title" msgid="7849477533103566291">"Setelan untuk <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
@@ -514,11 +514,11 @@
     <string name="shared_data_title" msgid="1017034836800864953">"Data bersama"</string>
     <string name="shared_data_summary" msgid="5516326713822885652">"Lihat dan ubah data bersama"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Tidak ada data yang dibagikan untuk pengguna ini."</string>
-    <string name="shared_data_query_failure_text" msgid="3489828881998773687">"Terjadi kesalahan saat mengambil data yang dibagikan. Coba lagi."</string>
+    <string name="shared_data_query_failure_text" msgid="3489828881998773687">"Terjadi error saat mengambil data yang dibagikan. Coba lagi."</string>
     <string name="blob_id_text" msgid="8680078988996308061">"ID data bersama: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
     <string name="blob_expires_text" msgid="7882727111491739331">"Berlaku sampai <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"Terjadi error saat menghapus data yang dibagikan."</string>
-    <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"Tidak ada sewa yang diperoleh dari data yang dibagikan. Apakah Anda ingin menghapusnya?"</string>
+    <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"Tidak ada lease yang diperoleh dari data yang dibagikan ini. Apakah Anda ingin menghapusnya?"</string>
     <string name="accessor_info_title" msgid="8289823651512477787">"Aplikasi yang berbagi data"</string>
     <string name="accessor_no_description_text" msgid="7510967452505591456">"Tidak ada deskripsi yang disediakan oleh aplikasi."</string>
     <string name="accessor_expires_text" msgid="4625619273236786252">"Lease akan berakhir pada <xliff:g id="DATE">%s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 437e826..2e96446 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Upplýsingar um snið"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Áður en þú getur búið til takmarkað snið þarftu að setja upp skjálás til að vernda forritin þín og persónuleg gögn."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Velja lás"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Skipta yfir í <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 86374f4..61f35ab 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -545,12 +545,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"פרטי פרופיל"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"לפני שתוכל ליצור פרופיל מוגבל, תצטרך להגדיר נעילת מסך כדי להגן על האפליקציות ועל הנתונים האישיים שלך."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"הגדרת נעילה"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"מעבר אל <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"אורח"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index ba8418b..ea9cfd8 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"プロファイル情報"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"制限付きプロファイルを作成する場合は、アプリや個人データを保護するように画面ロックを設定しておく必要があります。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ロックを設定"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> に切り替え"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index da5ff9b..106bc18 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"ინფორმაცია პროფილზე"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"შეზღუდული პროფილის შექმნამდე, საკუთარი აპლიკაციებისა და პირადი მონაცემების დასაცავად, უნდა დაბლოკოთ ეკრანი."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"საკეტის დაყენება"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>-ზე გადართვა"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"სტუმარი"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index e74f52b..0ef83bb 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Профильдік ақпарат"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Шектелген профайл жасақтауға дейін қолданбалар мен жеке деректерді қорғау үшін экран бекітпесін тағайындау қажет."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Бекітпе тағайындау"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> атты пайдаланушыға ауысу"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index c51f9a6..4cf8bed 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -518,7 +518,7 @@
     <string name="blob_id_text" msgid="8680078988996308061">"លេខសម្គាល់​ទិន្នន័យ​ដែលបាន​ចែករំលែក៖ <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
     <string name="blob_expires_text" msgid="7882727111491739331">"ផុតកំណត់​នៅថ្ងៃទី <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"មានបញ្ហា​ក្នុងការលុប​ទិន្នន័យ​ដែលបានចែករំលែក។"</string>
-    <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"មិនមានការជួល​ដែលបានទទួល​សម្រាប់​ទិន្នន័យដែលបានចែករំលែក​នេះទេ។ តើអ្នកចង់​លុបទិន្នន័យ​នេះដែរទេ?"</string>
+    <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"មិនមានភតិសន្យា​ដែលបានទទួល​សម្រាប់​ទិន្នន័យដែលបានចែករំលែក​នេះទេ។ តើអ្នកចង់​លុបទិន្នន័យ​នេះដែរទេ?"</string>
     <string name="accessor_info_title" msgid="8289823651512477787">"កម្មវិធី​ដែលកំពុង​ចែករំលែក​ទិន្នន័យ"</string>
     <string name="accessor_no_description_text" msgid="7510967452505591456">"គ្មាន​ការពណ៌នា​ដែលផ្ដល់ដោយ​កម្មវិធីទេ។"</string>
     <string name="accessor_expires_text" msgid="4625619273236786252">"ភតិសន្យា​ផុតកំណត់​នៅថ្ងៃទី <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"ព័ត៌មាន​ប្រវត្តិរូប"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"មុន​ពេល​អ្នក​អាច​បង្កើត​ប្រវត្តិ​រូប​បាន​ដាក់​កម្រិត អ្នក​ត្រូវ​រៀបចំ​ការ​ចាក់​សោ​អេក្រង់ ដើម្បី​ការពារ​កម្មវិធី និង​ទិន្នន័យ​ផ្ទាល់ខ្លួន​របស់​អ្នក។"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"កំណត់​ការ​ចាក់​សោ"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"ប្ដូរទៅ <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូល​ភ្ញៀវ"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"លុប​​​ភ្ញៀវ"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 9e37fbc..ed854ac 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -232,8 +232,7 @@
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP ವಿಳಾಸ ಮತ್ತು ಪೋರ್ಟ್"</string>
     <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR ಕೋಡ್ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ"</string>
     <string name="adb_wireless_qrcode_pairing_description" msgid="8578868049289910131">"QR ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡುವ ಮೂಲಕ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್‌ನಲ್ಲಿ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string>
-    <!-- no translation found for adb_wireless_no_network_msg (2365795244718494658) -->
-    <skip />
+    <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಸಂಪರ್ಕಿಸಿ"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, ಡೀಬಗ್, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"ದೋಷ ವರದಿಯ ಶಾರ್ಟ್‌ಕಟ್‌‌"</string>
     <string name="bugreport_in_power_summary" msgid="1885529649381831775">"ದೋಷ ವರದಿ ಮಾಡಲು ಪವರ್ ಮೆನುನಲ್ಲಿ ಬಟನ್ ತೋರಿಸು"</string>
@@ -432,8 +431,7 @@
     <string name="power_discharge_by" msgid="4113180890060388350">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ಸಮಯದವರೆಗೆ ಫೋನ್‌ ರನ್‌ ಆಗಬೇಕು"</string>
     <string name="power_discharge_by_only" msgid="92545648425937000">"<xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ಫೋನ್‌ ರನ್‌ ಆಗಬೇಕು"</string>
     <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> ರವರೆಗೆ"</string>
-    <!-- no translation found for power_suggestion_battery_run_out (6332089307827787087) -->
-    <skip />
+    <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"<xliff:g id="TIME">%1$s</xliff:g> ಗಳಲ್ಲಿ ಬ್ಯಾಟರಿ ಮುಕ್ತಾಯವಾಗಬಹುದು"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5802195288324091585">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ನಿಮಿಷಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ"</string>
     <string name="power_remaining_less_than_duration" msgid="1812668275239801236">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ಕ್ಕಿಂತ ಕಡಿಮೆ (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
     <string name="power_remaining_more_than_subtext" msgid="7919119719242734848">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
@@ -511,32 +509,21 @@
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string>
-    <!-- no translation found for help_label (3528360748637781274) -->
-    <skip />
-    <!-- no translation found for storage_category (2287342585424631813) -->
-    <skip />
-    <!-- no translation found for shared_data_title (1017034836800864953) -->
-    <skip />
-    <!-- no translation found for shared_data_summary (5516326713822885652) -->
-    <skip />
+    <string name="help_label" msgid="3528360748637781274">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
+    <string name="storage_category" msgid="2287342585424631813">"ಸಂಗ್ರಹಣೆ"</string>
+    <string name="shared_data_title" msgid="1017034836800864953">"ಹಂಚಿಕೊಳ್ಳಲಾದ ಡೇಟಾ"</string>
+    <string name="shared_data_summary" msgid="5516326713822885652">"ಹಂಚಿಕೊಳ್ಳಲಾದ ಡೇಟಾವನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ಮಾರ್ಪಡಿಸಿ"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"ಈ ಬಳಕೆದಾರರಿಗೆ ಯಾವುದೇ ಹಂಚಿದ ಡೇಟಾ ಇಲ್ಲ."</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"ಹಂಚಿದ ಡೇಟಾವನ್ನು ಪಡೆಯುವಲ್ಲಿ ದೋಷ ಕಂಡುಬಂದಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
-    <!-- no translation found for blob_id_text (8680078988996308061) -->
-    <skip />
-    <!-- no translation found for blob_expires_text (7882727111491739331) -->
-    <skip />
+    <string name="blob_id_text" msgid="8680078988996308061">"ಹಂಚಿಕೊಳ್ಳಲಾದ ಡೇಟಾ ID: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
+    <string name="blob_expires_text" msgid="7882727111491739331">"<xliff:g id="DATE">%s</xliff:g> ರಂದು ಅವಧಿ ಮುಕ್ತಾಯವಾಗಲಿದೆ"</string>
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"ಹಂಚಿದ ಡೇಟಾವನ್ನು ಅಳಿಸುವಾಗ ದೋಷ ಕಂಡುಬಂದಿದೆ."</string>
     <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"ಈ ಹಂಚಿದ ಡೇಟಾವನ್ನು ಗುತ್ತಿಗೆಗೆ ಪಡೆದಿಲ್ಲ. ನೀವು ಅದನ್ನು ಅಳಿಸಲು ಬಯಸುವಿರಾ?"</string>
-    <!-- no translation found for accessor_info_title (8289823651512477787) -->
-    <skip />
-    <!-- no translation found for accessor_no_description_text (7510967452505591456) -->
-    <skip />
-    <!-- no translation found for accessor_expires_text (4625619273236786252) -->
-    <skip />
-    <!-- no translation found for delete_blob_text (2819192607255625697) -->
-    <skip />
-    <!-- no translation found for delete_blob_confirmation_text (7807446938920827280) -->
-    <skip />
+    <string name="accessor_info_title" msgid="8289823651512477787">"ಹಂಚಿಕೊಂಡ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸುವ ಆ್ಯಪ್‌ಗಳು"</string>
+    <string name="accessor_no_description_text" msgid="7510967452505591456">"ಆ್ಯಪ್‌ನಿಂದ ಯಾವುದೇ ವಿವರಣೆಯನ್ನು ಒದಗಿಸಲಾಗಿಲ್ಲ."</string>
+    <string name="accessor_expires_text" msgid="4625619273236786252">"<xliff:g id="DATE">%s</xliff:g> ರಂದು ಗುತ್ತಿಗೆ ಅವಧಿ ಮುಗಿಯುತ್ತದೆ"</string>
+    <string name="delete_blob_text" msgid="2819192607255625697">"ಹಂಚಿಕೊಳ್ಳಲಾದ ಡೇಟಾವನ್ನು ಅಳಿಸಿ"</string>
+    <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"ಈ ಹಂಚಿಕೊಳ್ಳಲಾದ ಡೇಟಾವನ್ನು ಅಳಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ?"</string>
     <string name="user_add_user_item_summary" msgid="5748424612724703400">"ಬಳಕೆದಾರರು ತಮ್ಮದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವಿಷಯವನ್ನು ಹೊಂದಿದ್ದಾರೆ"</string>
     <string name="user_add_profile_item_summary" msgid="5418602404308968028">"ನಿಮ್ಮ ಖಾತೆಯಿಂದ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವಿಷಯಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ನೀವು ನಿರ್ಬಂಧಿಸಬಹುದು"</string>
     <string name="user_add_user_item_title" msgid="2394272381086965029">"ಬಳಕೆದಾರ"</string>
@@ -556,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"ಪ್ರೊಫೈಲ್‌‌ ಮಾಹಿತಿ"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್‌ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index cd69dd6..6f642c3 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"프로필 정보"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"제한된 프로필을 만들기 전에 화면 잠금을 설정하여 앱과 개인 데이터를 보호해야 합니다."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"잠금 설정"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>(으)로 전환"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"게스트"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index c05fd66..417e511 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Профилдин чоо-жайы"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Чектелген профайл түзөөрдөн мурун, сиз өзүңүздүн колдонмолоруңузду жана жеке маалыматтарыңызды коргош үчүн, бөгөттөө көшөгөсүн орнотушуңуз керек болот."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Бөгөт коюу"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> аккаунтуна которулуу"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Конок"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 4591ca0..94800ea 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -545,12 +545,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profilio informacija"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prieš kuriant apribotą profilį reikės nustatyti ekrano užraktą, kad apsaugotumėte programas ir asmeninius duomenis."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nustatyti užraktą"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Perjungti į <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index c36b7f4..d7816aa 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -544,12 +544,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profila informācija"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Lai varētu izveidot ierobežotu profilu, jums jāiestata ekrāna bloķēšana, kas aizsargās jūsu lietotni un personas datus."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Iestatīt bloķēšanu"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Pārslēgties uz: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index bde3e92..c593bee 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"പ്രൊഫൈൽ വിവരം"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"ഒരു നിയന്ത്രിത പ്രൊഫൈൽ സൃഷ്‌ടിക്കുന്നതിനുമുമ്പ്, നിങ്ങളുടെ അപ്ലിക്കേഷനുകളും വ്യക്തിഗത ഡാറ്റയും പരിരക്ഷിക്കുന്നതിന് ഒരു സ്‌ക്രീൻ ലോക്ക് സജ്ജീകരിക്കേണ്ടതുണ്ട്."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ലോക്ക് സജ്ജീകരിക്കുക"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> എന്നതിലേക്ക് മാറുക"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 38c8b86..d3e41f7 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Профайлын мэдээлэл"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Та хязгаарлагдсан профайл үүсгэхийн өмнө өөрийн апп-ууд болон хувийн өгөгдлийг хамгаалахын тулд дэлгэцийн түгжээг тохируулах шаардлагатай."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Түгжээг тохируулах"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> руу сэлгэх"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 746ef57..c222bd3 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"प्रोफाइल माहिती"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"तुम्ही एक प्रतिबंधित प्रोफाईल तयार करु शकण्यापूर्वी तुम्हाला तुमचे अ‍ॅप्स आणि वैयक्तिक डेटा संरक्षित करण्यासाठी एक स्क्रीन लॉक सेट करण्याची आवश्यकता राहील."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करा"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> वर स्विच करा"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 5e400e3..1dc3dc4 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Maklumat profil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Sebelum anda boleh membuat profil yang terhad, anda perlu menyediakan kunci skrin untuk melindungi apl dan data peribadi anda."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Tetapkan kunci"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Tukar kepada <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index b71f842..58213a7 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profilinformasjon"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan opprette en begrenset profil, må du konfigurere skjermlåsen for å beskytte appene og de personlige dataene dine."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Angi lås"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Bytt til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 79fd469..cc26483 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -431,7 +431,7 @@
     <string name="power_discharge_by" msgid="4113180890060388350">"ब्याट्री लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="92545648425937000">"लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ"</string>
     <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> सम्म"</string>
-    <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"ब्याट्री <xliff:g id="TIME">%1$s</xliff:g> बजेभित्र सकिन सक्छ"</string>
+    <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"ब्याट्री <xliff:g id="TIME">%1$s</xliff:g> बजेसम्ममा सकिन सक्छ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5802195288324091585">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी छ"</string>
     <string name="power_remaining_less_than_duration" msgid="1812668275239801236">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="7919119719242734848">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> भन्दा बढी समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -513,16 +513,12 @@
     <string name="storage_category" msgid="2287342585424631813">"भण्डारण"</string>
     <string name="shared_data_title" msgid="1017034836800864953">"साझा डेटा"</string>
     <string name="shared_data_summary" msgid="5516326713822885652">"साझा डेटा हेर्नुहोस् र परिमार्जन गर्नुहोस्"</string>
-    <!-- no translation found for shared_data_no_blobs_text (3108114670341737434) -->
-    <skip />
-    <!-- no translation found for shared_data_query_failure_text (3489828881998773687) -->
-    <skip />
+    <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"यो प्रयोगकर्तासँग कुनै पनि डेटा सेयर गरिएको छैन।"</string>
+    <string name="shared_data_query_failure_text" msgid="3489828881998773687">"सेयर गरिएको डेटा प्राप्त गर्ने क्रममा कुनै त्रुटि भयो। फेरि प्रयास गर्नुहोस्।"</string>
     <string name="blob_id_text" msgid="8680078988996308061">"साझा डेटाको ID: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
     <string name="blob_expires_text" msgid="7882727111491739331">"<xliff:g id="DATE">%s</xliff:g> मा म्याद सकिन्छ"</string>
-    <!-- no translation found for shared_data_delete_failure_text (3842701391009628947) -->
-    <skip />
-    <!-- no translation found for shared_data_no_accessors_dialog_text (8903738462570715315) -->
-    <skip />
+    <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"सेयर गरिएको डेटा मेट्ने क्रममा त्रुटि भयो।"</string>
+    <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"सेयर गरिएको यो डेटाका लागि कुनै ठेक्का पट्टा लिएको छैन। तपाईं यसलाई मेट्न चाहनुहुन्छ?"</string>
     <string name="accessor_info_title" msgid="8289823651512477787">"साझा डेटा प्रयोग गर्ने अनुप्रयोगहरू"</string>
     <string name="accessor_no_description_text" msgid="7510967452505591456">"यो अनुप्रयोगले कुनै विवरण प्रदान गरेको छैन।"</string>
     <string name="accessor_expires_text" msgid="4625619273236786252">"लिजको म्याद <xliff:g id="DATE">%s</xliff:g> मा सकिन्छ"</string>
@@ -547,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"प्रोफाइल जानकारी"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"निषेधयुक्त प्रोफाइल बनाउनु अघि तपाईँको अनुप्रयोग र व्यक्तिगत डेटा सुरक्षा गर्नाका लागि तपाईँले स्क्रिन लक सेटअप गर्नु पर्दछ ।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"लक सेट गर्नुहोस्"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"प्रयोगकर्ता बदलेर <xliff:g id="USER_NAME">%s</xliff:g> पार्नुहोस्"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index c780d6a..57fc87a 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profielinfo"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Voordat je een beperkt profiel kunt maken, moet je een schermvergrendeling instellen om je apps en persoonsgegevens te beschermen."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Vergrendeling instellen"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Overschakelen naar <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 82cfbe3..3a7f2c5 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -232,8 +232,7 @@
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP ଠିକଣା ଓ ପୋର୍ଟ"</string>
     <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR କୋଡ୍ ସ୍କାନ୍ କରନ୍ତୁ"</string>
     <string name="adb_wireless_qrcode_pairing_description" msgid="8578868049289910131">"ଏକ QR କୋଡ୍ ସ୍କାନ୍ କରି ୱାଇ-ଫାଇରେ ଡିଭାଇସ୍ ପେୟାର୍ କରନ୍ତୁ"</string>
-    <!-- no translation found for adb_wireless_no_network_msg (2365795244718494658) -->
-    <skip />
+    <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"ଦୟାକରି ଏକ ୱାଇ-ଫାଇ ନେଟୱାର୍କରେ ସଂଯୋଗ କରନ୍ତୁ"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, ଡିବଗ୍, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"ବଗ୍ ରିପୋର୍ଟ ସର୍ଟକଟ୍‌"</string>
     <string name="bugreport_in_power_summary" msgid="1885529649381831775">"ବଗ୍ ରିପୋର୍ଟ ଦେବାପାଇଁ ପାୱାର୍‌ ମେନୁରେ ଏକ ବଟନ୍‌ ଦେଖନ୍ତୁ"</string>
@@ -432,8 +431,7 @@
     <string name="power_discharge_by" msgid="4113180890060388350">"ବ୍ୟାଟେରୀ ପାଖାପାଖି <xliff:g id="TIME">%1$s</xliff:g> ଚାଲିବ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="92545648425937000">"ବ୍ୟାଟେରୀ <xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ"</string>
     <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
-    <!-- no translation found for power_suggestion_battery_run_out (6332089307827787087) -->
-    <skip />
+    <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"<xliff:g id="TIME">%1$s</xliff:g> ସୁଦ୍ଧା ବ୍ୟାଟେରୀର ଚାର୍ଜ ଶେଷ ହୋଇ ଯାଇପାରେ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5802195288324091585">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ରୁ କମ୍ ସମୟ ବଳକା ଅଛି"</string>
     <string name="power_remaining_less_than_duration" msgid="1812668275239801236">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ରୁ କମ୍ ସମୟ ବଳକା ଅଛି (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="7919119719242734848">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ରୁ ଅଧିକ ସମୟ ବଳକା ଅଛି(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -511,32 +509,21 @@
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ଫୋନ୍ ସ୍ପିକର୍"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string>
-    <!-- no translation found for help_label (3528360748637781274) -->
-    <skip />
-    <!-- no translation found for storage_category (2287342585424631813) -->
-    <skip />
-    <!-- no translation found for shared_data_title (1017034836800864953) -->
-    <skip />
-    <!-- no translation found for shared_data_summary (5516326713822885652) -->
-    <skip />
+    <string name="help_label" msgid="3528360748637781274">"ସାହାଯ୍ୟ ଓ ମତାମତ"</string>
+    <string name="storage_category" msgid="2287342585424631813">"ଷ୍ଟୋରେଜ୍"</string>
+    <string name="shared_data_title" msgid="1017034836800864953">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା"</string>
+    <string name="shared_data_summary" msgid="5516326713822885652">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ଦେଖନ୍ତୁ ଏବଂ ଏହାକୁ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"ଏହି ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ କୌଣସି ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ନାହିଁ।"</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ଫେଚ୍ କରିବା ସମୟରେ ଏକ ତ୍ରୁଟି ହୋଇଥିଲା। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
-    <!-- no translation found for blob_id_text (8680078988996308061) -->
-    <skip />
-    <!-- no translation found for blob_expires_text (7882727111491739331) -->
-    <skip />
+    <string name="blob_id_text" msgid="8680078988996308061">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ID: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
+    <string name="blob_expires_text" msgid="7882727111491739331">"<xliff:g id="DATE">%s</xliff:g>ରେ ମିଆଦ ଶେଷ ହେଉଛି"</string>
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ଡିଲିଟ୍ କରିବା ସମୟରେ ଏକ ତ୍ରୁଟି ହୋଇଥିଲା।"</string>
     <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"ସେୟାର୍ କରାଯାଇଥିବା ଏହି ଡାଟା ପାଇଁ କୌଣସି ଲିଜ୍ ପ୍ରାପ୍ତ ହୋଇନାହିଁ। ଆପଣ ଏହା ଡିଲିଟ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?"</string>
-    <!-- no translation found for accessor_info_title (8289823651512477787) -->
-    <skip />
-    <!-- no translation found for accessor_no_description_text (7510967452505591456) -->
-    <skip />
-    <!-- no translation found for accessor_expires_text (4625619273236786252) -->
-    <skip />
-    <!-- no translation found for delete_blob_text (2819192607255625697) -->
-    <skip />
-    <!-- no translation found for delete_blob_confirmation_text (7807446938920827280) -->
-    <skip />
+    <string name="accessor_info_title" msgid="8289823651512477787">"ଡାଟା ସେୟାର୍ କରୁଥିବା ଆପଗୁଡ଼ିକ"</string>
+    <string name="accessor_no_description_text" msgid="7510967452505591456">"ଆପ୍ ଦ୍ୱାରା କୌଣସି ବର୍ଣ୍ଣନା ପ୍ରଦାନ କରାଯାଇନାହିଁ।"</string>
+    <string name="accessor_expires_text" msgid="4625619273236786252">"<xliff:g id="DATE">%s</xliff:g>ରେ ଲିଜର ମିଆଦ ଶେଷ ହେଉଛି"</string>
+    <string name="delete_blob_text" msgid="2819192607255625697">"ସେୟାର୍ କରାଯାଇଥିବା ଡାଟା ଡିଲିଟ୍ କରନ୍ତୁ"</string>
+    <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"ଆପଣ ସେୟାର୍ କରାଯାଇଥିବା ଏହି ଡାଟା ଡିଲିଟ୍ କରିବାକୁ ଚାହୁଁଥିବା ନିଶ୍ଚିତ କି?"</string>
     <string name="user_add_user_item_summary" msgid="5748424612724703400">"ଉପଯୋଗକର୍ତ୍ତାମାନଙ୍କ ପାଖରେ ନିଜର ଆପ୍‌ ଓ କଣ୍ଟେଣ୍ଟ ଅଛି"</string>
     <string name="user_add_profile_item_summary" msgid="5418602404308968028">"ନିଜ ଆକାଉଣ୍ଟରୁ ଆପ୍‌ ତଥା କଣ୍ଟେଣ୍ଟକୁ ଆପଣ ଆକ୍ସେସ୍ ରୋକିପାରିବେ"</string>
     <string name="user_add_user_item_title" msgid="2394272381086965029">"ଉପଯୋଗକର୍ତ୍ତା"</string>
@@ -556,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"ପ୍ରୋଫାଇଲ୍ ସୂଚନା"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"ପ୍ରତିବନ୍ଧିତ ପ୍ରୋଫାଇଲ୍‌ ତିଆରି କରିବାବେଳେ, ନିଜ ଆପ୍‌ ଓ ବ୍ୟକ୍ତିଗତ ତଥ୍ୟର ସୁରକ୍ଷା ପାଇଁ ଏକ ସ୍କ୍ରୀନ୍‌ ଲକ୍‌ ସେଟ୍‌ କରନ୍ତୁ।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ଲକ୍‌ ସେଟ୍‌ କରନ୍ତୁ"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>କୁ ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 52ba604..9802257 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"ਪ੍ਰੋਫਾਈਲ ਜਾਣਕਾਰੀ"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ ਤੁਸੀਂ ਇੱਕ ਪ੍ਰਤਿਬੰਧਿਤ ਪ੍ਰੋਫਾਈਲ ਬਣਾ ਸਕੋ, ਤੁਹਾਨੂੰ ਆਪਣੀਆਂ ਐਪਾਂ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਸੁਰੱਖਿਅਤ ਕਰਨ ਲਈ ਇੱਕ ਸਕ੍ਰੀਨ  ਲਾਕ  ਸੈੱਟ ਅੱਪ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">" ਲਾਕ  ਸੈੱਟ ਕਰੋ"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> \'ਤੇ ਜਾਓ"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 602e179..734b257 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -545,12 +545,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Informacje o profilu"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Zanim utworzysz profil z ograniczeniami, musisz skonfigurować ekran blokady, by chronić aplikacje i osobiste dane."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Ustaw blokadę"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Przełącz na: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gość"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 90ce2a9..5860409 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -544,12 +544,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Informații de profil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Înainte de a putea crea un profil cu permisiuni limitate, va trebui să configurați blocarea ecranului pentru a vă proteja aplicațiile și datele personale."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Configurați blocarea"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Comutați la <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 71b231c..ab230b7 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -278,7 +278,7 @@
     <string name="private_dns_mode_off" msgid="7065962499349997041">"Отключено"</string>
     <string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"Автоматический режим"</string>
     <string name="private_dns_mode_provider" msgid="3619040641762557028">"Имя хоста поставщика персонального DNS-сервера"</string>
-    <string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Введите имя хоста поставщика услуг DNS"</string>
+    <string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Введите имя хоста поставщика DNS"</string>
     <string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"Ошибка подключения"</string>
     <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Показывать параметры сертификации беспроводных мониторов"</string>
     <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Вести подробный журнал, показывать RSSI для каждого SSID при выборе сети"</string>
@@ -545,12 +545,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Информация о профиле"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Чтобы создать профиль с ограниченным доступом, необходимо предварительно настроить блокировку экрана для защиты приложений и личных данных"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Включить блокировку"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Переключиться на этот аккаунт: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 0e2cd84..73cfce1 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -545,12 +545,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Informácie o profile"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Pred vytvorením obmedzeného profilu je nutné najprv nastaviť zámku obrazovky na ochranu aplikácií a osobných údajov."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nastaviť uzamknutie"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Prepnúť na používateľa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index f3bba52..8e3782c 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -545,12 +545,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Podatki za profil"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Preden lahko ustvarite profil z omejitvami, morate nastaviti zaklepanje zaslona, da zaščitite aplikacije in osebne podatke."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nastavi zaklepanje"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Preklop na račun <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 2cd2393..6d1e80a 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Inform. i profilit"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para se të mund të krijosh një profil të kufizuar, duhet të konfigurosh një kyçje të ekranit për të mbrojtur aplikacionet dhe të dhënat e tua personale."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Cakto kyçjen"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Kalo te <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 102d925..775b7d5 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -544,12 +544,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Подаци о профилу"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Да бисте могли да направите ограничени профил, треба да подесите закључавање екрана да бисте заштитили апликације и личне податке."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Подеси закључавање"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Пређи на корисника <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 50297a7..e40ba29 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profilinfo"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Innan du skapar en begränsad profil måste du konfigurera ett skärmlås för att skydda dina appar och personliga data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurera lås"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Byt till <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index bfa1208..815896d 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Maelezo ya wasifu"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Kabla uunde wasifu uliowekekwa vikwazo, utahitajika kuweka skrini iliyofungwa ili kulinda programu zako na data binafsi."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Weka ufunguo"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Badili utumie <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 98bb0ca..5080c31 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -418,8 +418,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"நிறம் அடையாளங்காண முடியாமை (சிவப்பு-பச்சை)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"நிறம் அடையாளங்காண முடியாமை (நீலம்-மஞ்சள்)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"வண்ணத்திருத்தம்"</string>
-    <!-- no translation found for accessibility_display_daltonizer_preference_subtitle (1284746051652993443) -->
-    <skip />
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"வண்ணத் திருத்தத்தைப் பயன்படுத்தி உங்கள் சாதனத்தில் வண்ணம் காண்பிக்கப்படும் விதத்தைச் சரிசெய்யலாம்"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a36b6a3..0e763dd 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -232,8 +232,7 @@
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP చిరునామా &amp; పోర్ట్"</string>
     <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR కోడ్‌ను స్కాన్ చేయండి"</string>
     <string name="adb_wireless_qrcode_pairing_description" msgid="8578868049289910131">"పరికరాన్ని Wi-Fi ద్వారా పెయిర్ చేయడానికి QR కోడ్‌ను స్కాన్ చేయండి"</string>
-    <!-- no translation found for adb_wireless_no_network_msg (2365795244718494658) -->
-    <skip />
+    <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"దయచేసి Wi-Fi నెట్‌వర్క్‌కు కనెక్ట్ చేయండి"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, డీబగ్, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"బగ్ నివేదిక షార్ట్‌కట్"</string>
     <string name="bugreport_in_power_summary" msgid="1885529649381831775">"బగ్ నివేదికను తీసుకోవడానికి పవర్ మెనూలో బటన్‌ను చూపు"</string>
@@ -418,7 +417,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ప్రొటానోమలీ (ఎరుపు-ఆకుపచ్చ రంగు)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"ట్రైటనోమలీ (నీలం-పసుపు రంగు)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"రంగు సవరణ"</string>
-    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"రంగుల సరి చేసే ఫీచర్‌తో మీరు మీ పరికరంలో రంగులో కనిపించే పద్ధతిని సర్దుబాటు చేయగలుగుతారు"</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"రంగులు సరి చేసే ఫీచర్ సాయంతో, మీ పరికరంలో రంగులు కనిపించే పద్ధతిని మీరు సర్దుబాటు చేయగలుగుతారు"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
@@ -432,8 +431,7 @@
     <string name="power_discharge_by" msgid="4113180890060388350">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="92545648425937000">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి"</string>
     <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> వరకు"</string>
-    <!-- no translation found for power_suggestion_battery_run_out (6332089307827787087) -->
-    <skip />
+    <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"బ్యాటరీ <xliff:g id="TIME">%1$s</xliff:g> సమయానికి ఖాళీ అవ్వచ్చు"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5802195288324091585">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది"</string>
     <string name="power_remaining_less_than_duration" msgid="1812668275239801236">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="7919119719242734848">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> కంటే ఎక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -511,32 +509,21 @@
     <string name="media_transfer_this_device_name" msgid="2716555073132169240">"ఫోన్ స్పీకర్"</string>
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string>
-    <!-- no translation found for help_label (3528360748637781274) -->
-    <skip />
-    <!-- no translation found for storage_category (2287342585424631813) -->
-    <skip />
-    <!-- no translation found for shared_data_title (1017034836800864953) -->
-    <skip />
-    <!-- no translation found for shared_data_summary (5516326713822885652) -->
-    <skip />
+    <string name="help_label" msgid="3528360748637781274">"సహాయం &amp; అభిప్రాయం"</string>
+    <string name="storage_category" msgid="2287342585424631813">"నిల్వ"</string>
+    <string name="shared_data_title" msgid="1017034836800864953">"షేర్ చేసిన డేటా"</string>
+    <string name="shared_data_summary" msgid="5516326713822885652">"షేర్ చేసిన డేటాను చూసి, సవరించండి"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"ఈ యూజర్ కోసం షేర్ చేసిన డేటా ఏదీ లేదు."</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"షేర్ చేసిన డేటా పొందడంలో ఎర్రర్ ఏర్పడింది. మళ్లీ ట్రై చేయండి."</string>
-    <!-- no translation found for blob_id_text (8680078988996308061) -->
-    <skip />
-    <!-- no translation found for blob_expires_text (7882727111491739331) -->
-    <skip />
+    <string name="blob_id_text" msgid="8680078988996308061">"షేర్ చేసిన డేటా ID: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
+    <string name="blob_expires_text" msgid="7882727111491739331">"<xliff:g id="DATE">%s</xliff:g>న గడువు ముగుస్తుంది"</string>
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"షేర్ చేసిన డేటాను తొలగించడంలో ఎర్రర్ ఏర్పడింది."</string>
     <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"ఈ షేర్ చేసిన డేటాకు సేకరించబడిన లీజులు ఏవీ లేవు. దీన్ని మీరు తొలగించాలనుకుంటున్నారా?"</string>
-    <!-- no translation found for accessor_info_title (8289823651512477787) -->
-    <skip />
-    <!-- no translation found for accessor_no_description_text (7510967452505591456) -->
-    <skip />
-    <!-- no translation found for accessor_expires_text (4625619273236786252) -->
-    <skip />
-    <!-- no translation found for delete_blob_text (2819192607255625697) -->
-    <skip />
-    <!-- no translation found for delete_blob_confirmation_text (7807446938920827280) -->
-    <skip />
+    <string name="accessor_info_title" msgid="8289823651512477787">"యాప్‌ల షేరింగ్ డేటా"</string>
+    <string name="accessor_no_description_text" msgid="7510967452505591456">"యాప్ ద్వారా ఎలాంటి వివరణ అందించబడలేదు."</string>
+    <string name="accessor_expires_text" msgid="4625619273236786252">"లీజు గడువు <xliff:g id="DATE">%s</xliff:g>తో ముగుస్తుంది"</string>
+    <string name="delete_blob_text" msgid="2819192607255625697">"షేర్ చేసిన డేటాను తొలగించు"</string>
+    <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"మీరు ఖచ్చితంగా ఈ షేర్ చేసిన డేటాను తొలగించాలనుకుంటున్నారా?"</string>
     <string name="user_add_user_item_summary" msgid="5748424612724703400">"వినియోగదారులు వారి స్వంత అనువర్తనాలను మరియు కంటెంట్‌ను కలిగి ఉన్నారు"</string>
     <string name="user_add_profile_item_summary" msgid="5418602404308968028">"మీరు మీ ఖాతా నుండి అనువర్తనాలకు మరియు కంటెంట్‌కు ప్రాప్యతను పరిమితం చేయవచ్చు"</string>
     <string name="user_add_user_item_title" msgid="2394272381086965029">"వినియోగదారు"</string>
@@ -556,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"ప్రొఫైల్ సమాచారం"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"మీరు పరిమితం చేయబడిన ప్రొఫైల్‌ను సృష్టించడానికి ముందు, మీ అనువర్తనాలు మరియు వ్యక్తిగత డేటాను రక్షించడానికి స్క్రీన్ లాక్‌ను సెటప్ చేయాల్సి ఉంటుంది."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"లాక్‌ను సెట్ చేయి"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>కు మార్చు"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 765c5dd..b24f006d 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -513,16 +513,12 @@
     <string name="storage_category" msgid="2287342585424631813">"พื้นที่เก็บข้อมูล"</string>
     <string name="shared_data_title" msgid="1017034836800864953">"ข้อมูลที่แชร์"</string>
     <string name="shared_data_summary" msgid="5516326713822885652">"ดูและแก้ไขข้อมูลที่แชร์"</string>
-    <!-- no translation found for shared_data_no_blobs_text (3108114670341737434) -->
-    <skip />
-    <!-- no translation found for shared_data_query_failure_text (3489828881998773687) -->
-    <skip />
+    <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"ไม่มีข้อมูลที่แชร์สำหรับผู้ใช้รายนี้"</string>
+    <string name="shared_data_query_failure_text" msgid="3489828881998773687">"เกิดข้อผิดพลาดขณะดึงข้อมูลที่แชร์ ลองใหม่"</string>
     <string name="blob_id_text" msgid="8680078988996308061">"รหัสข้อมูลที่แชร์: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
     <string name="blob_expires_text" msgid="7882727111491739331">"จะหมดอายุในวันที่ <xliff:g id="DATE">%s</xliff:g>"</string>
-    <!-- no translation found for shared_data_delete_failure_text (3842701391009628947) -->
-    <skip />
-    <!-- no translation found for shared_data_no_accessors_dialog_text (8903738462570715315) -->
-    <skip />
+    <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"เกิดข้อผิดพลาดขณะลบข้อมูลที่แชร์"</string>
+    <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"ไม่มีสัญญาเช่าที่ได้มาสำหรับข้อมูลที่แชร์นี้ คุณต้องการลบข้อมูลนี้ไหม"</string>
     <string name="accessor_info_title" msgid="8289823651512477787">"แอปที่แชร์ข้อมูล"</string>
     <string name="accessor_no_description_text" msgid="7510967452505591456">"แอปไม่ได้ให้คำอธิบายไว้"</string>
     <string name="accessor_expires_text" msgid="4625619273236786252">"เวลาได้รับสิทธิ์จะสิ้นสุดในวันที่ <xliff:g id="DATE">%s</xliff:g>"</string>
@@ -547,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"ข้อมูลโปรไฟล์"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"ก่อนที่คุณจะสามารถสร้างโปรไฟล์ที่ถูกจำกัดได้ คุณจะต้องตั้งค่าล็อกหน้าจอเพื่อปกป้องแอปและข้อมูลส่วนตัวของคุณ"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ตั้งค่าล็อก"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"เปลี่ยนเป็น <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้เข้าร่วม"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้เข้าร่วมออก"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"ผู้เข้าร่วม"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 1146619..8d70718 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Info sa profile"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Bago ka makakalikha ng pinaghihigpitang profile, kakailanganin mong mag-set up ng screen lock upang protektahan ang iyong apps at personal na data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Itakda ang lock"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Lumipat sa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 3853331..3579957 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profil bilgisi"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Kısıtlanmış bir profil oluşturabilmeniz için uygulamalarınızı ve kişisel verilerinizi korumak üzere bir ekran kilidi oluşturmanız gerekir."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Kilidi ayarla"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> hesabına geç"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 2eeda18..5dae4f0 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -545,12 +545,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Інформація профілю"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Перш ніж створювати обмежений профіль, потрібно налаштувати блокування екрана, щоб захистити свої програми та особисті дані."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Налаштувати блокування"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Перейти до користувача <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Гість"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index f802ebc..ce25f2f 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -300,8 +300,8 @@
     <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"اگر دستیاب ہو تو ٹیدرنگ ہارڈویئر سرعت کاری کا استعمال کریں"</string>
     <string name="adb_warning_title" msgid="7708653449506485728">"‏USB ڈیبگ کرنے کی اجازت دیں؟"</string>
     <string name="adb_warning_message" msgid="8145270656419669221">"‏USB ڈیبگ کرنا صرف ڈیولپمنٹ کے مقاصد کیلئے ہے۔ اپنے کمپیوٹر اور اپنے آلہ کے درمیان ڈیٹا کاپی کرنے کیلئے اسے استعمال کریں، بغیر اطلاع کے اپنے آلہ پر ایپس انسٹال کریں اور لاگ ڈیٹا پڑھیں۔"</string>
-    <string name="adbwifi_warning_title" msgid="727104571653031865">"وائرلیس ڈیبگ کرنے کی اجازت دیں؟"</string>
-    <string name="adbwifi_warning_message" msgid="8005936574322702388">"وائرلیس ڈیبگ کرنا صرف ڈیولپمنٹ کے مقاصد کے لیے ہے۔ اپنے کمپیوٹر اور اپنے آلہ کے درمیان ڈیٹا کاپی کرنے کے لیے اسے استعمال کریں، بغیر اطلاع کے اپنے آلہ پر ایپس انسٹال کریں اور لاگ ڈیٹا پڑھیں۔"</string>
+    <string name="adbwifi_warning_title" msgid="727104571653031865">"وائرلیس ڈیبگنگ کی اجازت دیں؟"</string>
+    <string name="adbwifi_warning_message" msgid="8005936574322702388">"وائرلیس ڈیبگنگ صرف ڈیولپمنٹ کے مقاصد کے لیے ہے۔ اپنے کمپیوٹر اور اپنے آلہ کے درمیان ڈیٹا کاپی کرنے کے لیے اسے استعمال کریں، بغیر اطلاع کے اپنے آلہ پر ایپس انسٹال کریں اور لاگ ڈیٹا پڑھیں۔"</string>
     <string name="adb_keys_warning_message" msgid="2968555274488101220">"‏اپنے ذریعہ پہلے سے اجازت یافتہ سبھی کمپیوٹرز سے USB ڈیبگ کرنے کی رسائی کو کالعدم کریں؟"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"ڈویلپمنٹ ترتیبات کی اجازت دیں؟"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"یہ ترتیبات صرف ڈویلپمنٹ استعمال کے ارادے سے ہیں۔ ان سے آپ کا آلہ اور اس پر موجود ایپلیکیشنز بریک ہو سکتی یا غلط برتاؤ کر سکتی ہیں۔"</string>
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"پروفائل کی معلومات"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"ایک محدود پروفائل بنانے سے پہلے، آپ کو اپنی ایپس اور ذاتی ڈیٹا کو محفوظ کرنے کیلئے ایک اسکرین لاک سیٹ اپ کرنا ہوگا۔"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"لاک سیٹ کریں"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> پر سوئچ کریں"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 52fe395..07f3a6e 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -520,8 +520,8 @@
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"Umumiy maʼlumotlarni oʻchirishda xatolik yuz berdi."</string>
     <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"Bu umumiy maʼlumotlar yuzasidan kelgan soʻrov topilmadi. Oʻchirib tashlansinmi?"</string>
     <string name="accessor_info_title" msgid="8289823651512477787">"Umumiy maʼlumotlar bor ilovalar"</string>
-    <string name="accessor_no_description_text" msgid="7510967452505591456">"Ilova hech qanday tavsif bermagan."</string>
-    <string name="accessor_expires_text" msgid="4625619273236786252">"Ruxsat eskirish sanasi: <xliff:g id="DATE">%s</xliff:g>"</string>
+    <string name="accessor_no_description_text" msgid="7510967452505591456">"Ilovaga hech qanday tavsif bermagan."</string>
+    <string name="accessor_expires_text" msgid="4625619273236786252">"Ruxsat tugash sanasi: <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="delete_blob_text" msgid="2819192607255625697">"Umumiy maʼlumotlarni oʻchirish"</string>
     <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"Umumiy maʼlumotlarni oʻchirishni xohlaysizmi?"</string>
     <string name="user_add_user_item_summary" msgid="5748424612724703400">"Foydalanuvchilar o‘zlarining ilovalari va kontenlariga egalar"</string>
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profil haqida axborot"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Cheklangan profil yaratish uchun, shaxsiy ilovlar va ma‘lumotlarni himoyalash maqsadida avval ekran qulfini yaratish lozim."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Qulf o‘rnatish"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Bunga almashish: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini olib tashlash"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index eb22f7d..10ac60d 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -206,11 +206,11 @@
     <string name="enable_adb" msgid="8072776357237289039">"Gỡ lỗi qua USB"</string>
     <string name="enable_adb_summary" msgid="3711526030096574316">"Bật chế độ gỡ lỗi khi kết nối USB"</string>
     <string name="clear_adb_keys" msgid="3010148733140369917">"Thu hồi ủy quyền gỡ lỗi USB"</string>
-    <string name="enable_adb_wireless" msgid="6973226350963971018">"Gỡ lỗi không dây"</string>
+    <string name="enable_adb_wireless" msgid="6973226350963971018">"Gỡ lỗi qua Wi-Fi"</string>
     <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Chế độ gỡ lỗi khi có kết nối Wi-Fi"</string>
     <string name="adb_wireless_error" msgid="721958772149779856">"Lỗi"</string>
-    <string name="adb_wireless_settings" msgid="2295017847215680229">"Gỡ lỗi không dây"</string>
-    <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Để xem và sử dụng các thiết bị có sẵn, hãy bật tính năng gỡ lỗi không dây"</string>
+    <string name="adb_wireless_settings" msgid="2295017847215680229">"Gỡ lỗi qua Wi-Fi"</string>
+    <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Để xem và sử dụng các thiết bị có sẵn, hãy bật tính năng gỡ lỗi qua Wi-Fi"</string>
     <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Ghép nối thiết bị bằng mã QR"</string>
     <string name="adb_pair_method_qrcode_summary" msgid="3729901496856458634">"Ghép nối các thiết bị mới bằng Trình quét mã QR"</string>
     <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Ghép nối thiết bị bằng mã ghép nối"</string>
@@ -300,8 +300,8 @@
     <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Sử dụng tính năng tăng tốc phần cứng khi chia sẻ kết nối nếu có"</string>
     <string name="adb_warning_title" msgid="7708653449506485728">"Cho phép gỡ lỗi qua USB?"</string>
     <string name="adb_warning_message" msgid="8145270656419669221">"Gỡ lỗi USB chỉ dành cho mục đích phát triển. Hãy sử dụng tính năng này để sao chép dữ liệu giữa máy tính và thiết bị của bạn, cài đặt ứng dụng trên thiết bị của bạn mà không thông báo và đọc dữ liệu nhật ký."</string>
-    <string name="adbwifi_warning_title" msgid="727104571653031865">"Bật tính năng gỡ lỗi không dây?"</string>
-    <string name="adbwifi_warning_message" msgid="8005936574322702388">"Tính năng gỡ lỗi không dây chỉ dành cho mục đích phát triển. Hãy sử dụng tính năng này để sao chép dữ liệu giữa máy tính và thiết bị của bạn, cài đặt ứng dụng trên thiết bị mà không thông báo và đọc dữ liệu nhật ký."</string>
+    <string name="adbwifi_warning_title" msgid="727104571653031865">"Bật tính năng gỡ lỗi qua Wi-Fi?"</string>
+    <string name="adbwifi_warning_message" msgid="8005936574322702388">"Tính năng gỡ lỗi qua Wi-Fi chỉ dành cho mục đích phát triển. Hãy sử dụng tính năng này để sao chép dữ liệu giữa máy tính và thiết bị của bạn, cài đặt ứng dụng trên thiết bị mà không thông báo và đọc dữ liệu nhật ký."</string>
     <string name="adb_keys_warning_message" msgid="2968555274488101220">"Thu hồi quyền truy cập gỡ lỗi USB từ tất cả máy tính mà bạn đã ủy quyền trước đó?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"Cho phép cài đặt phát triển?"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"Những cài đặt này chỉ dành cho mục đích phát triển. Chúng có thể làm cho thiết bị và ứng dụng trên thiết bị của bạn bị lỗi và hoạt động sai."</string>
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Thông tin hồ sơ"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Trước khi bạn có thể tạo tiểu sử bị hạn chế, bạn sẽ cần thiết lập một màn hình khóa để bảo vệ các ứng dụng và dữ liệu cá nhân của bạn."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Thiết lập khóa"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Chuyển sang <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"Khách"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index a4727ca..c24f72d 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"个人资料信息"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"您需要先设置锁定屏幕来保护您的应用和个人数据,然后才可以创建受限个人资料。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"设置屏幕锁定方式"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"切换到<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"访客"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index b3f28fa..a4c2336 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"個人檔案資料"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"建立限制存取的個人檔案前,您必須先設定上鎖畫面來保護您的應用程式和個人資料。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"設定上鎖畫面"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 05d4d46..5183df3 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -543,12 +543,8 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"設定檔資訊"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"如要建立設有限制的個人資料,你必須先設定螢幕鎖定來保護你的應用程式和個人資料。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"設定鎖定"</string>
-    <!-- no translation found for user_switch_to_user (6975428297154968543) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (3482026122932643557) -->
-    <skip />
-    <!-- no translation found for guest_exit_guest (5908239569510734136) -->
-    <skip />
-    <!-- no translation found for guest_nickname (6332276931583337261) -->
-    <skip />
+    <string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
+    <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9112602..53e7921 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -566,7 +566,7 @@
     <!-- [CHAR LIMIT=50] Title for adb wireless pair by QR code preference -->
     <string name="adb_pair_method_qrcode_title">Pair device with QR code</string>
     <!-- [CHAR LIMIT=NONE] Summary for adb wireless pair by QR code preference -->
-    <string name="adb_pair_method_qrcode_summary">Pair new devices using QR code Scanner</string>
+    <string name="adb_pair_method_qrcode_summary">Pair new devices using QR code scanner</string>
     <!-- [CHAR LIMIT=50] Title for adb wireless pair by pairing code preference -->
     <string name="adb_pair_method_code_title">Pair device with pairing code</string>
     <!-- [CHAR LIMIT=NONE] Summary for adb wireless pair by pairing code preference -->
@@ -604,7 +604,7 @@
     <!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing scanner title -->
     <string name="adb_wireless_qrcode_pairing_title">Scan QR code</string>
     <!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing description -->
-    <string name="adb_wireless_qrcode_pairing_description">Pair device over Wi\u2011Fi by scanning a QR Code</string>
+    <string name="adb_wireless_qrcode_pairing_description">Pair device over Wi\u2011Fi by scanning a QR code</string>
     <!-- [CHAR LIMIT=NONE] Toast message when trying to enable Wi-Fi debugging and no Wi-Fi network connected -->
     <string name="adb_wireless_no_network_msg">Please connect to a Wi\u2011Fi network</string>
     <!--Adb wireless search Keywords [CHAR LIMIT=NONE]-->
@@ -928,11 +928,15 @@
     <!-- UI debug setting: show all ANRs summary [CHAR LIMIT=100] -->
     <string name="show_all_anrs_summary">Display App Not Responding dialog for background apps</string>
 
-    <!-- UI debug setting: show all ANRs? [CHAR LIMIT=25] -->
+    <!-- UI debug setting: show missing channel toasts? [CHAR LIMIT=25] -->
     <string name="show_notification_channel_warnings">Show notification channel warnings</string>
-    <!-- UI debug setting: show all ANRs summary [CHAR LIMIT=50] -->
+    <!-- UI debug setting: show missing channel toasts summary [CHAR LIMIT=50] -->
     <string name="show_notification_channel_warnings_summary">Displays on-screen warning when an app posts a notification without a valid channel</string>
 
+    <!-- UI debug setting: enforce shortcut requirements for conversation space [CHAR LIMIT=25] -->
+    <string name="enforce_shortcuts_for_conversations">Enforce shortcuts for conversation notifications</string>
+    <!-- UI debug setting: enforce shortcut requirements for conversation space summary [CHAR LIMIT=50] -->
+    <string name="enforce_shortcuts_for_conversations_summary">Require notifications to be backed by a long-lived sharing shortcut in order to appear in the conversation section</string>
 
     <!-- UI debug setting: force allow apps on external storage [CHAR LIMIT=50] -->
     <string name="force_allow_on_external">Force allow apps on external</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index fb8a0b7..3024b84 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -146,6 +146,7 @@
                     ((BluetoothMediaDevice) device).getCachedDevice();
             if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
                 mOnTransferBluetoothDevice = connectDevice;
+                device.setState(MediaDeviceState.STATE_CONNECTING);
                 cachedDevice.connect();
                 return;
             }
@@ -220,7 +221,7 @@
      */
     public MediaDevice getMediaDeviceById(List<MediaDevice> devices, String id) {
         for (MediaDevice mediaDevice : devices) {
-            if (mediaDevice.getId().equals(id)) {
+            if (TextUtils.equals(mediaDevice.getId(), id)) {
                 return mediaDevice;
             }
         }
@@ -236,7 +237,7 @@
      */
     public MediaDevice getMediaDeviceById(String id) {
         for (MediaDevice mediaDevice : mMediaDevices) {
-            if (mediaDevice.getId().equals(id)) {
+            if (TextUtils.equals(mediaDevice.getId(), id)) {
                 return mediaDevice;
             }
         }
@@ -394,6 +395,7 @@
             dispatchDeviceListUpdate();
             if (mOnTransferBluetoothDevice != null && mOnTransferBluetoothDevice.isConnected()) {
                 connectDevice(mOnTransferBluetoothDevice);
+                mOnTransferBluetoothDevice.setState(MediaDeviceState.STATE_CONNECTED);
                 mOnTransferBluetoothDevice = null;
             }
         }
@@ -539,6 +541,14 @@
 
         @Override
         public void onDeviceAttributesChanged() {
+            if (mOnTransferBluetoothDevice != null
+                    && !((BluetoothMediaDevice) mOnTransferBluetoothDevice).getCachedDevice()
+                    .isBusy()
+                    && !mOnTransferBluetoothDevice.isConnected()) {
+                // Failed to connect
+                mOnTransferBluetoothDevice.setState(MediaDeviceState.STATE_DISCONNECTED);
+                mOnTransferBluetoothDevice = null;
+            }
             dispatchDeviceAttributesChanged();
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
index 19e3808..65c7786 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiSavedConfigUtils.java
@@ -65,5 +65,17 @@
         }
         return savedConfigs;
     }
+
+    /**
+     * Returns the count of the saved configurations on the device, including both Wi-Fi networks
+     * and Passpoint profiles.
+     *
+     * @param context The application context
+     * @param wifiManager An instance of {@link WifiManager}
+     * @return count of saved Wi-Fi networks
+     */
+    public static int getAllConfigsCount(Context context, WifiManager wifiManager) {
+        return getAllConfigs(context, wifiManager).size();
+    }
 }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 1c69072..7ddd64c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -218,6 +218,26 @@
     }
 
     @Test
+    public void getMediaDeviceById_idIsNull_shouldReturnNull() {
+        final MediaDevice device1 = mock(MediaDevice.class);
+        final MediaDevice device2 = mock(MediaDevice.class);
+        mLocalMediaManager.mMediaDevices.add(device1);
+        mLocalMediaManager.mMediaDevices.add(device2);
+
+        when(device1.getId()).thenReturn(null);
+        when(device2.getId()).thenReturn(null);
+
+        MediaDevice device = mLocalMediaManager
+                .getMediaDeviceById(mLocalMediaManager.mMediaDevices, TEST_CURRENT_DEVICE_ID);
+
+        assertThat(device).isNull();
+
+        device = mLocalMediaManager.getMediaDeviceById(TEST_CURRENT_DEVICE_ID);
+
+        assertThat(device).isNull();
+    }
+
+    @Test
     public void onDeviceAdded_addDevice() {
         final MediaDevice device = mock(MediaDevice.class);
 
@@ -464,6 +484,26 @@
     }
 
     @Test
+    public void onDeviceAttributesChanged_failingTransferring_shouldResetState() {
+        final MediaDevice currentDevice = mock(MediaDevice.class);
+        final MediaDevice device = mock(BluetoothMediaDevice.class);
+        final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+        mLocalMediaManager.mMediaDevices.add(device);
+        mLocalMediaManager.mMediaDevices.add(currentDevice);
+        when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
+        when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
+        when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice);
+        when(cachedDevice.isConnected()).thenReturn(false);
+        when(cachedDevice.isBusy()).thenReturn(false);
+
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.connectDevice(device);
+
+        mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged();
+        verify(device).setState(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+    }
+
+    @Test
     public void onRequestFailed_checkDevicesState() {
         mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
         mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 86ba8bb..01a2b69 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -418,6 +418,7 @@
                     Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS,
                     Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT,
                     Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT,
+                    Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS,
                     Settings.Global.SAFE_BOOT_DISALLOWED,
                     Settings.Global.SELINUX_STATUS,
                     Settings.Global.SELINUX_UPDATE_CONTENT_URL,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 68bd407..c2015ed 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -363,6 +363,7 @@
         public void onError(@BugreportErrorCode int errorCode) {
             synchronized (mLock) {
                 stopProgressLocked(mInfo.id);
+                mInfo.deleteEmptyFiles();
             }
             Log.e(TAG, "Bugreport API callback onError() errorCode = " + errorCode);
             return;
@@ -1980,6 +1981,22 @@
         }
 
         /**
+         * Deletes empty files for a given bugreport.
+         */
+        private void deleteEmptyFiles() {
+            if (bugreportFile.length() == 0) {
+                Log.i(TAG, "Deleting empty bugreport file: " + bugreportFile);
+                bugreportFile.delete();
+            }
+            for (File file : screenshotFiles) {
+                if (file.length() == 0) {
+                    Log.i(TAG, "Deleting empty screenshot file: " + file);
+                    file.delete();
+                }
+            }
+        }
+
+        /**
          * Rename all screenshots files so that they contain the new {@code name} instead of the
          * {@code initialName} if user has changed it.
          */
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 791b832..c6f0327 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -255,6 +255,9 @@
     <!-- Query all packages on device on R+ -->
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
+    <!-- Permission to register process observer -->
+    <uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER"/>
+
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml b/packages/SystemUI/res/color/kg_user_switcher_rounded_background_color.xml
similarity index 87%
copy from packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
copy to packages/SystemUI/res/color/kg_user_switcher_rounded_background_color.xml
index 344859c..b16d038 100644
--- a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
+++ b/packages/SystemUI/res/color/kg_user_switcher_rounded_background_color.xml
@@ -17,6 +17,6 @@
   -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_activated="true" android:color="?android:attr/colorAccent" />
+    <item android:state_activated="true" android:color="@color/kg_user_switcher_activated_background_color" />
     <item android:color="@android:color/transparent" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml b/packages/SystemUI/res/color/qs_user_avatar_frame.xml
similarity index 92%
rename from packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
rename to packages/SystemUI/res/color/qs_user_avatar_frame.xml
index 344859c..0c816b4 100644
--- a/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
+++ b/packages/SystemUI/res/color/qs_user_avatar_frame.xml
@@ -18,5 +18,5 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_activated="true" android:color="?android:attr/colorAccent" />
-    <item android:color="@android:color/transparent" />
+    <item android:color="@color/qs_user_switcher_avatar_background" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bg_avatar_selected.xml b/packages/SystemUI/res/drawable/bg_avatar_selected.xml
new file mode 100644
index 0000000..84d3416
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bg_avatar_selected.xml
@@ -0,0 +1,28 @@
+<?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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="100"
+        android:viewportHeight="100">
+
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M50,50m-50,0a50,50 0,1 1,100 0a50,50 0,1 1,-100 0"/>
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_add_circle.xml b/packages/SystemUI/res/drawable/ic_add_circle.xml
new file mode 100644
index 0000000..79ea707
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_add_circle.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48"
+        android:viewportHeight="48">
+
+    <path
+        android:pathData="M23.9998,10.667C16.6398,10.667 10.6665,16.6403 10.6665,24.0003C10.6665,31.3603 16.6398,37.3337 23.9998,37.3337C31.3598,37.3337 37.3332,31.3603 37.3332,24.0003C37.3332,16.6403 31.3598,10.667 23.9998,10.667ZM22.6665,17.3337V22.667H17.3332V25.3337H22.6665V30.667H25.3332V25.3337H30.6665V22.667H25.3332V17.3337H22.6665ZM13.3332,24.0003C13.3332,29.8803 18.1198,34.667 23.9998,34.667C29.8798,34.667 34.6665,29.8803 34.6665,24.0003C34.6665,18.1203 29.8798,13.3337 23.9998,13.3337C18.1198,13.3337 13.3332,18.1203 13.3332,24.0003Z"
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"/>
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_add_circle_qs.xml b/packages/SystemUI/res/drawable/ic_add_circle_qs.xml
deleted file mode 100644
index 8e93344..0000000
--- a/packages/SystemUI/res/drawable/ic_add_circle_qs.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48.0dp"
-        android:height="48.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/colorForeground">
-    <group
-            android:scaleX="1.2"
-            android:scaleY="1.2"
-            android:pivotX="12.0"
-            android:pivotY="12.0">
-        <path
-            android:fillColor="#FFFFFFFF"
-            android:pathData="M12.000000,2.000000C6.500000,2.000000 2.000000,6.500000 2.000000,12.000000s4.500000,10.000000 10.000000,10.000000c5.500000,0.000000 10.000000,-4.500000 10.000000,-10.000000S17.500000,2.000000 12.000000,2.000000zM17.000000,13.000000l-4.000000,0.000000l0.000000,4.000000l-2.000000,0.000000l0.000000,-4.000000L7.000000,13.000000l0.000000,-2.000000l4.000000,0.000000L11.000000,7.000000l2.000000,0.000000l0.000000,4.000000l4.000000,0.000000L17.000000,13.000000z"/>
-    </group>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_avatar_guest_user.xml b/packages/SystemUI/res/drawable/ic_avatar_guest_user.xml
new file mode 100644
index 0000000..e3a83a2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_avatar_guest_user.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48"
+        android:viewportHeight="48">
+
+    <path
+        android:pathData="M23.9998,10.667C16.6398,10.667 10.6665,16.6403 10.6665,24.0003C10.6665,31.3603 16.6398,37.3337 23.9998,37.3337C31.3598,37.3337 37.3332,31.3603 37.3332,24.0003C37.3332,16.6403 31.3598,10.667 23.9998,10.667ZM17.4265,32.3737C17.9998,31.1737 21.4932,30.0003 23.9998,30.0003C26.5065,30.0003 30.0132,31.1737 30.5732,32.3737C28.7598,33.8137 26.4798,34.667 23.9998,34.667C21.5198,34.667 19.2398,33.8137 17.4265,32.3737ZM23.9998,27.3337C25.9465,27.3337 30.5732,28.1203 32.4798,30.4403C33.8398,28.6537 34.6665,26.427 34.6665,24.0003C34.6665,18.1203 29.8798,13.3337 23.9998,13.3337C18.1198,13.3337 13.3332,18.1203 13.3332,24.0003C13.3332,26.427 14.1598,28.6537 15.5198,30.4403C17.4265,28.1203 22.0532,27.3337 23.9998,27.3337ZM23.9998,16.0003C21.4132,16.0003 19.3332,18.0803 19.3332,20.667C19.3332,23.2537 21.4132,25.3337 23.9998,25.3337C26.5865,25.3337 28.6665,23.2537 28.6665,20.667C28.6665,18.0803 26.5865,16.0003 23.9998,16.0003ZM21.9998,20.667C21.9998,21.7737 22.8932,22.667 23.9998,22.667C25.1065,22.667 25.9998,21.7737 25.9998,20.667C25.9998,19.5603 25.1065,18.667 23.9998,18.667C22.8932,18.667 21.9998,19.5603 21.9998,20.667Z"
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"/>
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_avatar_user.xml b/packages/SystemUI/res/drawable/ic_avatar_user.xml
new file mode 100644
index 0000000..b4a4a88
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_avatar_user.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48"
+        android:viewportHeight="48">
+
+    <path
+        android:pathData="M23.9998,10.667C16.6398,10.667 10.6665,16.6403 10.6665,24.0003C10.6665,31.3603 16.6398,37.3337 23.9998,37.3337C31.3598,37.3337 37.3332,31.3603 37.3332,24.0003C37.3332,16.6403 31.3598,10.667 23.9998,10.667ZM32.4798,30.4403C30.5732,28.1203 25.9465,27.3337 23.9998,27.3337C22.0532,27.3337 17.4265,28.1203 15.5198,30.4403C14.1598,28.6537 13.3332,26.427 13.3332,24.0003C13.3332,18.1203 18.1198,13.3337 23.9998,13.3337C29.8798,13.3337 34.6665,18.1203 34.6665,24.0003C34.6665,26.427 33.8398,28.6537 32.4798,30.4403ZM19.3332,20.667C19.3332,18.0803 21.4132,16.0003 23.9998,16.0003C26.5865,16.0003 28.6665,18.0803 28.6665,20.667C28.6665,23.2537 26.5865,25.3337 23.9998,25.3337C21.4132,25.3337 19.3332,23.2537 19.3332,20.667Z"
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"/>
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml b/packages/SystemUI/res/drawable/kg_user_switcher_rounded_bg.xml
similarity index 69%
rename from packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml
rename to packages/SystemUI/res/drawable/kg_user_switcher_rounded_bg.xml
index e3d010e..13c5fe0 100644
--- a/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml
+++ b/packages/SystemUI/res/drawable/kg_user_switcher_rounded_bg.xml
@@ -16,17 +16,17 @@
   -->
 <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" />
+    <item>
+        <shape>
+            <solid android:color="@color/kg_user_switcher_rounded_background_color" />
 
-         <padding
-                 android:left="1dp"
-                 android:right="1dp"
-                 android:bottom="1dp"
-                 android:top="1dp" />
+            <padding
+                android:left="8dp"
+                android:right="8dp"
+                android:bottom="8dp"
+                android:top="8dp" />
 
-         <corners android:radius="48dp" />
-      </shape>
-   </item>
+            <corners android:radius="48dp" />
+        </shape>
+    </item>
 </ripple>
diff --git a/packages/SystemUI/res/drawable/qs_bg_avatar.xml b/packages/SystemUI/res/drawable/qs_bg_avatar.xml
new file mode 100644
index 0000000..41176a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_bg_avatar.xml
@@ -0,0 +1,28 @@
+<?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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="100"
+        android:viewportHeight="100">
+
+    <path
+        android:fillColor="@color/qs_user_switcher_avatar_background"
+        android:pathData="M50,50m-50,0a50,50 0,1 1,100 0a50,50 0,1 1,-100 0"/>
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
index 1f38b1e..4413b1e 100644
--- a/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml
@@ -20,24 +20,24 @@
 <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_width="wrap_content"
         android:layout_height="wrap_content"
         android:padding="8dp"
-        android:layout_marginEnd="8dp"
+        android:layout_marginEnd="32dp"
         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">
+        android:background="@drawable/kg_user_switcher_rounded_bg"
+        sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher">
     <TextView android:id="@+id/user_name"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="13dp"
+            android:layout_marginStart="25dp"
+            android:layout_marginEnd="12dp"
             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:layout_width="@dimen/kg_framed_avatar_size"
+            android:layout_height="@dimen/kg_framed_avatar_size"
             android:contentDescription="@null"
             sysui:badgeDiameter="18dp"
             sysui:badgeMargin="1dp" />
diff --git a/packages/SystemUI/res/layout/bubble_dismiss_target.xml b/packages/SystemUI/res/layout/bubble_dismiss_target.xml
index ca085b6..f5cd727 100644
--- a/packages/SystemUI/res/layout/bubble_dismiss_target.xml
+++ b/packages/SystemUI/res/layout/bubble_dismiss_target.xml
@@ -17,7 +17,7 @@
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="@dimen/pip_dismiss_gradient_height"
+    android:layout_height="@dimen/floating_dismiss_gradient_height"
     android:layout_gravity="bottom|center_horizontal">
 
     <FrameLayout
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 5119b59..db81e23 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -40,29 +40,20 @@
 
     <TextView
         android:id="@+id/status"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.Control.Status"
         android:paddingTop="@dimen/control_padding_adjustment"
         android:paddingStart="@dimen/control_status_padding"
-        android:clickable="false"
+        android:clickable="true"
         android:focusable="false"
+        android:singleLine="true"
+        android:ellipsize="marquee"
+        android:marqueeRepeatLimit = "marquee_forever"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toBottomOf="@+id/icon"
         app:layout_constraintStart_toEndOf="@+id/icon" />
 
-
-    <TextView
-        android:id="@+id/status_extra"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.Control.Status"
-        android:paddingTop="@dimen/control_padding_adjustment"
-        android:paddingStart="@dimen/control_status_padding"
-        android:clickable="false"
-        android:focusable="false"
-        app:layout_constraintBottom_toBottomOf="@+id/icon"
-        app:layout_constraintStart_toEndOf="@+id/status" />
-
     <TextView
         android:id="@+id/title"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml
index 50aa212..cb53fe6 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml
@@ -27,7 +27,7 @@
     android:paddingRight="@dimen/global_actions_grid_item_side_margin"
     android:layout_marginRight="3dp"
     android:layout_marginLeft="3dp"
-    android:background="@drawable/rounded_bg_full">
+    android:background="@drawable/control_background">
     <LinearLayout
         android:layout_width="@dimen/global_actions_grid_item_width"
         android:layout_height="@dimen/global_actions_grid_item_height"
@@ -42,7 +42,7 @@
             android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin"
             android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin"
             android:scaleType="centerInside"
-            android:tint="@color/global_actions_text" />
+            android:tint="@color/control_default_foreground" />
 
         <TextView
             android:id="@*android:id/message"
@@ -53,7 +53,7 @@
             android:singleLine="true"
             android:gravity="center"
             android:textSize="12dp"
-            android:textColor="@color/global_actions_text"
+            android:textColor="@color/control_default_foreground"
             android:textAppearance="?android:attr/textAppearanceSmall" />
 
         <TextView
@@ -62,7 +62,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="center"
-            android:textColor="@color/global_actions_text"
+            android:textColor="@color/control_default_foreground"
             android:textAppearance="?android:attr/textAppearanceSmall" />
     </LinearLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index 92ae1b9..620e2e6 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -67,9 +67,7 @@
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:orientation="vertical"
-          android:layout_marginRight="@dimen/global_actions_grid_horizontal_padding"
-          android:layout_marginLeft="@dimen/global_actions_grid_horizontal_padding"
       />
     </LinearLayout>
   </com.android.systemui.globalactions.MinHeightScrollView>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index b1e5165..626951c 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -36,8 +36,8 @@
             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:layout_width="@dimen/kg_framed_avatar_size"
+            android:layout_height="@dimen/kg_framed_avatar_size"
             android:contentDescription="@null"
             android:backgroundTint="@color/qs_user_detail_avatar_tint"
             android:backgroundTintMode="src_atop"
@@ -45,5 +45,5 @@
             sysui:framePadding="2.5dp"
             sysui:badgeDiameter="18dp"
             sysui:badgeMargin="1dp"
-            sysui:frameColor="@color/qs_user_detail_avatar_frame" />
+            sysui:frameColor="@color/kg_user_switcher_rounded_background_color" />
 </com.android.systemui.statusbar.policy.KeyguardUserDetailItemView>
diff --git a/packages/SystemUI/res/layout/pip_dismiss_view.xml b/packages/SystemUI/res/layout/pip_dismiss_view.xml
deleted file mode 100644
index 2cc4b22..0000000
--- a/packages/SystemUI/res/layout/pip_dismiss_view.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/pip_dismiss_gradient_height"
-    android:alpha="0">
-
-    <TextView
-        android:id="@+id/pip_dismiss_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom|center_horizontal"
-        android:text="@string/pip_phone_dismiss_hint"
-        android:textColor="#FFFFFFFF"
-        android:textSize="14sp"
-        android:shadowColor="@android:color/black"
-        android:shadowDx="-2"
-        android:shadowDy="2"
-        android:shadowRadius="0.01" />
-
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 5c03e309..0608685 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -30,21 +30,20 @@
         android:clipToPadding="false"
         android:focusable="true"
         android:background="@drawable/ripple_drawable"
-        systemui:regularTextAppearance="@style/TextAppearance.QS.UserSwitcher"
-        systemui:activatedTextAppearance="@style/TextAppearance.QS.UserSwitcher.Activated">
+        systemui:regularTextAppearance="@style/TextAppearance.QS.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:layout_width="@dimen/qs_framed_avatar_size"
+            android:layout_height="@dimen/qs_framed_avatar_size"
             android:layout_marginBottom="7dp"
             android:backgroundTint="@color/qs_user_detail_avatar_tint"
             android:backgroundTintMode="src_atop"
-            systemui:frameWidth="2dp"
-            systemui:framePadding="2.5dp"
+            systemui:frameWidth="6dp"
             systemui:badgeDiameter="18dp"
             systemui:badgeMargin="1dp"
-            systemui:frameColor="@color/qs_user_detail_avatar_frame"/>
+            systemui:framePadding="-1dp"
+            systemui:frameColor="@color/qs_user_avatar_frame"/>
 
     <LinearLayout
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 63bbe62..da5819c 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -31,4 +31,7 @@
 
     <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
     <integer name="navigation_bar_deadzone_orientation">1</integer>
+
+    <!-- Max number of columns for quick controls area -->
+    <integer name="controls_max_columns">4</integer>
 </resources>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 9b0fe46..93aa270 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -86,4 +86,15 @@
     <color name="GM2_red_500">#E25142</color>
     <color name="GM2_yellow_500">#F5C518</color>
 
+    <!-- Icon color for user avatars in keyguard user switcher -->
+    <color name="kg_user_switcher_avatar_icon_color">@android:color/background_light</color>
+    <!-- Icon color for selected user avatars in keyguard user switcher -->
+    <color name="kg_user_switcher_selected_avatar_icon_color">#202124</color>
+    <!-- Icon color for user avatars in quick settings user switcher  -->
+    <color name="qs_user_switcher_avatar_icon_color">@android:color/background_light</color>
+    <!-- Icon color for selected user avatars in quick settings user switcher  -->
+    <color name="qs_user_switcher_selected_avatar_icon_color">#202124</color>
+    <!-- Color of background circle of user avatars in quick settings user switcher -->
+    <color name="qs_user_switcher_avatar_background">#3C4043</color>
+
 </resources>
diff --git a/packages/SystemUI/res/values-sw320dp/dimens.xml b/packages/SystemUI/res/values-sw320dp/dimens.xml
index 2774554..47a2a09 100644
--- a/packages/SystemUI/res/values-sw320dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw320dp/dimens.xml
@@ -30,5 +30,7 @@
     <dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen>
     <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
 
+    <!-- Home Controls -->
+    <dimen name="controls_list_side_margin">10dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values-sw360dp/dimens.xml b/packages/SystemUI/res/values-sw360dp/dimens.xml
index dfd9743..35a6536 100644
--- a/packages/SystemUI/res/values-sw360dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw360dp/dimens.xml
@@ -26,5 +26,7 @@
          navigation_extra_key_width -->
     <dimen name="navigation_side_padding">40dip</dimen>
 
+    <!-- Home Controls -->
+    <dimen name="controls_list_side_margin">12dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values-sw392dp/dimens.xml b/packages/SystemUI/res/values-sw392dp/dimens.xml
index be1d952..308bc69 100644
--- a/packages/SystemUI/res/values-sw392dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw392dp/dimens.xml
@@ -30,5 +30,7 @@
     <dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen>
     <dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
 
+    <!-- Home Controls -->
+    <dimen name="controls_list_side_margin">16dp</dimen>
 </resources>
 
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 8934183..8f73d23 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -45,6 +45,11 @@
     <!-- Height of the status bar header bar when on Keyguard -->
     <dimen name="status_bar_header_height_keyguard">60dp</dimen>
 
+    <!-- Size of user icon + frame in the qs user picker (incl. frame) -->
+    <dimen name="qs_framed_avatar_size">60dp</dimen>
+    <!-- Size of user icon + frame in the keyguard user picker (incl. frame) -->
+    <dimen name="kg_framed_avatar_size">48dp</dimen>
+
     <!-- The width of user avatar when on Keyguard -->
     <dimen name="multi_user_switch_width_keyguard">48dp</dimen>
 
@@ -88,4 +93,7 @@
 
     <!-- Keyboard shortcuts helper -->
     <dimen name="ksh_layout_width">488dp</dimen>
+
+    <!-- Text size for user name in user switcher -->
+    <dimen name="kg_user_switcher_text_size">18sp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index b375364..02bd602 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -30,20 +30,10 @@
         <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:textSize">@dimen/kg_user_switcher_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/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml
index 95716c8..3343a84 100644
--- a/packages/SystemUI/res/values/arrays_tv.xml
+++ b/packages/SystemUI/res/values/arrays_tv.xml
@@ -35,7 +35,5 @@
     </string-array>
 
     <string-array name="audio_recording_disclosure_exempt_apps" translatable="false">
-      <item>com.google.android.katniss</item>
-      <item>com.google.android.apps.mediashell</item>
     </string-array>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 74bbee6..288487a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -58,6 +58,19 @@
     <!-- Shadows under the clock, date and other keyguard text fields -->
     <color name="keyguard_shadow_color">#B2000000</color>
 
+    <!-- Color for rounded background for activated user in keyguard user switcher -->
+    <color name="kg_user_switcher_activated_background_color">#26000000</color>
+    <!-- Icon color for user avatars in keyguard user switcher -->
+    <color name="kg_user_switcher_avatar_icon_color">@android:color/background_light</color>
+    <!-- Icon color for selected user avatars in keyguard user switcher -->
+    <color name="kg_user_switcher_selected_avatar_icon_color">@android:color/background_light</color>
+    <!-- Icon color for user avatars in user switcher quick settings -->
+    <color name="qs_user_switcher_avatar_icon_color">#3C4043</color>
+    <!-- Icon color for selected user avatars in user switcher quick settings -->
+    <color name="qs_user_switcher_selected_avatar_icon_color">@android:color/background_light</color>
+    <!-- Color of background circle of user avatars in quick settings -->
+    <color name="qs_user_switcher_avatar_background">#DADCE0</color>
+
     <!-- The color of the legacy notification background -->
     <color name="notification_legacy_background_color">#ff1a1a1a</color>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 922acff..f549a32 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -533,4 +533,12 @@
          This config value should contain the package name of that preferred application.
     -->
     <string translatable="false" name="config_controlsPreferredPackage"></string>
+
+    <!-- Max number of columns for quick controls area -->
+    <integer name="controls_max_columns">2</integer>
+    <!-- If the dp width of the available space is <= this value, potentially adjust the number
+         of columns-->
+    <integer name="controls_max_columns_adjust_below_width_dp">320</integer>
+    <!-- If the config font scale is >= this value, potentially adjust the number of columns-->
+    <item name="controls_max_columns_adjust_above_font_scale" translatable="false" format="float" type="dimen">1.25</item>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5a94f25..344479f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -752,8 +752,10 @@
          quick settings header -->
     <dimen name="max_avatar_size">48dp</dimen>
 
-    <!-- Size of user icon + frame in the qs/keyguard user picker (incl. frame) -->
-    <dimen name="framed_avatar_size">54dp</dimen>
+    <!-- Size of user icon + frame in the qs user picker (incl. frame) -->
+    <dimen name="qs_framed_avatar_size">54dp</dimen>
+    <!-- Size of user icon + frame in the keyguard user picker (incl. frame) -->
+    <dimen name="kg_framed_avatar_size">54dp</dimen>
 
     <!-- Margin on the left side of the carrier text on Keyguard -->
     <dimen name="keyguard_carrier_text_margin">16dp</dimen>
@@ -953,7 +955,7 @@
     <dimen name="recents_quick_scrub_onboarding_margin_start">8dp</dimen>
 
     <!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
-    <dimen name="pip_dismiss_gradient_height">176dp</dimen>
+    <dimen name="floating_dismiss_gradient_height">176dp</dimen>
 
     <!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. -->
     <dimen name="pip_dismiss_text_bottom_margin">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d17b81d..566d143 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2647,15 +2647,15 @@
     <!-- Title for Magnification Controls Window [CHAR LIMIT=NONE] -->
     <string name="magnification_controls_title">Magnification Window Controls</string>
 
-    <!-- Quick Controls strings -->
-    <!-- Quick Controls empty state, title [CHAR LIMIT=30] -->
-    <string name="quick_controls_title">Quick controls</string>
-    <!-- Quick Controls empty state, subtitle [CHAR LIMIT=100] -->
+    <!-- Device Controls strings -->
+    <!-- Device Controls empty state, title [CHAR LIMIT=30] -->
+    <string name="quick_controls_title">Device controls</string>
+    <!-- Device Controls empty state, subtitle [CHAR LIMIT=100] -->
     <string name="quick_controls_subtitle">Add controls for your connected devices</string>
 
-    <!-- Quick Controls setup, title [CHAR LIMIT=50] -->
-    <string name="quick_controls_setup_title">Set up quick controls</string>
-    <!-- Quick Controls setup, subtitle [CHAR LIMIT=100] -->
+    <!-- Device Controls setup, title [CHAR LIMIT=50] -->
+    <string name="quick_controls_setup_title">Set up device controls</string>
+    <!-- Device Controls setup, subtitle [CHAR LIMIT=100] -->
     <string name="quick_controls_setup_subtitle">Hold the Power button to access your controls</string>
 
     <!-- Controls management providers screen title [CHAR LIMIT=60]-->
@@ -2679,7 +2679,7 @@
     <string name="controls_favorite_other_zone_header">Other</string>
 
     <!-- Controls dialog title [CHAR LIMIT=40] -->
-    <string name="controls_dialog_title">Add to quick controls</string>
+    <string name="controls_dialog_title">Add to device controls</string>
     <!-- Controls dialog add to favorites [CHAR LIMIT=40] -->
     <string name="controls_dialog_ok">Add to favorites</string>
     <!-- Controls dialog message [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1598465..aabee1c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -249,6 +249,7 @@
     <style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
 
     <style name="TextAppearance.AuthCredential">
+        <item name="android:accessibilityLiveRegion">polite</item>
         <item name="android:gravity">center_horizontal</item>
         <item name="android:textAlignment">gravity</item>
         <item name="android:layout_gravity">top</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index caee8cc..88f4176 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -34,6 +34,7 @@
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 
 /**
@@ -50,6 +51,7 @@
     private boolean mDismissing;
     protected boolean mResumed;
     private CountDownTimer mCountdownTimer = null;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     // To avoid accidental lockout due to events while the device in in the pocket, ignore
     // any passwords with length less than or equal to this length.
@@ -61,6 +63,7 @@
 
     public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
     }
 
     @Override
@@ -151,6 +154,8 @@
             LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
             LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
         }
+
+        mKeyguardUpdateMonitor.setCredentialAttempted();
         mPendingLockCheck = LockPatternChecker.checkCredential(
                 mLockPatternUtils,
                 password,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
index d154434..af5196f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
@@ -24,6 +24,8 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageButton;
@@ -40,6 +42,7 @@
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.MediaControllerFactory;
 import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
@@ -71,10 +74,11 @@
     private KeyguardMediaObserver mObserver;
 
     @Inject
-    public KeyguardMediaPlayer(Context context, @Background Executor backgroundExecutor) {
+    public KeyguardMediaPlayer(Context context, MediaControllerFactory factory,
+            @Background Executor backgroundExecutor) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
-        mViewModel = new KeyguardMediaViewModel(context);
+        mViewModel = new KeyguardMediaViewModel(context, factory);
     }
 
     /** Binds media controls to a view hierarchy. */
@@ -139,14 +143,16 @@
     private static final class KeyguardMediaViewModel {
 
         private final Context mContext;
+        private final MediaControllerFactory mMediaControllerFactory;
         private final MutableLiveData<KeyguardMedia> mMedia = new MutableLiveData<>();
         private final Object mActionsLock = new Object();
         private List<PendingIntent> mActions;
         private float mAlbumArtRadius;
         private int mAlbumArtSize;
 
-        KeyguardMediaViewModel(Context context) {
+        KeyguardMediaViewModel(Context context, MediaControllerFactory factory) {
             mContext = context;
+            mMediaControllerFactory = factory;
             loadDimens();
         }
 
@@ -162,6 +168,17 @@
         public void updateControls(NotificationEntry entry, Icon appIcon,
                 MediaMetadata mediaMetadata) {
 
+            // Check the playback state of the media controller. If it is null, then the session was
+            // probably destroyed. Don't update in this case.
+            final MediaSession.Token token = entry.getSbn().getNotification().extras
+                    .getParcelable(Notification.EXTRA_MEDIA_SESSION);
+            final MediaController controller = token != null
+                    ? mMediaControllerFactory.create(token) : null;
+            if (controller != null && controller.getPlaybackState() == null) {
+                clearControls();
+                return;
+            }
+
             // Foreground and Background colors computed from album art
             Notification notif = entry.getSbn().getNotification();
             int fgColor = notif.color;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 48c6bd1..ad92f8f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -282,6 +282,7 @@
 
         @Override
         public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
+            mKeyguardUpdateMonitor.setCredentialAttempted();
             mLockPatternView.disableInput();
             if (mPendingLockCheck != null) {
                 mPendingLockCheck.cancel(false);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 57e3f14..88d6943 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -223,6 +223,7 @@
     private int mRingMode;
     private int mPhoneState;
     private boolean mKeyguardIsVisible;
+    private boolean mCredentialAttempted;
     private boolean mKeyguardGoingAway;
     private boolean mGoingToSleep;
     private boolean mBouncer;
@@ -498,11 +499,21 @@
     }
 
     /**
+     * Updates KeyguardUpdateMonitor's internal state to know if credential was attempted on
+     * bouncer. Note that this does not care if the credential was correct/incorrect. This is
+     * cleared when the user leaves the bouncer (unlocked, screen off, back to lockscreen, etc)
+     */
+    public void setCredentialAttempted() {
+        mCredentialAttempted = true;
+        updateBiometricListeningState();
+    }
+
+    /**
      * Updates KeyguardUpdateMonitor's internal state to know if keyguard is goingAway
      */
     public void setKeyguardGoingAway(boolean goingAway) {
         mKeyguardGoingAway = goingAway;
-        updateFingerprintListeningState();
+        updateBiometricListeningState();
     }
 
     /**
@@ -664,6 +675,8 @@
             updateFingerprintListeningState();
         } else {
             setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
+            mFingerprintCancelSignal = null;
+            mFaceCancelSignal = null;
         }
 
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
@@ -679,6 +692,11 @@
                     getCurrentUser());
         }
 
+        if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
+                || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
+            mFingerprintLockedOut = true;
+        }
+
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -688,6 +706,7 @@
     }
 
     private void handleFingerprintLockoutReset() {
+        mFingerprintLockedOut = false;
         updateFingerprintListeningState();
     }
 
@@ -1274,6 +1293,7 @@
     private CancellationSignal mFaceCancelSignal;
     private FingerprintManager mFpm;
     private FaceManager mFaceManager;
+    private boolean mFingerprintLockedOut;
 
     /**
      * When we receive a
@@ -1820,13 +1840,17 @@
     }
 
     private boolean shouldListenForFingerprint() {
+        final boolean allowedOnBouncer =
+                !(mFingerprintLockedOut && mBouncer && mCredentialAttempted);
+
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
         final boolean shouldListen = (mKeyguardIsVisible || !mDeviceInteractive ||
                 (mBouncer && !mKeyguardGoingAway) || mGoingToSleep ||
                 shouldListenForFingerprintAssistant() || (mKeyguardOccluded && mIsDreaming))
                 && !mSwitchingUser && !isFingerprintDisabled(getCurrentUser())
-                && (!mKeyguardGoingAway || !mDeviceInteractive) && mIsPrimaryUser;
+                && (!mKeyguardGoingAway || !mDeviceInteractive) && mIsPrimaryUser
+                && allowedOnBouncer;
         return shouldListen;
     }
 
@@ -2372,6 +2396,8 @@
             // camera requests dismiss keyguard (tapping on photos for example). When these happen,
             // face auth should resume.
             mSecureCameraLaunched = false;
+        } else {
+            mCredentialAttempted = false;
         }
 
         for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 23fa645..5442299 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -120,7 +120,7 @@
         private void init(DozeParameters dozeParameters) {
             mIsHighEndGfx = ActivityManager.isHighEndGfx();
             mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
-            mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking;
+            mNeedTransition = false;
 
             // We will preserve EGL context when we are in lock screen or aod
             // to avoid janking in following transition, we need to release when back to home.
@@ -137,7 +137,7 @@
             mRenderer = getRendererInstance();
             getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo);
             setFixedSizeAllowed(true);
-            setOffsetNotificationsEnabled(true);
+            setOffsetNotificationsEnabled(mNeedTransition);
             updateSurfaceSize();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index 6923079..13d6a9b 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -49,6 +49,8 @@
     public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
     public static final Interpolator HEADS_UP_APPEAR = new HeadsUpAppearInterpolator();
     public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
+    public static final Interpolator SHADE_ANIMATION =
+            new PathInterpolator(0.6f, 0.02f, 0.4f, 0.98f);
     public static final Interpolator ICON_OVERSHOT_LESS
             = new PathInterpolator(0.4f, 0f, 0.2f, 1.1f);
     public static final Interpolator PANEL_CLOSE_ACCELERATED
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index b006bc1..8bf2591 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -214,9 +214,11 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        setText(mTitleView, getTitle(mBiometricPromptBundle));
+        final CharSequence title = getTitle(mBiometricPromptBundle);
+        setText(mTitleView, title);
         setTextOrHide(mSubtitleView, getSubtitle(mBiometricPromptBundle));
         setTextOrHide(mDescriptionView, getDescription(mBiometricPromptBundle));
+        announceForAccessibility(title);
 
         final boolean isManagedProfile = Utils.isManagedProfile(mContext, mEffectiveUserId);
         final Drawable image;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index eff6934..044feaa 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -514,7 +514,7 @@
         mDismissTargetContainer = new FrameLayout(context);
         mDismissTargetContainer.setLayoutParams(new FrameLayout.LayoutParams(
                 MATCH_PARENT,
-                getResources().getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height),
+                getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
                 Gravity.BOTTOM));
         mDismissTargetContainer.setClipChildren(false);
         mDismissTargetContainer.addView(targetView);
@@ -523,7 +523,7 @@
 
         // Start translated down so the target springs up.
         targetView.setTranslationY(
-                getResources().getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height));
+                getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height));
 
         // Save the MagneticTarget instance for the newly set up view - we'll add this to the
         // MagnetizedObjects.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index ea1abf9..a0b4938 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -323,6 +323,9 @@
 
     /** Plays a dismiss animation on the dragged out bubble. */
     public void dismissDraggedOutBubble(View bubble, Runnable after) {
+        if (bubble == null) {
+            return;
+        }
         animationForChild(bubble)
                 .withStiffness(SpringForce.STIFFNESS_HIGH)
                 .scaleX(1.1f)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index f2303e6..fe1e632 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -55,13 +55,19 @@
 
     companion object {
         private const val TAG = "ControlsFavoritingActivity"
+
+        // If provided and no structure is available, use as the title
         const val EXTRA_APP = "extra_app_label"
+
+        // If provided, show this structure page first
+        const val EXTRA_STRUCTURE = "extra_structure"
         private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
         private const val TOOLTIP_MAX_SHOWN = 2
     }
 
     private var component: ComponentName? = null
     private var appName: CharSequence? = null
+    private var structureExtra: CharSequence? = null
 
     private lateinit var structurePager: ViewPager2
     private lateinit var statusText: TextView
@@ -111,6 +117,7 @@
         val collator = Collator.getInstance(resources.configuration.locales[0])
         comparator = compareBy(collator) { it.structureName }
         appName = intent.getCharSequenceExtra(EXTRA_APP)
+        structureExtra = intent.getCharSequenceExtra(EXTRA_STRUCTURE) ?: ""
         component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
 
         bindViews()
@@ -137,9 +144,15 @@
                 listOfStructures = controlsByStructure.map {
                     StructureContainer(it.key, AllModel(it.value, favoriteKeys, emptyZoneString))
                 }.sortedWith(comparator)
+
+                val structureIndex = listOfStructures.indexOfFirst {
+                    sc -> sc.structureName == structureExtra
+                }.let { if (it == -1) 0 else it }
+
                 executor.execute {
                     doneButton.isEnabled = true
                     structurePager.adapter = StructureAdapter(listOfStructures)
+                    structurePager.setCurrentItem(structureIndex)
                     if (error) {
                         statusText.text = resources.getText(R.string.controls_favorite_load_error)
                     } else {
@@ -247,7 +260,10 @@
         requireViewById<Button>(R.id.other_apps).apply {
             visibility = View.VISIBLE
             setOnClickListener {
-                this@ControlsFavoritingActivity.onBackPressed()
+                val i = Intent()
+                i.setComponent(
+                    ComponentName(context, ControlsProviderSelectorActivity::class.java))
+                context.startActivity(i)
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 7d3a860..b1cb04e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -51,7 +51,6 @@
 ) {
     val icon: ImageView = layout.requireViewById(R.id.icon)
     val status: TextView = layout.requireViewById(R.id.status)
-    val statusExtra: TextView = layout.requireViewById(R.id.status_extra)
     val title: TextView = layout.requireViewById(R.id.title)
     val subtitle: TextView = layout.requireViewById(R.id.subtitle)
     val context: Context = layout.getContext()
@@ -65,6 +64,8 @@
         val ld = layout.getBackground() as LayerDrawable
         ld.mutate()
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
+        // needed for marquee to start
+        status.setSelected(true)
     }
 
     fun bindData(cws: ControlWithState) {
@@ -103,8 +104,7 @@
 
         behavior?.bind(cws)
 
-        layout.setContentDescription(
-            "${title.text} ${subtitle.text} ${status.text} ${statusExtra.text}")
+        layout.setContentDescription("${title.text} ${subtitle.text} ${status.text}")
     }
 
     fun actionResponse(@ControlAction.ResponseResult response: Int) {
@@ -113,15 +113,12 @@
 
     fun setTransientStatus(tempStatus: String) {
         val previousText = status.getText()
-        val previousTextExtra = statusExtra.getText()
 
         cancelUpdate = uiExecutor.executeDelayed({
                 status.setText(previousText)
-                statusExtra.setText(previousTextExtra)
             }, UPDATE_DELAY_IN_MILLIS)
 
         status.setText(tempStatus)
-        statusExtra.setText("")
     }
 
     fun action(action: ControlAction) {
@@ -154,7 +151,6 @@
         }
 
         status.setTextColor(fg)
-        statusExtra.setTextColor(fg)
 
         icon.setImageDrawable(ri.icon)
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 05a0c45..208d911 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -21,10 +21,12 @@
 import android.content.Context
 import android.content.Intent
 import android.content.SharedPreferences
+import android.content.res.Configuration
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
 import android.service.controls.Control
 import android.service.controls.actions.ControlAction
+import android.util.TypedValue
 import android.util.Log
 import android.view.ContextThemeWrapper
 import android.view.LayoutInflater
@@ -43,6 +45,7 @@
 import com.android.systemui.controls.controller.ControlInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.StructureInfo
+import com.android.systemui.controls.management.ControlsFavoritingActivity
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.management.ControlsProviderSelectorActivity
 import com.android.systemui.dagger.qualifiers.Background
@@ -90,21 +93,8 @@
     private lateinit var lastItems: List<SelectionItem>
     private var popup: ListPopupWindow? = null
     private var activeDialog: Dialog? = null
-    private val addControlsItem: SelectionItem
     private var hidden = true
 
-    init {
-        val addDrawable = context.getDrawable(R.drawable.ic_add).apply {
-            setTint(context.resources.getColor(R.color.control_secondary_text, null))
-        }
-        addControlsItem = SelectionItem(
-            context.resources.getString(R.string.controls_providers_title),
-            "",
-            addDrawable,
-            EMPTY_COMPONENT
-        )
-    }
-
     override val available: Boolean
         get() = controlsController.get().available
 
@@ -182,7 +172,7 @@
         inflater.inflate(R.layout.controls_no_favorites, parent, true)
 
         val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup
-        viewGroup.setOnClickListener(launchSelectorActivityListener(context))
+        viewGroup.setOnClickListener { v: View -> startProviderSelectorActivity(v.context) }
 
         val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
         subtitle.setText(context.resources.getString(R.string.quick_controls_subtitle))
@@ -196,16 +186,28 @@
         }
     }
 
-    private fun launchSelectorActivityListener(context: Context): (View) -> Unit {
-        return { _ ->
-            val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
-            context.sendBroadcast(closeDialog)
-
-            val i = Intent()
-            i.setComponent(ComponentName(context, ControlsProviderSelectorActivity::class.java))
-            i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-            context.startActivity(i)
+    private fun startFavoritingActivity(context: Context, si: StructureInfo) {
+        val i = Intent(context, ControlsFavoritingActivity::class.java).apply {
+            putExtra(ControlsFavoritingActivity.EXTRA_APP,
+                controlsListingController.get().getAppLabel(si.componentName))
+            putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, si.structure)
+            putExtra(Intent.EXTRA_COMPONENT_NAME, si.componentName)
+            addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
         }
+        startActivity(context, i)
+    }
+
+    private fun startProviderSelectorActivity(context: Context) {
+        val i = Intent(context, ControlsProviderSelectorActivity::class.java).apply {
+            addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+        }
+        startActivity(context, i)
+    }
+
+    private fun startActivity(context: Context, intent: Intent) {
+        val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+        context.sendBroadcast(closeDialog)
+        context.startActivity(intent)
     }
 
     private fun showControlsView(items: List<SelectionItem>) {
@@ -246,7 +248,7 @@
                         ) {
                             when (pos) {
                                 // 0: Add Control
-                                0 -> launchSelectorActivityListener(view.context)(parent)
+                                0 -> startFavoritingActivity(view.context, selectedStructure)
                                 else -> Log.w(ControlsUiController.TAG,
                                     "Unsupported index ($pos) on 'more' menu selection")
                             }
@@ -345,10 +347,12 @@
         val inflater = LayoutInflater.from(context)
         inflater.inflate(R.layout.controls_with_favorites, parent, true)
 
+        val maxColumns = findMaxColumns()
+
         val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
         var lastRow: ViewGroup = createRow(inflater, listView)
         selectedStructure.controls.forEach {
-            if (lastRow.getChildCount() == 2) {
+            if (lastRow.getChildCount() == maxColumns) {
                 lastRow = createRow(inflater, listView)
             }
             val baseLayout = inflater.inflate(
@@ -365,12 +369,40 @@
             controlViewsById.put(key, cvh)
         }
 
-        // add spacer if necessary to keep control size consistent
-        if ((selectedStructure.controls.size % 2) == 1) {
+        // add spacers if necessary to keep control size consistent
+        var spacersToAdd = selectedStructure.controls.size % maxColumns
+        while (spacersToAdd > 0) {
             lastRow.addView(Space(context), LinearLayout.LayoutParams(0, 0, 1f))
+            spacersToAdd--
         }
     }
 
+    /**
+     * For low-dp width screens that also employ an increased font scale, adjust the
+     * number of columns. This helps prevent text truncation on these devices.
+     */
+    private fun findMaxColumns(): Int {
+        val res = context.resources
+        var maxColumns = res.getInteger(R.integer.controls_max_columns)
+        val maxColumnsAdjustWidth =
+            res.getInteger(R.integer.controls_max_columns_adjust_below_width_dp)
+
+        val outValue = TypedValue()
+        res.getValue(R.dimen.controls_max_columns_adjust_above_font_scale, outValue, true)
+        val maxColumnsAdjustFontScale = outValue.getFloat()
+
+        val config = res.configuration
+        val isPortrait = config.orientation == Configuration.ORIENTATION_PORTRAIT
+        if (isPortrait &&
+            config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED &&
+            config.screenWidthDp <= maxColumnsAdjustWidth &&
+            config.fontScale >= maxColumnsAdjustFontScale) {
+            maxColumns--
+        }
+
+        return maxColumns
+    }
+
     private fun loadPreference(structures: List<StructureInfo>): StructureInfo {
         if (structures.isEmpty()) return EMPTY_STRUCTURE
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index d33cd94..8fd840e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -56,13 +56,12 @@
             enabled: Boolean,
             offset: Int = 0
         ): RenderInfo {
-            val (fg, bg) = deviceColorMap.getValue(deviceType)
-
-            val iconKey = if (offset > 0) {
+            val key = if (offset > 0) {
                 deviceType * BUCKET_SIZE + offset
             } else deviceType
 
-            val iconState = deviceIconMap.getValue(iconKey)
+            val (fg, bg) = deviceColorMap.getValue(key)
+            val iconState = deviceIconMap.getValue(key)
             val resourceId = iconState[enabled]
             var icon: Drawable?
             if (resourceId == APP_ICON_ID) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index f79c8b2..5a95665 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -47,9 +47,10 @@
     lateinit var control: Control
     lateinit var cvh: ControlViewHolder
     lateinit var rangeTemplate: RangeTemplate
-    lateinit var statusExtra: TextView
     lateinit var status: TextView
     lateinit var context: Context
+    var currentStatusText: CharSequence = ""
+    var currentRangeValue: String = ""
 
     companion object {
         private const val DEFAULT_FORMAT = "%.1f"
@@ -83,8 +84,8 @@
     override fun bind(cws: ControlWithState) {
         this.control = cws.control!!
 
-        statusExtra = cvh.statusExtra
-        status.setText(control.getStatusText())
+        currentStatusText = control.getStatusText()
+        status.setText(currentStatusText)
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
@@ -96,7 +97,7 @@
         val checked = template.isChecked()
         val currentRatio = rangeTemplate.getCurrentValue() /
                 (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
-        updateRange(currentRatio, checked)
+        updateRange(currentRatio, checked, /* isDragging */ false)
 
         cvh.applyRenderInfo(checked)
 
@@ -147,7 +148,7 @@
                                 AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)
                             val ratioDiff = (value - rangeTemplate.getCurrentValue()) /
                                 (rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
-                            updateRange(ratioDiff, template.isChecked())
+                            updateRange(ratioDiff, template.isChecked(), /* isDragging */ false)
                             endUpdateRange()
                             true
                         }
@@ -167,26 +168,27 @@
     }
 
     fun beginUpdateRange() {
-        status.setVisibility(View.GONE)
-        statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+        status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
                 .getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
     }
 
-    fun updateRange(ratioDiff: Float, checked: Boolean) {
+    fun updateRange(ratioDiff: Float, checked: Boolean, isDragging: Boolean) {
         val changeAmount = if (checked) (MAX_LEVEL * ratioDiff).toInt() else MIN_LEVEL
         val newLevel = Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, clipLayer.getLevel() + changeAmount))
         clipLayer.setLevel(newLevel)
 
         if (checked) {
             val newValue = levelToRangeValue(clipLayer.getLevel())
-            val formattedNewValue = format(rangeTemplate.getFormatString().toString(),
+            currentRangeValue = format(rangeTemplate.getFormatString().toString(),
                     DEFAULT_FORMAT, newValue)
-
-            statusExtra.setText(formattedNewValue)
-            statusExtra.setVisibility(View.VISIBLE)
+            val text = if (isDragging) {
+                currentRangeValue
+            } else {
+                "$currentStatusText $currentRangeValue"
+            }
+            status.setText(text)
         } else {
-            statusExtra.setText("")
-            statusExtra.setVisibility(View.GONE)
+            status.setText(currentStatusText)
         }
     }
 
@@ -210,9 +212,9 @@
     }
 
     fun endUpdateRange() {
-        statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
+        status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
                 .getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
-        status.setVisibility(View.VISIBLE)
+        status.setText("$currentStatusText $currentRangeValue")
         cvh.action(FloatAction(rangeTemplate.getTemplateId(),
             findNearestStep(levelToRangeValue(clipLayer.getLevel()))))
     }
@@ -260,7 +262,8 @@
                 isDragging = true
             }
 
-            this@ToggleRangeBehavior.updateRange(-xDiff / v.getWidth(), true)
+            this@ToggleRangeBehavior.updateRange(-xDiff / v.getWidth(),
+                /* checked */ true, /* isDragging */ true)
             return true
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index c16dce1..3f88f25 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -40,7 +40,7 @@
 
     private int mDockState = DockManager.STATE_NONE;
 
-    public DozeDockHandler(AmbientDisplayConfiguration config, DozeMachine machine,
+    DozeDockHandler(AmbientDisplayConfiguration config, DozeMachine machine,
             DockManager dockManager) {
         mMachine = machine;
         mConfig = config;
@@ -74,8 +74,13 @@
         @Override
         public void onEvent(int dockState) {
             if (DEBUG) Log.d(TAG, "dock event = " + dockState);
-            final DozeMachine.State nextState;
+
             mDockState = dockState;
+            if (isPulsing()) {
+                return;
+            }
+
+            DozeMachine.State nextState;
             switch (mDockState) {
                 case DockManager.STATE_DOCKED:
                     nextState = State.DOZE_AOD_DOCKED;
@@ -90,10 +95,15 @@
                 default:
                     return;
             }
-
             mMachine.requestState(nextState);
         }
 
+        private boolean isPulsing() {
+            DozeMachine.State state = mMachine.getState();
+            return state == State.DOZE_REQUEST_PULSE || state == State.DOZE_PULSING
+                    || state == State.DOZE_PULSING_BRIGHT;
+        }
+
         void register() {
             if (mRegistered) {
                 return;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index f7f9afd..18bfd89 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -339,8 +339,8 @@
             return State.DOZE;
         }
         if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING
-                || mState == State.DOZE_AOD || mState == State.DOZE)
-                && requestedState == State.DOZE_PULSE_DONE) {
+                || mState == State.DOZE_AOD || mState == State.DOZE
+                || mState == State.DOZE_AOD_DOCKED) && requestedState == State.DOZE_PULSE_DONE) {
             Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
             return mState;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index fdd8593..4dd5e87 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -489,6 +489,7 @@
 
         mAdapter = new MyAdapter();
 
+        mDepthController.setShowingHomeControls(shouldShowControls());
         ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, getWalletPanelViewController(),
                 mDepthController, mSysuiColorExtractor, mStatusBarService,
                 mNotificationShadeWindowController,
@@ -1780,8 +1781,12 @@
             }
             if (mBackgroundDrawable == null) {
                 mBackgroundDrawable = new ScrimDrawable();
-                mScrimAlpha = mBlurUtils.supportsBlursOnWindows()
-                        ? ScrimController.BLUR_SCRIM_ALPHA : ScrimController.BUSY_SCRIM_ALPHA;
+                if (mControlsUiController != null) {
+                    mScrimAlpha = 1.0f;
+                } else {
+                    mScrimAlpha = mBlurUtils.supportsBlursOnWindows()
+                            ? ScrimController.BLUR_SCRIM_ALPHA : ScrimController.BUSY_SCRIM_ALPHA;
+                }
             }
             getWindow().setBackgroundDrawable(mBackgroundDrawable);
         }
@@ -1841,8 +1846,9 @@
             if (!(mBackgroundDrawable instanceof ScrimDrawable)) {
                 return;
             }
-            ((ScrimDrawable) mBackgroundDrawable).setColor(colors.supportsDarkText() ? Color.WHITE
-                    : Color.BLACK, animate);
+            boolean hasControls = mControlsUiController != null;
+            ((ScrimDrawable) mBackgroundDrawable).setColor(
+                    !hasControls && colors.supportsDarkText() ? Color.WHITE : Color.BLACK, animate);
             View decorView = getWindow().getDecorView();
             if (colors.supportsDarkText()) {
                 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java b/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java
new file mode 100644
index 0000000..71bc7c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControllerFactory.java
@@ -0,0 +1,45 @@
+/*
+ * 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.media;
+
+import android.content.Context;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+
+import javax.inject.Inject;
+
+/**
+ * Testable wrapper around {@link MediaController} constructor.
+ */
+public class MediaControllerFactory {
+
+    private final Context mContext;
+
+    @Inject
+    public MediaControllerFactory(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Creates a new MediaController from a session's token.
+     *
+     * @param token The token for the session. This value must never be null.
+     */
+    public MediaController create(MediaSession.Token token) {
+        return new MediaController(mContext, token);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index dba4343..d219a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -319,7 +319,6 @@
                     getSurfaceTransactionHelper()
                             .crop(tx, leash, getDestinationBounds())
                             .round(tx, leash, shouldApplyCornerRadius());
-                    tx.show(leash);
                     tx.apply();
                 }
             };
@@ -360,7 +359,6 @@
                     getSurfaceTransactionHelper()
                             .alpha(tx, leash, 1f)
                             .round(tx, leash, shouldApplyCornerRadius());
-                    tx.show(leash);
                     tx.apply();
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 1868536..e24d29f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -231,7 +231,12 @@
     /**
      * @return {@link Rect} of the destination PiP window bounds.
      */
-    Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) {
+    Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds,
+            Size minimalSize) {
+        if (!componentName.equals(mLastPipComponentName)) {
+            onResetReentryBoundsUnchecked();
+            mLastPipComponentName = componentName;
+        }
         final Rect destinationBounds;
         if (bounds == null) {
             final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
@@ -246,11 +251,7 @@
             transformBoundsToAspectRatio(destinationBounds, aspectRatio,
                     false /* useCurrentMinEdgeSize */);
         }
-        if (destinationBounds.equals(bounds)) {
-            return bounds;
-        }
         mAspectRatio = aspectRatio;
-        onResetReentryBoundsUnchecked();
         mLastDestinationBounds.set(destinationBounds);
         return destinationBounds;
     }
@@ -483,6 +484,7 @@
         pw.println(prefix + TAG);
         pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
         pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction);
+        pw.println(innerPrefix + "mReentrySize=" + mReentrySize);
         pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
         pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
         pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 8cff20a..b10dd93 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -247,7 +247,7 @@
     public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(info, "Requires RunningTaskInfo");
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                getAspectRatioOrDefault(info.pictureInPictureParams),
+                info.topActivity, getAspectRatioOrDefault(info.pictureInPictureParams),
                 null /* bounds */, getMinimalSize(info.topActivityInfo));
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         mTaskInfo = info;
@@ -303,7 +303,7 @@
             return;
         }
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                getAspectRatioOrDefault(newParams),
+                info.topActivity, getAspectRatioOrDefault(newParams),
                 null /* bounds */, getMinimalSize(info.topActivityInfo));
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
@@ -319,23 +319,29 @@
      * TODO(b/152809058): consolidate the display info handling logic in SysUI
      */
     @SuppressWarnings("unchecked")
-    public void mayUpdateCurrentAnimationOnRotationChange() {
+    public void onMovementBoundsChanged(boolean fromImeAdjustment, boolean fromShelfAdjustment) {
         final PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getCurrentAnimator();
-        if (animator != null && animator.isRunning()
-                && animator.getTransitionDirection() == TRANSITION_DIRECTION_TO_PIP) {
-            final Rect currentDestinationBounds = animator.getDestinationBounds();
-            if (mPipBoundsHandler.getDisplayBounds().contains(currentDestinationBounds)) {
-                return;
-            }
-            final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    getAspectRatioOrDefault(mTaskInfo.pictureInPictureParams),
-                    null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
-            if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
-                animator.updateEndValue(newDestinationBounds);
-            }
-            animator.setDestinationBounds(newDestinationBounds);
+        if (animator == null || !animator.isRunning()
+                || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
+            return;
         }
+
+        final Rect currentDestinationBounds = animator.getDestinationBounds();
+        if (!fromImeAdjustment && !fromShelfAdjustment
+                && mPipBoundsHandler.getDisplayBounds().contains(currentDestinationBounds)) {
+            // no need to update the destination bounds, bail early
+            return;
+        }
+
+        final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
+                mTaskInfo.topActivity, getAspectRatioOrDefault(mTaskInfo.pictureInPictureParams),
+                null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
+        if (newDestinationBounds.equals(currentDestinationBounds)) return;
+        if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
+            animator.updateEndValue(newDestinationBounds);
+        }
+        animator.setDestinationBounds(newDestinationBounds);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
deleted file mode 100644
index b725811..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ /dev/null
@@ -1,188 +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.systemui.pip.phone;
-
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-import android.widget.FrameLayout;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-/**
- * Displays the dismiss UI and target for floating objects.
- */
-public class PipDismissViewController {
-
-    // This delay controls how long to wait before we show the target when the user first moves
-    // the PIP, to prevent the target from animating if the user just wants to fling the PIP
-    public static final int SHOW_TARGET_DELAY = 100;
-    private static final int SHOW_TARGET_DURATION = 350;
-    private static final int HIDE_TARGET_DURATION = 225;
-
-    private Context mContext;
-    private WindowManager mWindowManager;
-    private View mDismissView;
-
-    // Used for dismissing a bubble -- bubble should be in the target to be considered a dismiss
-    private View mTargetView;
-    private int mTargetSlop;
-    private Point mWindowSize;
-    private int[] mLoc = new int[2];
-    private boolean mIntersecting;
-    private Vibrator mVibe;
-
-    public PipDismissViewController(Context context) {
-        mContext = context;
-        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        mVibe = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
-    }
-
-    /**
-     * Creates the dismiss target for showing via {@link #showDismissTarget()}.
-     */
-    public void createDismissTarget() {
-        if (mDismissView == null) {
-            // Determine sizes for the view
-            final Rect stableInsets = new Rect();
-            WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
-            mWindowSize = new Point();
-            mWindowManager.getDefaultDisplay().getRealSize(mWindowSize);
-            final int gradientHeight = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.pip_dismiss_gradient_height);
-            final int bottomMargin = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.pip_dismiss_text_bottom_margin);
-            mTargetSlop = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.bubble_dismiss_slop);
-
-            // Create a new view for the dismiss target
-            LayoutInflater inflater = LayoutInflater.from(mContext);
-            mDismissView = inflater.inflate(R.layout.pip_dismiss_view, null);
-            mDismissView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-            mDismissView.forceHasOverlappingRendering(false);
-
-            // Set the gradient background
-            Drawable gradient = mContext.getResources().getDrawable(R.drawable.pip_dismiss_scrim);
-            gradient.setAlpha((int) (255 * 0.85f));
-            mDismissView.setBackground(gradient);
-
-            // Adjust bottom margins of the text
-            mTargetView = mDismissView.findViewById(R.id.pip_dismiss_text);
-            FrameLayout.LayoutParams tlp = (FrameLayout.LayoutParams) mTargetView.getLayoutParams();
-            tlp.bottomMargin = stableInsets.bottom + bottomMargin;
-            mTargetView.setLayoutParams(tlp);
-
-            // Add the target to the window
-            LayoutParams lp =  new LayoutParams(
-                    LayoutParams.MATCH_PARENT, gradientHeight,
-                    0, mWindowSize.y - gradientHeight,
-                    LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                    LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                            | LayoutParams.FLAG_NOT_TOUCHABLE
-                            | LayoutParams.FLAG_NOT_FOCUSABLE,
-                    PixelFormat.TRANSLUCENT);
-            lp.setTitle("pip-dismiss-overlay");
-            lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-            lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
-            lp.setFitInsetsTypes(0 /* types */);
-            mWindowManager.addView(mDismissView, lp);
-        }
-        mDismissView.animate().cancel();
-    }
-
-
-    /**
-     * Updates the dismiss target based on location of the view, only used for bubbles not for PIP.
-     *
-     * @return whether the view is within the dismiss target.
-     */
-    public boolean updateTarget(View view) {
-        if (mDismissView == null) {
-            return false;
-        }
-        if (mDismissView.getAlpha() > 0) {
-            view.getLocationOnScreen(mLoc);
-            Rect viewRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + view.getWidth(),
-                    mLoc[1] + view.getHeight());
-            mTargetView.getLocationOnScreen(mLoc);
-            Rect targetRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + mTargetView.getWidth(),
-                    mLoc[1] + mTargetView.getHeight());
-            expandRect(targetRect, mTargetSlop);
-            boolean intersecting = targetRect.intersect(viewRect);
-            if (intersecting != mIntersecting) {
-                // TODO: is this the right effect?
-                mVibe.vibrate(VibrationEffect.get(intersecting
-                        ? VibrationEffect.EFFECT_CLICK
-                        : VibrationEffect.EFFECT_TICK));
-            }
-            mIntersecting = intersecting;
-            return intersecting;
-        }
-        return false;
-    }
-
-    /**
-     * Shows the dismiss target.
-     */
-    public void showDismissTarget() {
-        mDismissView.animate()
-                .alpha(1f)
-                .setInterpolator(Interpolators.LINEAR)
-                .setStartDelay(SHOW_TARGET_DELAY)
-                .setDuration(SHOW_TARGET_DURATION)
-                .start();
-    }
-
-    /**
-     * Hides and destroys the dismiss target.
-     */
-    public void destroyDismissTarget() {
-        if (mDismissView != null) {
-            mDismissView.animate()
-                    .alpha(0f)
-                    .setInterpolator(Interpolators.LINEAR)
-                    .setStartDelay(0)
-                    .setDuration(HIDE_TARGET_DURATION)
-                    .withEndAction(new Runnable() {
-                        @Override
-                        public void run() {
-                            mWindowManager.removeViewImmediate(mDismissView);
-                            mDismissView = null;
-                        }
-                    })
-                    .start();
-        }
-    }
-
-    private void expandRect(Rect outRect, int expandAmount) {
-        outRect.left = Math.max(0, outRect.left - expandAmount);
-        outRect.top = Math.max(0, outRect.top - expandAmount);
-        outRect.right = Math.min(mWindowSize.x, outRect.right + expandAmount);
-        outRect.bottom = Math.min(mWindowSize.y, outRect.bottom + expandAmount);
-    }
-}
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 99d6df5..918c45b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -362,7 +362,7 @@
         mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
                 animatingBounds, fromImeAdjustment, fromShelfAdjustment,
                 mTmpDisplayInfo.rotation);
-        mPipTaskOrganizer.mayUpdateCurrentAnimationOnRotationChange();
+        mPipTaskOrganizer.onMovementBoundsChanged(fromImeAdjustment, fromShelfAdjustment);
     }
 
     public void dump(PrintWriter pw) {
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 8397c65..a192afc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -24,8 +24,6 @@
 import android.app.ActivityManager.StackInfo;
 import android.app.IActivityTaskManager;
 import android.content.Context;
-import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.RemoteException;
@@ -40,6 +38,7 @@
 import com.android.systemui.util.FloatingContentCoordinator;
 import com.android.systemui.util.animation.FloatProperties;
 import com.android.systemui.util.animation.PhysicsAnimator;
+import com.android.systemui.util.magnetictarget.MagnetizedObject;
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
@@ -61,9 +60,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 height that the user has to drag offscreen to dismiss the PiP
-    private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;
-
     private final Context mContext;
     private final IActivityTaskManager mActivityTaskManager;
     private final PipTaskOrganizer mPipTaskOrganizer;
@@ -103,7 +99,7 @@
     /**
      * Update listener that resizes the PIP to {@link #mAnimatedBounds}.
      */
-    private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
+    final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
             (target, values) -> resizePipUnchecked(mAnimatedBounds);
 
     /** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
@@ -122,6 +118,13 @@
 
     private final Consumer<Rect> mUpdateBoundsCallback = mBounds::set;
 
+    /**
+     * Whether we're springing to the touch event location (vs. moving it to that position
+     * instantly). We spring-to-touch after PIP is dragged out of the magnetic target, since it was
+     * 'stuck' in the target and needs to catch up to the touch location.
+     */
+    private boolean mSpringingToTouch = false;
+
     public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
             PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
             PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
@@ -211,9 +214,35 @@
             mFloatingContentCoordinator.onContentMoved(this);
         }
 
-        cancelAnimations();
-        resizePipUnchecked(toBounds);
-        mBounds.set(toBounds);
+        if (!mSpringingToTouch) {
+            // If we are moving PIP directly to the touch event locations, cancel any animations and
+            // move PIP to the given bounds.
+            cancelAnimations();
+            resizePipUnchecked(toBounds);
+            mBounds.set(toBounds);
+        } else {
+            // If PIP is 'catching up' after being stuck in the dismiss target, update the animation
+            // to spring towards the new touch location.
+            mAnimatedBoundsPhysicsAnimator
+                    .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
+                    .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig)
+                    .withEndActions(() -> mSpringingToTouch = false);
+
+            startBoundsAnimator(toBounds.left /* toX */, toBounds.top /* toY */);
+        }
+    }
+
+    /** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */
+    void setSpringingToTouch(boolean springingToTouch) {
+        if (springingToTouch) {
+            mAnimatedBounds.set(mBounds);
+        }
+
+        mSpringingToTouch = springingToTouch;
+    }
+
+    void prepareForAnimation() {
+        mAnimatedBounds.set(mBounds);
     }
 
     /**
@@ -278,24 +307,11 @@
     }
 
     /**
-     * @return whether the PiP at the current bounds should be dismissed.
-     */
-    boolean shouldDismissPip() {
-        Point displaySize = new Point();
-        mContext.getDisplay().getRealSize(displaySize);
-        final int y = displaySize.y - mStableInsets.bottom;
-        if (mBounds.bottom > y) {
-            float offscreenFraction = (float) (mBounds.bottom - y) / mBounds.height();
-            return offscreenFraction >= DISMISS_OFFSCREEN_FRACTION;
-        }
-        return false;
-    }
-
-    /**
      * Flings the PiP to the closest snap target.
      */
     void flingToSnapTarget(
-            float velocityX, float velocityY, Runnable updateAction, @Nullable Runnable endAction) {
+            float velocityX, float velocityY,
+            @Nullable Runnable updateAction, @Nullable Runnable endAction) {
         mAnimatedBounds.set(mBounds);
         mAnimatedBoundsPhysicsAnimator
                 .flingThenSpring(
@@ -303,9 +319,13 @@
                         true /* flingMustReachMinOrMax */)
                 .flingThenSpring(
                         FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
-                .addUpdateListener((target, values) -> updateAction.run())
                 .withEndActions(endAction);
 
+        if (updateAction != null) {
+            mAnimatedBoundsPhysicsAnimator.addUpdateListener(
+                    (target, values) -> updateAction.run());
+        }
+
         final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
         final float estimatedFlingYEndValue =
                 PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY);
@@ -338,16 +358,14 @@
      * Animates the dismissal of the PiP off the edge of the screen.
      */
     void animateDismiss(float velocityX, float velocityY, @Nullable Runnable updateAction) {
-        final float velocity = PointF.length(velocityX, velocityY);
-        final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
-        final Point dismissEndPoint = getDismissEndPoint(mBounds, velocityX, velocityY, isFling);
-
         mAnimatedBounds.set(mBounds);
 
-        // Animate to the dismiss end point, and then dismiss PIP.
+        // Animate off the bottom of the screen, then dismiss PIP.
         mAnimatedBoundsPhysicsAnimator
-                .spring(FloatProperties.RECT_X, dismissEndPoint.x, velocityX, mSpringConfig)
-                .spring(FloatProperties.RECT_Y, dismissEndPoint.y, velocityY, mSpringConfig)
+                .spring(FloatProperties.RECT_Y,
+                        mBounds.bottom + mBounds.height(),
+                        velocityY,
+                        mSpringConfig)
                 .withEndActions(this::dismissPip);
 
         // If we were provided with an update action, run it whenever there's an update.
@@ -356,7 +374,7 @@
                     (target, values) -> updateAction.run());
         }
 
-        startBoundsAnimator(dismissEndPoint.x /* toX */, dismissEndPoint.y /* toY */);
+        startBoundsAnimator(mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */);
     }
 
     /**
@@ -408,6 +426,7 @@
     private void cancelAnimations() {
         mAnimatedBoundsPhysicsAnimator.cancel();
         mAnimatingToBounds.setEmpty();
+        mSpringingToTouch = false;
     }
 
     /** Set new fling configs whose min/max values respect the given movement bounds. */
@@ -426,7 +445,9 @@
      * the 'real' bounds to equal the final animated bounds.
      */
     private void startBoundsAnimator(float toX, float toY) {
-        cancelAnimations();
+        if (!mSpringingToTouch) {
+            cancelAnimations();
+        }
 
         // Set animatingToBounds directly to avoid allocating a new Rect, but then call
         // setAnimatingToBounds to run the normal logic for changing animatingToBounds.
@@ -484,47 +505,29 @@
     }
 
     /**
-     * @return the coordinates the PIP should animate to based on the direction of velocity when
-     *         dismissing.
+     * Returns a MagnetizedObject wrapper for PIP's animated bounds. This is provided to the
+     * magnetic dismiss target so it can calculate PIP's size and position.
      */
-    private Point getDismissEndPoint(Rect pipBounds, float velX, float velY, boolean isFling) {
-        Point displaySize = new Point();
-        mContext.getDisplay().getRealSize(displaySize);
-        final float bottomBound = displaySize.y + pipBounds.height() * .1f;
-        if (isFling && velX != 0 && velY != 0) {
-            // Line is defined by: y = mx + b, m = slope, b = y-intercept
-            // Find the slope
-            final float slope = velY / velX;
-            // Sub in slope and PiP position to solve for y-intercept: b = y - mx
-            final float yIntercept = pipBounds.top - slope * pipBounds.left;
-            // Now find the point on this line when y = bottom bound: x = (y - b) / m
-            final float x = (bottomBound - yIntercept) / slope;
-            return new Point((int) x, (int) bottomBound);
-        } else {
-            // If it wasn't a fling the velocity on 'up' is not reliable for direction of movement,
-            // just animate downwards.
-            return new Point(pipBounds.left, (int) bottomBound);
-        }
-    }
+    MagnetizedObject<Rect> getMagnetizedPip() {
+        return new MagnetizedObject<Rect>(
+                mContext, mAnimatedBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
+            @Override
+            public float getWidth(@NonNull Rect animatedPipBounds) {
+                return animatedPipBounds.width();
+            }
 
-    /**
-     * @return whether the gesture it towards the dismiss area based on the velocity when
-     *         dismissing.
-     */
-    public boolean isGestureToDismissArea(Rect pipBounds, float velX, float velY,
-            boolean isFling) {
-        Point endpoint = getDismissEndPoint(pipBounds, velX, velY, isFling);
-        // Center the point
-        endpoint.x += pipBounds.width() / 2;
-        endpoint.y += pipBounds.height() / 2;
+            @Override
+            public float getHeight(@NonNull Rect animatedPipBounds) {
+                return animatedPipBounds.height();
+            }
 
-        // The dismiss area is the middle third of the screen, half the PIP's height from the bottom
-        Point size = new Point();
-        mContext.getDisplay().getRealSize(size);
-        final int left = size.x / 3;
-        Rect dismissArea = new Rect(left, size.y - (pipBounds.height() / 2), left * 2,
-                size.y + pipBounds.height());
-        return dismissArea.contains(endpoint.x, endpoint.y);
+            @Override
+            public void getLocationOnScreen(
+                    @NonNull Rect animatedPipBounds, @NonNull int[] loc) {
+                loc[0] = animatedPipBounds.left;
+                loc[1] = animatedPipBounds.top;
+            }
+        };
     }
 
     public void dump(PrintWriter pw, String prefix) {
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 7cc2759..bbb4939 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -20,11 +20,13 @@
 import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
 import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
 
+import android.annotation.SuppressLint;
 import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -33,14 +35,23 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
+import android.view.Gravity;
 import android.view.IPinnedStackController;
 import android.view.InputEvent;
 import android.view.MotionEvent;
+import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -51,7 +62,10 @@
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.DismissCircleView;
 import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.util.animation.PhysicsAnimator;
+import com.android.systemui.util.magnetictarget.MagnetizedObject;
 
 import java.io.PrintWriter;
 
@@ -62,9 +76,6 @@
 public class PipTouchHandler {
     private static final String TAG = "PipTouchHandler";
 
-    // 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;
-
     private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
     private static final int BOTTOM_OFFSET_BUFFER_DP = 1;
 
@@ -73,17 +84,45 @@
     // Allow PIP to resize to a slightly bigger state upon touch
     private final boolean mEnableResize;
     private final Context mContext;
+    private final WindowManager mWindowManager;
     private final IActivityManager mActivityManager;
     private final PipBoundsHandler mPipBoundsHandler;
     private PipResizeGestureHandler mPipResizeGestureHandler;
     private IPinnedStackController mPinnedStackController;
 
     private final PipMenuActivityController mMenuController;
-    private final PipDismissViewController mDismissViewController;
     private final PipSnapAlgorithm mSnapAlgorithm;
     private final AccessibilityManager mAccessibilityManager;
     private boolean mShowPipMenuOnAnimationEnd = false;
 
+    /**
+     * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
+     * PIP.
+     */
+    private MagnetizedObject<Rect> mMagnetizedPip;
+
+    /**
+     * Container for the dismiss circle, so that it can be animated within the container via
+     * translation rather than within the WindowManager via slow layout animations.
+     */
+    private ViewGroup mTargetViewContainer;
+
+    /** Circle view used to render the dismiss target. */
+    private DismissCircleView mTargetView;
+
+    /**
+     * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius.
+     */
+    private MagnetizedObject.MagneticTarget mMagneticTarget;
+
+    /** PhysicsAnimator instance for animating the dismiss target in/out. */
+    private PhysicsAnimator<View> mMagneticTargetAnimator;
+
+    /** Default configuration to use for springing the dismiss target in/out. */
+    private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
+            new PhysicsAnimator.SpringConfig(
+                    SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_NO_BOUNCY);
+
     // The current movement bounds
     private Rect mMovementBounds = new Rect();
     // The current resized bounds, changed by user resize.
@@ -104,21 +143,20 @@
     private int mDeferResizeToNormalBoundsUntilRotation = -1;
     private int mDisplayRotation;
 
+    /**
+     * Runnable that can be posted delayed to show the target. This needs to be saved as a member
+     * variable so we can pass it to removeCallbacks.
+     */
+    private Runnable mShowTargetAction = this::showDismissTargetMaybe;
+
     private Handler mHandler = new Handler();
-    private Runnable mShowDismissAffordance = new Runnable() {
-        @Override
-        public void run() {
-            if (mEnableDismissDragToEdge) {
-                mDismissViewController.showDismissTarget();
-            }
-        }
-    };
 
     // Behaviour states
     private int mMenuState = MENU_STATE_NONE;
     private boolean mIsImeShowing;
     private int mImeHeight;
     private int mImeOffset;
+    private int mDismissAreaHeight;
     private boolean mIsShelfShowing;
     private int mShelfHeight;
     private int mMovementBoundsExtraOffsets;
@@ -168,6 +206,7 @@
         }
     }
 
+    @SuppressLint("InflateParams")
     public PipTouchHandler(Context context, IActivityManager activityManager,
             IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
             InputConsumerController inputConsumerController,
@@ -180,9 +219,9 @@
         mContext = context;
         mActivityManager = activityManager;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mMenuController = menuController;
         mMenuController.addListener(new PipMenuListener());
-        mDismissViewController = new PipDismissViewController(context);
         mSnapAlgorithm = pipSnapAlgorithm;
         mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(),
                 2.5f);
@@ -200,6 +239,7 @@
         mExpandedShortestEdgeSize = res.getDimensionPixelSize(
                 R.dimen.pip_expanded_shortest_edge_size);
         mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
+        mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
 
         mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
         mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
@@ -212,6 +252,56 @@
         mFloatingContentCoordinator = floatingContentCoordinator;
         mConnection = new PipAccessibilityInteractionConnection(mMotionHelper,
                 this::onAccessibilityShowMenu, mHandler);
+
+        final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
+        mTargetView = new DismissCircleView(context);
+        final FrameLayout.LayoutParams newParams =
+                new FrameLayout.LayoutParams(targetSize, targetSize);
+        newParams.gravity = Gravity.CENTER;
+        mTargetView.setLayoutParams(newParams);
+
+        mTargetViewContainer = new FrameLayout(context);
+        mTargetViewContainer.setClipChildren(false);
+        mTargetViewContainer.addView(mTargetView);
+
+        mMagnetizedPip = mMotionHelper.getMagnetizedPip();
+        mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
+        mMagnetizedPip.setPhysicsAnimatorUpdateListener(mMotionHelper.mResizePipUpdateListener);
+        mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
+            @Override
+            public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+                mMotionHelper.prepareForAnimation();
+
+                // Show the dismiss target, in case the initial touch event occurred within the
+                // magnetic field radius.
+                showDismissTargetMaybe();
+            }
+
+            @Override
+            public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+                    float velX, float velY, boolean wasFlungOut) {
+                if (wasFlungOut) {
+                    mMotionHelper.flingToSnapTarget(velX, velY, null, null);
+                    hideDismissTarget();
+                } else {
+                    mMotionHelper.setSpringingToTouch(true);
+                }
+            }
+
+            @Override
+            public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+                mHandler.post(() -> {
+                    mMotionHelper.animateDismiss(0, 0, null);
+                    hideDismissTarget();
+                });
+
+
+                MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
+                        PipUtils.getTopPipActivity(mContext, mActivityManager));
+            }
+        });
+
+        mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
     }
 
     public void setTouchGesture(PipTouchGesture gesture) {
@@ -231,7 +321,8 @@
     }
 
     public void onActivityPinned() {
-        cleanUpDismissTarget();
+        createDismissTargetMaybe();
+
         mShowPipMenuOnAnimationEnd = true;
         mPipResizeGestureHandler.onActivityPinned();
         mFloatingContentCoordinator.onContentAdded(mMotionHelper);
@@ -264,6 +355,10 @@
     public void onConfigurationChanged() {
         mMotionHelper.onConfigurationChanged();
         mMotionHelper.synchronizePinnedStackBounds();
+
+        // Recreate the dismiss target for the new orientation.
+        cleanUpDismissTarget();
+        createDismissTargetMaybe();
     }
 
     public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
@@ -351,6 +446,74 @@
         }
     }
 
+    /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
+    private void createDismissTargetMaybe() {
+        if (!mTargetViewContainer.isAttachedToWindow()) {
+            mHandler.removeCallbacks(mShowTargetAction);
+            mMagneticTargetAnimator.cancel();
+
+            final Point windowSize = new Point();
+            mWindowManager.getDefaultDisplay().getRealSize(windowSize);
+            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                    WindowManager.LayoutParams.MATCH_PARENT,
+                    mDismissAreaHeight,
+                    0, windowSize.y - mDismissAreaHeight,
+                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                    PixelFormat.TRANSLUCENT);
+            lp.setTitle("pip-dismiss-overlay");
+            lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+            lp.setFitInsetsTypes(0 /* types */);
+
+            mTargetViewContainer.setVisibility(View.INVISIBLE);
+            mWindowManager.addView(mTargetViewContainer, lp);
+        }
+    }
+
+    /** Makes the dismiss target visible and animates it in, if it isn't already visible. */
+    private void showDismissTargetMaybe() {
+        createDismissTargetMaybe();
+
+        if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
+
+            mTargetView.setTranslationY(mTargetViewContainer.getHeight());
+            mTargetViewContainer.setVisibility(View.VISIBLE);
+
+            // Set the magnetic field radius to half of PIP's width.
+            mMagneticTarget.setMagneticFieldRadiusPx(mMotionHelper.getBounds().width());
+
+            // Cancel in case we were in the middle of animating it out.
+            mMagneticTargetAnimator.cancel();
+            mMagneticTargetAnimator
+                    .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
+                    .start();
+        }
+    }
+
+    /** Animates the magnetic dismiss target out and then sets it to GONE. */
+    private void hideDismissTarget() {
+        mHandler.removeCallbacks(mShowTargetAction);
+        mMagneticTargetAnimator
+                .spring(DynamicAnimation.TRANSLATION_Y,
+                        mTargetViewContainer.getHeight(),
+                        mTargetSpringConfig)
+                .withEndActions(() ->  mTargetViewContainer.setVisibility(View.GONE))
+                .start();
+    }
+
+    /**
+     * Removes the dismiss target and cancels any pending callbacks to show it.
+     */
+    private void cleanUpDismissTarget() {
+        mHandler.removeCallbacks(mShowTargetAction);
+
+        if (mTargetViewContainer.isAttachedToWindow()) {
+            mWindowManager.removeView(mTargetViewContainer);
+        }
+    }
+
     private void onRegistrationChanged(boolean isRegistered) {
         mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
                 ? mConnection : null);
@@ -375,8 +538,24 @@
         if (mPinnedStackController == null) {
             return true;
         }
+
         MotionEvent ev = (MotionEvent) inputEvent;
 
+        if (mMagnetizedPip.maybeConsumeMotionEvent(ev)) {
+            // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
+            // to the touch state. Touch state needs a DOWN event in order to later process MOVE
+            // events it'll receive if the object is dragged out of the magnetic field.
+            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+                mTouchState.onTouchEvent(ev);
+            }
+
+            // Continue tracking velocity when the object is in the magnetic field, since we want to
+            // respect touch input velocity if the object is dragged out and then flung.
+            mTouchState.addMovementToVelocityTracker(ev);
+
+            return true;
+        }
+
         // Update the touch state
         mTouchState.onTouchEvent(ev);
 
@@ -600,17 +779,13 @@
             mDelta.set(0f, 0f);
             mStartPosition.set(bounds.left, bounds.top);
             mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
+            mMotionHelper.setSpringingToTouch(false);
 
             // 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) {
                 mMenuController.pokeMenu();
             }
-
-            if (mEnableDismissDragToEdge) {
-                mDismissViewController.createDismissTarget();
-                mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
-            }
         }
 
         @Override
@@ -623,8 +798,10 @@
                 mSavedSnapFraction = -1f;
 
                 if (mEnableDismissDragToEdge) {
-                    mHandler.removeCallbacks(mShowDismissAffordance);
-                    mDismissViewController.showDismissTarget();
+                    if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
+                        mHandler.removeCallbacks(mShowTargetAction);
+                        showDismissTargetMaybe();
+                    }
                 }
             }
 
@@ -644,10 +821,6 @@
                 mTmpBounds.offsetTo((int) left, (int) top);
                 mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
 
-                if (mEnableDismissDragToEdge) {
-                    updateDismissFraction();
-                }
-
                 final PointF curPos = touchState.getLastTouchPosition();
                 if (mMovementWithinDismiss) {
                     // Track if movement remains near the bottom edge to identify swipe to dismiss
@@ -661,9 +834,7 @@
         @Override
         public boolean onUp(PipTouchState touchState) {
             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();
+                hideDismissTarget();
             }
 
             if (!touchState.isUserInteracting()) {
@@ -671,26 +842,8 @@
             }
 
             final PointF vel = touchState.getVelocity();
-            final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
             final float velocity = PointF.length(vel.x, vel.y);
             final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
-            final boolean isUpWithinDimiss = ENABLE_FLING_DISMISS
-                    && touchState.getLastTouchPosition().y >= mMovementBounds.bottom
-                    && mMotionHelper.isGestureToDismissArea(mMotionHelper.getBounds(), vel.x,
-                            vel.y, isFling);
-            final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal
-                    && (mMovementWithinDismiss || isUpWithinDimiss);
-            if (mEnableDismissDragToEdge) {
-                // Check if the user dragged or flung the PiP offscreen to dismiss it
-                if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
-                    MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
-                            PipUtils.getTopPipActivity(mContext, mActivityManager));
-                    mMotionHelper.animateDismiss(
-                            vel.x, vel.y,
-                            PipTouchHandler.this::updateDismissFraction /* updateAction */);
-                    return true;
-                }
-            }
 
             if (touchState.isDragging()) {
                 Runnable endAction = null;
@@ -749,14 +902,6 @@
     }
 
     /**
-     * Removes the dismiss target and cancels any pending callbacks to show it.
-     */
-    private void cleanUpDismissTarget() {
-        mHandler.removeCallbacks(mShowDismissAffordance);
-        mDismissViewController.destroyDismissTarget();
-    }
-
-    /**
      * @return whether the menu will resize as a part of showing the full menu.
      */
     private boolean willResizeMenu() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index e3f65ef..dc286c1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -92,7 +92,7 @@
 
                 // Initialize the velocity tracker
                 initOrResetVelocityTracker();
-                addMovement(ev);
+                addMovementToVelocityTracker(ev);
 
                 mActivePointerId = ev.getPointerId(0);
                 if (DEBUG) {
@@ -120,7 +120,7 @@
                 }
 
                 // Update the velocity tracker
-                addMovement(ev);
+                addMovementToVelocityTracker(ev);
                 int pointerIndex = ev.findPointerIndex(mActivePointerId);
                 if (pointerIndex == -1) {
                     Log.e(TAG, "Invalid active pointer id on MOVE: " + mActivePointerId);
@@ -151,7 +151,7 @@
                 }
 
                 // Update the velocity tracker
-                addMovement(ev);
+                addMovementToVelocityTracker(ev);
 
                 int pointerIndex = ev.getActionIndex();
                 int pointerId = ev.getPointerId(pointerIndex);
@@ -174,7 +174,7 @@
                 }
 
                 // Update the velocity tracker
-                addMovement(ev);
+                addMovementToVelocityTracker(ev);
                 mVelocityTracker.computeCurrentVelocity(1000,
                         mViewConfig.getScaledMaximumFlingVelocity());
                 mVelocity.set(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
@@ -318,6 +318,20 @@
         return -1;
     }
 
+    void addMovementToVelocityTracker(MotionEvent event) {
+        if (mVelocityTracker == null) {
+            return;
+        }
+
+        // Add movement to velocity tracker using raw screen X and Y coordinates instead
+        // of window coordinates because the window frame may be moving at the same time.
+        float deltaX = event.getRawX() - event.getX();
+        float deltaY = event.getRawY() - event.getY();
+        event.offsetLocation(deltaX, deltaY);
+        mVelocityTracker.addMovement(event);
+        event.offsetLocation(-deltaX, -deltaY);
+    }
+
     private void initOrResetVelocityTracker() {
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
@@ -333,16 +347,6 @@
         }
     }
 
-    private void addMovement(MotionEvent event) {
-        // Add movement to velocity tracker using raw screen X and Y coordinates instead
-        // of window coordinates because the window frame may be moving at the same time.
-        float deltaX = event.getRawX() - event.getX();
-        float deltaY = event.getRawY() - event.getY();
-        event.offsetLocation(deltaX, deltaY);
-        mVelocityTracker.addMovement(event);
-        event.offsetLocation(-deltaX, -deltaY);
-    }
-
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
index 9c175bc..8efeef1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
@@ -30,6 +30,14 @@
  */
 public class PipControlsView extends LinearLayout {
 
+    public PipControlsView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
     public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(
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 52c8960..f7a05d4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -334,6 +334,8 @@
      * Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
      */
     public void showPictureInPictureMenu() {
+        if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), current state=" + getStateDescription());
+
         if (getState() == STATE_PIP) {
             resizePinnedStack(STATE_PIP_MENU);
         }
@@ -343,10 +345,18 @@
      * Closes PIP (PIPed activity and PIP system UI).
      */
     public void closePip() {
+        if (DEBUG) Log.d(TAG, "closePip(), current state=" + getStateDescription());
+
         closePipInternal(true);
     }
 
     private void closePipInternal(boolean removePipStack) {
+        if (DEBUG) {
+            Log.d(TAG,
+                    "closePipInternal() removePipStack=" + removePipStack + ", current state="
+                            + getStateDescription());
+        }
+
         mState = STATE_NO_PIP;
         mPipTaskId = TASK_ID_NO_PIP;
         mPipMediaController = null;
@@ -371,6 +381,8 @@
      * Moves the PIPed activity to the fullscreen and closes PIP system UI.
      */
     void movePipToFullscreen() {
+        if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription());
+
         mPipTaskId = TASK_ID_NO_PIP;
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             mListeners.get(i).onMoveToFullscreen();
@@ -386,6 +398,7 @@
     public void suspendPipResizing(int reason) {
         if (DEBUG) Log.d(TAG,
                 "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
+
         mSuspendPipResizingReason |= reason;
     }
 
@@ -408,7 +421,11 @@
      * @param state In Pip state also used to determine the new size for the Pip.
      */
     void resizePinnedStack(int state) {
-        if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state, new Exception());
+        if (DEBUG) {
+            Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state="
+                    + getStateDescription(), new Exception());
+        }
+
         boolean wasStateNoPip = (mState == STATE_NO_PIP);
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             mListeners.get(i).onPipResizeAboutToStart();
@@ -418,7 +435,7 @@
             if (DEBUG) Log.d(TAG, "resizePinnedStack() deferring"
                     + " mSuspendPipResizingReason=" + mSuspendPipResizingReason
                     + " mResumeResizePinnedStackRunnableState="
-                    + mResumeResizePinnedStackRunnableState);
+                    + stateToName(mResumeResizePinnedStackRunnableState));
             return;
         }
         mState = state;
@@ -458,7 +475,8 @@
      * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
      */
     private void showPipMenu() {
-        if (DEBUG) Log.d(TAG, "showPipMenu()");
+        if (DEBUG) Log.d(TAG, "showPipMenu(), current state=" + getStateDescription());
+
         mState = STATE_PIP_MENU;
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             mListeners.get(i).onShowPipMenu();
@@ -712,6 +730,7 @@
 
     private void onPipTransitionFinishedOrCanceled() {
         if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()");
+
         if (getState() == STATE_PIP_MENU) {
             showPipMenu();
         }
@@ -753,4 +772,28 @@
             WindowManagerWrapper.getInstance().setPipVisibility(visible);
         });
     }
+
+    private String getStateDescription() {
+        if (mSuspendPipResizingReason == 0) {
+            return stateToName(mState);
+        }
+        return stateToName(mResumeResizePinnedStackRunnableState) + " (while " + stateToName(mState)
+                + " is suspended)";
+    }
+
+    private static String stateToName(int state) {
+        switch (state) {
+            case STATE_NO_PIP:
+                return "NO_PIP";
+
+            case STATE_PIP:
+                return "PIP";
+
+            case STATE_PIP_MENU:
+                return "PIP_MENU";
+
+            default:
+                return "UNKNOWN(" + state + ")";
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index f43f8e7..c7e77cc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
+import android.util.Log;
 
 import com.android.systemui.R;
 import com.android.systemui.pip.tv.dagger.TvPipComponent;
@@ -34,6 +35,7 @@
  * Activity to show the PIP menu to control PIP.
  */
 public class PipMenuActivity extends Activity implements PipManager.Listener {
+    private static final boolean DEBUG = false;
     private static final String TAG = "PipMenuActivity";
 
     static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
@@ -47,7 +49,6 @@
     private boolean mRestorePipSizeWhenClose;
     private PipControlsViewController mPipControlsViewController;
 
-
     @Inject
     public PipMenuActivity(TvPipComponent.Builder pipComponentBuilder, PipManager pipManager) {
         super();
@@ -57,6 +58,8 @@
 
     @Override
     protected void onCreate(Bundle bundle) {
+        if (DEBUG) Log.d(TAG, "onCreate()");
+
         super.onCreate(bundle);
         if (!mPipManager.isPipShown()) {
             finish();
@@ -81,13 +84,18 @@
 
     @Override
     protected void onNewIntent(Intent intent) {
+        if (DEBUG) Log.d(TAG, "onNewIntent(), intent=" + intent);
         super.onNewIntent(intent);
 
         onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS));
     }
 
     private void restorePipAndFinish() {
+        if (DEBUG) Log.d(TAG, "restorePipAndFinish()");
+
         if (mRestorePipSizeWhenClose) {
+            if (DEBUG) Log.d(TAG, "   > restoring to the default position");
+
             // When PIP menu activity is closed, restore to the default position.
             mPipManager.resizePinnedStack(PipManager.STATE_PIP);
         }
@@ -96,12 +104,16 @@
 
     @Override
     public void onResume() {
+        if (DEBUG) Log.d(TAG, "onResume()");
+
         super.onResume();
         mFadeInAnimation.start();
     }
 
     @Override
     public void onPause() {
+        if (DEBUG) Log.d(TAG, "onPause()");
+
         super.onPause();
         mFadeOutAnimation.start();
         restorePipAndFinish();
@@ -109,6 +121,8 @@
 
     @Override
     protected void onDestroy() {
+        if (DEBUG) Log.d(TAG, "onDestroy()");
+
         super.onDestroy();
         mPipManager.removeListener(this);
         mPipManager.resumePipResizing(
@@ -117,29 +131,41 @@
 
     @Override
     public void onBackPressed() {
+        if (DEBUG) Log.d(TAG, "onBackPressed()");
+
         restorePipAndFinish();
     }
 
     @Override
-    public void onPipEntered() { }
+    public void onPipEntered() {
+        if (DEBUG) Log.d(TAG, "onPipEntered()");
+    }
 
     @Override
     public void onPipActivityClosed() {
+        if (DEBUG) Log.d(TAG, "onPipActivityClosed()");
+
         finish();
     }
 
     @Override
     public void onPipMenuActionsChanged(ParceledListSlice actions) {
+        if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()");
+
         boolean hasCustomActions = actions != null && !actions.getList().isEmpty();
         mPipControlsViewController.setActions(
                 hasCustomActions ? actions.getList() : Collections.EMPTY_LIST);
     }
 
     @Override
-    public void onShowPipMenu() { }
+    public void onShowPipMenu() {
+        if (DEBUG) Log.d(TAG, "onShowPipMenu()");
+    }
 
     @Override
     public void onMoveToFullscreen() {
+        if (DEBUG) Log.d(TAG, "onMoveToFullscreen()");
+
         // Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds.
         // This conflicts with restoring PIP position, so disable it.
         mRestorePipSizeWhenClose = false;
@@ -148,8 +174,17 @@
 
     @Override
     public void onPipResizeAboutToStart() {
+        if (DEBUG) Log.d(TAG, "onPipResizeAboutToStart()");
+
         finish();
         mPipManager.suspendPipResizing(
                 PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
     }
+
+    @Override
+    public void finish() {
+        if (DEBUG) Log.d(TAG, "finish()", new RuntimeException());
+
+        super.finish();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 8fa64d3..e6876bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -66,7 +66,10 @@
     private int mMaxTiles;
     protected QSPanel mFullPanel;
     private QuickQSMediaPlayer mMediaPlayer;
+    /** Whether or not the QS media player feature is enabled. */
     private boolean mUsingMediaPlayer;
+    /** Whether or not the QuickQSPanel currently contains a media player. */
+    private boolean mHasMediaPlayer;
     private LinearLayout mHorizontalLinearLayout;
 
     // Only used with media
@@ -185,8 +188,8 @@
 
     boolean switchTileLayout() {
         if (!mUsingMediaPlayer) return false;
-        if (mMediaPlayer.hasMediaSession()
-                && mHorizontalLinearLayout.getVisibility() == View.GONE) {
+        mHasMediaPlayer = mMediaPlayer.hasMediaSession();
+        if (mHasMediaPlayer && mHorizontalLinearLayout.getVisibility() == View.GONE) {
             mHorizontalLinearLayout.setVisibility(View.VISIBLE);
             ((View) mRegularTileLayout).setVisibility(View.GONE);
             mTileLayout.setListening(false);
@@ -198,8 +201,7 @@
             if (mHost != null) setTiles(mHost.getTiles());
             mTileLayout.setListening(mListening);
             return true;
-        } else if (!mMediaPlayer.hasMediaSession()
-                && mHorizontalLinearLayout.getVisibility() == View.VISIBLE) {
+        } else if (!mHasMediaPlayer && mHorizontalLinearLayout.getVisibility() == View.VISIBLE) {
             mHorizontalLinearLayout.setVisibility(View.GONE);
             ((View) mRegularTileLayout).setVisibility(View.VISIBLE);
             mTileLayout.setListening(false);
@@ -215,8 +217,9 @@
         return false;
     }
 
-    public boolean hasMediaPlayerSession() {
-        return mMediaPlayer.hasMediaSession();
+    /** Returns true if this panel currently contains a media player. */
+    public boolean hasMediaPlayer() {
+        return mHasMediaPlayer;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 11b625f..e1ffad4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -344,7 +344,7 @@
         if (mQsDisabled) {
             lp.height = resources.getDimensionPixelSize(
                     com.android.internal.R.dimen.quick_qs_offset_height);
-        } else if (useQsMediaPlayer(mContext) && mHeaderQsPanel.hasMediaPlayerSession()) {
+        } else if (useQsMediaPlayer(mContext) && mHeaderQsPanel.hasMediaPlayer()) {
             lp.height = Math.max(getMinimumHeight(),
                     resources.getDimensionPixelSize(
                             com.android.internal.R.dimen.quick_qs_total_height_with_media));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 4f90527..447f48b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -29,6 +31,7 @@
 import com.android.systemui.R;
 import com.android.systemui.qs.PseudoGridView;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+
 /**
  * Quick settings detail view for user switching.
  */
@@ -101,6 +104,19 @@
             return v;
         }
 
+        private static Drawable getDrawable(Context context,
+                UserSwitcherController.UserRecord item) {
+            Drawable icon = getIconDrawable(context, item);
+            int iconColorRes = item.isCurrent ? R.color.qs_user_switcher_selected_avatar_icon_color
+                    : R.color.qs_user_switcher_avatar_icon_color;
+            icon.setTint(context.getResources().getColor(iconColorRes, context.getTheme()));
+
+            int bgRes = item.isCurrent ? R.drawable.bg_avatar_selected : R.drawable.qs_bg_avatar;
+            Drawable bg = context.getDrawable(bgRes);
+            LayerDrawable drawable = new LayerDrawable(new Drawable[]{bg, icon});
+            return drawable;
+        }
+
         @Override
         public void onClick(View view) {
             UserSwitcherController.UserRecord tag =
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3879c16..366ef93 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -21,6 +21,7 @@
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 
+import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -32,10 +33,8 @@
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Message;
 import android.util.AttributeSet;
 import android.util.Slog;
-import android.view.Choreographer;
 import android.view.Display;
 import android.view.InsetsState;
 import android.view.MotionEvent;
@@ -57,12 +56,12 @@
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
-import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -111,8 +110,6 @@
     private static final Interpolator IME_ADJUST_INTERPOLATOR =
             new PathInterpolator(0.2f, 0f, 0.1f, 1f);
 
-    private static final int MSG_RESIZE_STACK = 0;
-
     private DividerHandleView mHandle;
     private View mBackground;
     private MinimizedDockShadow mMinimizedShadow;
@@ -149,6 +146,7 @@
     private SplitDisplayLayout mSplitLayout;
     private DividerCallbacks mCallback;
     private final Rect mStableInsets = new Rect();
+    private final AnimationHandler mAnimationHandler = new AnimationHandler();
 
     private boolean mGrowRecents;
     private ValueAnimator mCurrentAnimator;
@@ -159,7 +157,6 @@
     private boolean mHomeStackResizable;
     private boolean mAdjustedForIme;
     private DividerState mState;
-    private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
 
     private SplitScreenTaskOrganizer mTiles;
     boolean mFirstLayout = true;
@@ -173,18 +170,7 @@
     // used interact with keyguard.
     private boolean mSurfaceHidden = false;
 
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_RESIZE_STACK:
-                    resizeStackSurfaces(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
-                    break;
-                default:
-                    super.handleMessage(msg);
-            }
-        }
-    };
+    private final Handler mHandler = new Handler();
 
     private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
         @Override
@@ -277,11 +263,10 @@
     public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mSfChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, context.getDisplay(),
-                Choreographer.getInstance());
         final DisplayManager displayManager =
                 (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
         mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+        mAnimationHandler.setProvider(new SfVsyncFrameCallbackProvider());
     }
 
     @Override
@@ -326,7 +311,6 @@
     void onDividerRemoved() {
         mRemoved = true;
         mCallback = null;
-        mHandler.removeMessages(MSG_RESIZE_STACK);
     }
 
     @Override
@@ -548,7 +532,7 @@
                 if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
                     SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget(
                             mStartPosition, 0 /* velocity */, false /* hardDismiss */);
-                    resizeStackDelayed(calculatePosition(x, y), mStartPosition, snapTarget);
+                    resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -633,7 +617,7 @@
         if (DEBUG) Slog.d(TAG, "Getting fling " + position + "->" + snapTarget.position);
         final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE;
         ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
-        anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(),
+        anim.addUpdateListener(animation -> resizeStackSurfaces((int) animation.getAnimatedValue(),
                 taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
                         ? TASK_POSITION_SAME
                         : snapTarget.taskPosition,
@@ -682,7 +666,6 @@
 
             @Override
             public void onAnimationCancel(Animator animation) {
-                mHandler.removeMessages(MSG_RESIZE_STACK);
                 mCancelled = true;
             }
 
@@ -693,8 +676,6 @@
                     delay = endDelay;
                 } else if (mCancelled) {
                     delay = 0;
-                } else if (mSfChoreographer.getSurfaceFlingerOffsetMs() > 0) {
-                    delay = mSfChoreographer.getSurfaceFlingerOffsetMs();
                 }
                 if (delay == 0) {
                     endAction.accept(mCancelled);
@@ -705,6 +686,7 @@
                 }
             }
         });
+        anim.setAnimationHandler(mAnimationHandler);
         mCurrentAnimator = anim;
         return anim;
     }
@@ -1047,13 +1029,6 @@
                 mDividerSize);
     }
 
-    public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) {
-        Message message = mHandler.obtainMessage(MSG_RESIZE_STACK, position, taskPosition,
-                taskSnapTarget);
-        message.setAsynchronous(true);
-        mSfChoreographer.scheduleAtSfVsync(mHandler, message);
-    }
-
     private void resizeStackSurfaces(SnapTarget taskSnapTarget) {
         resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 94afde78..2419515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -322,10 +322,10 @@
         default void suppressAmbientDisplay(boolean suppress) { }
 
         /**
-         * @see IStatusBar#showToast(String, IBinder, CharSequence, IBinder, int,
+         * @see IStatusBar#showToast(int, String, IBinder, CharSequence, IBinder, int,
          * ITransientNotificationCallback)
          */
-        default void showToast(String packageName, IBinder token, CharSequence text,
+        default void showToast(int uid, String packageName, IBinder token, CharSequence text,
                 IBinder windowToken, int duration,
                 @Nullable ITransientNotificationCallback callback) { }
 
@@ -798,7 +798,7 @@
     }
 
     @Override
-    public void showToast(String packageName, IBinder token, CharSequence text,
+    public void showToast(int uid, String packageName, IBinder token, CharSequence text,
             IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
@@ -807,7 +807,8 @@
             args.arg3 = text;
             args.arg4 = windowToken;
             args.arg5 = callback;
-            args.argi1 = duration;
+            args.argi1 = uid;
+            args.argi2 = duration;
             mHandler.obtainMessage(MSG_SHOW_TOAST, args).sendToTarget();
         }
     }
@@ -1276,9 +1277,10 @@
                     IBinder windowToken = (IBinder) args.arg4;
                     ITransientNotificationCallback callback =
                             (ITransientNotificationCallback) args.arg5;
-                    int duration = args.argi1;
+                    int uid = args.argi1;
+                    int duration = args.argi2;
                     for (Callbacks callbacks : mCallbacks) {
-                        callbacks.showToast(packageName, token, text, windowToken, duration,
+                        callbacks.showToast(uid, packageName, token, text, windowToken, duration,
                                 callback);
                     }
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index fd44f04..729f934 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -21,6 +21,7 @@
 import android.animation.ValueAnimator
 import android.app.WallpaperManager
 import android.util.Log
+import android.util.MathUtils
 import android.view.Choreographer
 import android.view.View
 import androidx.annotation.VisibleForTesting
@@ -37,6 +38,7 @@
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController
 import com.android.systemui.statusbar.phone.PanelExpansionListener
+import com.android.systemui.statusbar.phone.ScrimController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import java.io.FileDescriptor
 import java.io.PrintWriter
@@ -74,6 +76,7 @@
     var shadeSpring = DepthAnimation()
     @VisibleForTesting
     var globalActionsSpring = DepthAnimation()
+    var showingHomeControls: Boolean = false
 
     @VisibleForTesting
     var brightnessMirrorSpring = DepthAnimation()
@@ -105,6 +108,16 @@
         }
 
     /**
+     * Force stop blur effect when necessary.
+     */
+    private var scrimsVisible: Boolean = false
+        set(value) {
+            if (field == value) return
+            field = value
+            scheduleUpdate()
+        }
+
+    /**
      * Blur radius of the wake-up animation on this frame.
      */
     private var wakeAndUnlockBlurRadius = 0
@@ -133,7 +146,20 @@
                 shadeRadius = 0f
             }
         }
-        val blur = max(shadeRadius.toInt(), globalActionsSpring.radius)
+
+        // Home controls have black background, this means that we should not have blur when they
+        // are fully visible, otherwise we'll enter Client Composition unnecessarily.
+        var globalActionsRadius = globalActionsSpring.radius
+        if (showingHomeControls) {
+            globalActionsRadius = 0
+        }
+        var blur = max(shadeRadius.toInt(), globalActionsRadius)
+
+        // Make blur be 0 if it is necessary to stop blur effect.
+        if (scrimsVisible) {
+            blur = 0
+        }
+
         blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
         try {
             wallpaperManager.setWallpaperZoomOut(root.windowToken,
@@ -193,6 +219,10 @@
                 brightnessMirrorSpring.finishIfRunning()
             }
         }
+
+        override fun onDozeAmountChanged(linear: Float, eased: Float) {
+            wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased)
+        }
     }
 
     init {
@@ -201,6 +231,10 @@
             keyguardStateController.addCallback(keyguardStateCallback)
         }
         statusBarStateController.addCallback(statusBarStateCallback)
+        notificationShadeWindowController.setScrimsVisibilityListener {
+            // Stop blur effect when scrims is opaque to avoid unnecessary GPU composition.
+            visibility -> scrimsVisible = visibility == ScrimController.OPAQUE
+        }
     }
 
     /**
@@ -216,8 +250,12 @@
 
     private fun updateShadeBlur() {
         var newBlur = 0
-        if (statusBarStateController.state == StatusBarState.SHADE) {
-            newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
+        val state = statusBarStateController.state
+        if (state == StatusBarState.SHADE || state == StatusBarState.SHADE_LOCKED) {
+            val animatedBlur =
+                    Interpolators.SHADE_ANIMATION.getInterpolation(
+                            MathUtils.constrain(shadeExpansion / 0.15f, 0f, 1f))
+            newBlur = blurUtils.blurRadiusOfRatio(0.35f * animatedBlur + 0.65f * shadeExpansion)
         }
         shadeSpring.animateTo(newBlur)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 1696f07..53ec570 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -103,12 +103,20 @@
             override fun onEntryInflated(entry: NotificationEntry) {
                 if (!entry.ranking.isConversation) return
                 fun updateCount(isExpanded: Boolean) {
-                    if (isExpanded && !notifPanelCollapsed) {
+                    if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded())) {
                         resetCount(entry.key)
                         entry.row?.let(::resetBadgeUi)
                     }
                 }
-                entry.row?.setOnExpansionChangedListener(::updateCount)
+                entry.row?.setOnExpansionChangedListener { isExpanded ->
+                    if (entry.row?.isShown == true && isExpanded) {
+                        entry.row.performOnIntrinsicHeightReached {
+                            updateCount(isExpanded)
+                        }
+                    } else {
+                        updateCount(isExpanded)
+                    }
+                }
                 updateCount(entry.row?.isExpanded == true)
             }
 
@@ -169,7 +177,8 @@
 
     private fun resetBadgeUi(row: ExpandableNotificationRow): Unit =
             (row.layouts?.asSequence() ?: emptySequence())
-                    .mapNotNull { layout -> layout.contractedChild as? ConversationLayout }
+                    .flatMap { layout -> layout.allViews.asSequence()}
+                    .mapNotNull { view -> view as? ConversationLayout }
                     .forEach { convoLayout -> convoLayout.setUnreadCount(0) }
 
     private data class ConversationState(val unreadCount: Int, val notification: Notification)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index dd7be27..c1ba26d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -601,6 +601,13 @@
         return row != null && row.isPinned();
     }
 
+    /**
+     * Is this entry pinned and was expanded while doing so
+     */
+    public boolean isPinnedAndExpanded() {
+        return row != null && row.isPinnedAndExpanded();
+    }
+
     public void setRowPinned(boolean pinned) {
         if (row != null) row.setPinned(pinned);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 88cca43..5879c15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -83,9 +83,9 @@
 
     private val Ranking.personTypeInfo
         get() = when {
+            !isConversation -> TYPE_NON_PERSON
             channel?.isImportantConversation == true -> TYPE_IMPORTANT_PERSON
-            isConversation -> TYPE_PERSON
-            else -> TYPE_NON_PERSON
+            else -> TYPE_PERSON
         }
 
     private fun extractPersonTypeInfo(sbn: StatusBarNotification) =
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 2917346..5c578df 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
@@ -284,6 +284,12 @@
                 if (isPinned()) {
                     nowExpanded = !mExpandedWhenPinned;
                     mExpandedWhenPinned = nowExpanded;
+                    // Also notify any expansion changed listeners. This is necessary since the
+                    // expansion doesn't actually change (it's already system expanded) but it
+                    // changes visually
+                    if (mExpansionChangedListener != null) {
+                        mExpansionChangedListener.onExpansionChanged(nowExpanded);
+                    }
                 } else {
                     nowExpanded = !isExpanded();
                     setUserExpanded(nowExpanded);
@@ -326,6 +332,7 @@
     private NotificationInlineImageResolver mImageResolver;
     private NotificationMediaManager mMediaManager;
     @Nullable private OnExpansionChangedListener mExpansionChangedListener;
+    @Nullable private Runnable mOnIntrinsicHeightReachedRunnable;
 
     private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
             new SystemNotificationAsyncTask();
@@ -358,6 +365,16 @@
         return Arrays.copyOf(mLayouts, mLayouts.length);
     }
 
+    /**
+     * Is this entry pinned and was expanded while doing so
+     */
+    public boolean isPinnedAndExpanded() {
+        if (!isPinned()) {
+            return false;
+        }
+        return mExpandedWhenPinned;
+    }
+
     @Override
     public boolean isGroupExpansionChanging() {
         if (isChildInGroup()) {
@@ -2690,6 +2707,7 @@
         if (mMenuRow != null && mMenuRow.getMenuView() != null) {
             mMenuRow.onParentHeightUpdate();
         }
+        handleIntrinsicHeightReached();
     }
 
     @Override
@@ -2907,6 +2925,24 @@
         mExpansionChangedListener = listener;
     }
 
+    /**
+     * Perform an action when the notification height has reached its intrinsic height.
+     *
+     * @param runnable the runnable to run
+     */
+    public void performOnIntrinsicHeightReached(@Nullable Runnable runnable) {
+        mOnIntrinsicHeightReachedRunnable = runnable;
+        handleIntrinsicHeightReached();
+    }
+
+    private void handleIntrinsicHeightReached() {
+        if (mOnIntrinsicHeightReachedRunnable != null
+                && getActualHeight() == getIntrinsicHeight()) {
+            mOnIntrinsicHeightReachedRunnable.run();
+            mOnIntrinsicHeightReachedRunnable = null;
+        }
+    }
+
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
index 893e849..e4e3ebc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java
@@ -151,9 +151,12 @@
      * the real work once rather than repeatedly start and cancel it.
      */
     private void requestPipelineRun(NotificationEntry entry) {
-        mLogger.logRequestPipelineRun(entry.getKey());
-
         final BindEntry bindEntry = getBindEntry(entry);
+        if (bindEntry.row == null) {
+            // Row is not managed yet but may be soon. Stop for now.
+            return;
+        }
+        mLogger.logRequestPipelineRun(entry.getKey());
 
         // Abort any existing pipeline run
         mStage.abortStage(entry, bindEntry.row);
@@ -177,10 +180,6 @@
 
         final BindEntry bindEntry = mBindEntries.get(entry);
         final ExpandableNotificationRow row = bindEntry.row;
-        if (row == null) {
-            // Row is not managed yet but may be soon. Stop for now.
-            return;
-        }
 
         mStage.executeStage(entry, row, (en) -> onPipelineComplete(en));
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index b18bf01..3c3f1b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -988,6 +989,14 @@
         }
     }
 
+    public @NonNull View[] getAllViews() {
+        return new View[] {
+                mContractedChild,
+                mHeadsUpChild,
+                mExpandedChild,
+                mSingleLineView };
+    }
+
     public NotificationViewWrapper getVisibleWrapper(int visibleType) {
         switch (visibleType) {
             case VISIBLE_TYPE_EXPANDED:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index f06cfec..82e02b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -203,7 +203,9 @@
             Log.wtf(TAG, "onFullyShown when view was null");
         } else {
             mKeyguardView.onResume();
-            mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode());
+            if (mRoot != null) {
+                mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode());
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index d70484e..a19d35a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -50,19 +50,22 @@
     static final int STATE_BIOMETRICS_ERROR = 3;
     private float mDozeAmount;
     private int mIconColor;
-    private StateProvider mStateProvider;
     private int mOldState;
+    private int mState;
     private boolean mPulsing;
     private boolean mDozing;
     private boolean mKeyguardJustShown;
+    private boolean mPredrawRegistered;
     private final SparseArray<Drawable> mDrawableCache = new SparseArray<>();
 
     private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() {
         @Override
         public boolean onPreDraw() {
             getViewTreeObserver().removeOnPreDrawListener(this);
+            mPredrawRegistered = false;
 
-            int newState = mStateProvider.getState();
+            int newState = mState;
+            mOldState = mState;
             Drawable icon = getIcon(newState);
             setImageDrawable(icon, false);
 
@@ -80,7 +83,7 @@
                             @Override
                             public void onAnimationEnd(Drawable drawable) {
                                 if (getDrawable() == animation
-                                        && newState == mStateProvider.getState()
+                                        && newState == mState
                                         && newState == STATE_SCANNING_FACE) {
                                     animation.start();
                                 } else {
@@ -100,10 +103,6 @@
         super(context, attrs);
     }
 
-    void setStateProvider(StateProvider stateProvider) {
-        mStateProvider = stateProvider;
-    }
-
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
@@ -135,13 +134,16 @@
         return false;
     }
 
-    void update(int oldState, boolean pulsing, boolean dozing, boolean keyguardJustShown) {
-        mOldState = oldState;
+    void update(int newState, boolean pulsing, boolean dozing, boolean keyguardJustShown) {
+        mState = newState;
         mPulsing = pulsing;
         mDozing = dozing;
         mKeyguardJustShown = keyguardJustShown;
 
-        getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
+        if (!mPredrawRegistered) {
+            mPredrawRegistered = true;
+            getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
+        }
     }
 
     void setDozeAmount(float dozeAmount) {
@@ -175,7 +177,7 @@
         return mDrawableCache.get(iconRes);
     }
 
-    static int getIconForState(int state) {
+    private static int getIconForState(int state) {
         int iconRes;
         switch (state) {
             case STATE_LOCKED:
@@ -196,7 +198,7 @@
         return iconRes;
     }
 
-    static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
+    private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
             boolean dozing, boolean keyguardJustShown) {
 
         // Never animate when screen is off
@@ -260,9 +262,4 @@
         }
         return LOCK_ANIM_RES_IDS[0][lockAnimIndex];
     }
-
-    interface StateProvider {
-        int getState();
-    }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index f7c861b..a633e19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -352,7 +352,6 @@
         mLockIcon.setOnClickListener(this::handleClick);
         mLockIcon.setOnLongClickListener(this::handleLongClick);
         mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
-        mLockIcon.setStateProvider(this::getState);
 
         if (mLockIcon.isAttachedToWindow()) {
             mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
@@ -462,7 +461,7 @@
             shouldUpdate = false;
         }
         if (shouldUpdate && mLockIcon != null) {
-            mLockIcon.update(mLastState, mPulsing, mDozing, mKeyguardJustShown);
+            mLockIcon.update(state, mPulsing, mDozing, mKeyguardJustShown);
         }
         mLastState = state;
         mKeyguardJustShown = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 4462c72..3105155 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -625,7 +625,9 @@
             // mOrientedHandle is initialized lazily
             mOrientationHandle.setVisibility(View.GONE);
         }
-        mNavigationBarView.setVisibility(View.VISIBLE);
+        if (mNavigationBarView != null) {
+            mNavigationBarView.setVisibility(View.VISIBLE);
+        }
     }
 
     private int deltaRotation(int oldRotation, int newRotation) {
@@ -635,7 +637,7 @@
     }
 
     private void updatedFixedRotation() {
-        mFixedRotationEnabled = Settings.Global.getInt(getContext().getContentResolver(),
+        mFixedRotationEnabled = Settings.Global.getInt(mContentResolver,
                 FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0;
         if (!canShowSecondaryHandle()) {
             resetSecondaryHandle();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index e1e679f..462b42a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -61,6 +61,7 @@
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.function.Consumer;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -92,6 +93,7 @@
     private final State mCurrentState = new State();
     private OtherwisedCollapsedListener mListener;
     private ForcePluginOpenListener mForcePluginOpenListener;
+    private Consumer<Integer> mScrimsVisibilityListener;
     private final ArrayList<WeakReference<StatusBarWindowCallback>>
             mCallbacks = Lists.newArrayList();
 
@@ -150,6 +152,16 @@
         mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
     }
 
+    /**
+     * Register a listener to monitor scrims visibility
+     * @param listener A listener to monitor scrims visibility
+     */
+    public void setScrimsVisibilityListener(Consumer<Integer> listener) {
+        if (listener != null && mScrimsVisibilityListener != listener) {
+            mScrimsVisibilityListener = listener;
+        }
+    }
+
     private boolean shouldEnableKeyguardScreenRotation() {
         Resources res = mContext.getResources();
         return SystemProperties.getBoolean("lockscreen.rot_override", false)
@@ -477,6 +489,7 @@
     public void setScrimsVisibility(int scrimsVisibility) {
         mCurrentState.mScrimsVisibility = scrimsVisibility;
         apply(mCurrentState);
+        mScrimsVisibilityListener.accept(scrimsVisibility);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index bf5900f..95b41a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -71,7 +71,7 @@
     protected boolean mPluggedIn;
     protected boolean mCharging;
     private boolean mCharged;
-    private boolean mPowerSave;
+    protected boolean mPowerSave;
     private boolean mAodPowerSave;
     private boolean mTestmode = false;
     private boolean mHasReceivedBattery = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 7d54981..45e47f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -21,6 +21,8 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -294,6 +296,21 @@
             return convertView;
         }
 
+        private static Drawable getDrawable(Context context,
+                UserSwitcherController.UserRecord item) {
+            Drawable drawable = getIconDrawable(context, item);
+            int iconColorRes = item.isCurrent ? R.color.kg_user_switcher_selected_avatar_icon_color
+                    : R.color.kg_user_switcher_avatar_icon_color;
+            drawable.setTint(context.getResources().getColor(iconColorRes, context.getTheme()));
+
+            if (item.isCurrent) {
+                Drawable bg = context.getDrawable(R.drawable.bg_avatar_selected);
+                drawable = new LayerDrawable(new Drawable[]{bg, drawable});
+            }
+
+            return drawable;
+        }
+
         @Override
         public void onClick(View v) {
             UserSwitcherController.UserRecord user = (UserSwitcherController.UserRecord) v.getTag();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 69eaaa4..bb0b5e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -31,7 +31,6 @@
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
-import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -52,7 +51,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.RestrictedLockUtilsInternal;
-import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.Prefs;
@@ -684,18 +682,17 @@
             }
         }
 
-        public Drawable getDrawable(Context context, UserRecord item) {
+        protected static Drawable getIconDrawable(Context context, UserRecord item) {
+            int iconRes;
             if (item.isAddUser) {
-                return context.getDrawable(R.drawable.ic_add_circle_qs);
+                iconRes = R.drawable.ic_add_circle;
+            } else if (item.isGuest) {
+                iconRes = R.drawable.ic_avatar_guest_user;
+            } else {
+                iconRes = R.drawable.ic_avatar_user;
             }
-            Drawable icon = UserIcons.getDefaultUserIcon(
-                    context.getResources(), item.resolveId(), /* light= */ false);
-            if (item.isGuest) {
-                icon.setColorFilter(Utils.getColorAttrDefaultColor(context,
-                        android.R.attr.colorForeground),
-                        Mode.SRC_IN);
-            }
-            return icon;
+
+            return context.getDrawable(iconRes);
         }
 
         public void refresh() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 07985ab..02ae1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -27,6 +27,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -66,7 +67,8 @@
             // If the system process isn't there we're doomed anyway.
         }
 
-        new AudioRecordingDisclosureBar(mContext).start();
+        // Creating AudioRecordingDisclosureBar and just letting it run
+        new AudioRecordingDisclosureBar(mContext);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java
new file mode 100644
index 0000000..87b3956
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioActivityObserver.java
@@ -0,0 +1,44 @@
+/*
+ * 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.statusbar.tv.micdisclosure;
+
+import android.content.Context;
+
+import java.util.Set;
+
+/**
+ * A base class for implementing observers for different kinds of activities related to audio
+ * recording. These observers are to be initialized by {@link AudioRecordingDisclosureBar} and to
+ * report back to it.
+ */
+abstract class AudioActivityObserver {
+
+    interface OnAudioActivityStateChangeListener {
+        void onAudioActivityStateChange(boolean active, String packageName);
+    }
+
+    final Context mContext;
+
+    final OnAudioActivityStateChangeListener mListener;
+
+    AudioActivityObserver(Context context, OnAudioActivityStateChangeListener listener) {
+        mContext = context;
+        mListener = listener;
+    }
+
+    abstract Set<String> getActivePackages();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
index e70e30a..9148683 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.tv;
+package com.android.systemui.statusbar.tv.micdisclosure;
 
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
@@ -25,7 +25,6 @@
 import android.animation.PropertyValuesHolder;
 import android.annotation.IntDef;
 import android.annotation.UiThread;
-import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -40,6 +39,7 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.tv.TvStatusBar;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -54,9 +54,10 @@
  *
  * @see TvStatusBar
  */
-class AudioRecordingDisclosureBar {
-    private static final String TAG = "AudioRecordingDisclosureBar";
-    private static final boolean DEBUG = false;
+public class AudioRecordingDisclosureBar implements
+        AudioActivityObserver.OnAudioActivityStateChangeListener {
+    private static final String TAG = "AudioRecordingDisclosure";
+    static final boolean DEBUG = false;
 
     // This title is used to test the microphone disclosure indicator in
     // CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest
@@ -98,10 +99,12 @@
     private TextView mTextView;
 
     @State private int mState = STATE_NOT_SHOWN;
+
     /**
-     * Set of the applications that currently are conducting audio recording.
+     * Array of the observers that monitor different aspects of the system, such as AppOps and
+     * microphone foreground services
      */
-    private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>();
+    private final AudioActivityObserver[] mAudioActivityObservers;
     /**
      * Set of applications that we've notified the user about since the indicator came up. Meaning
      * that if an application is in this list then at some point since the indicator came up, it
@@ -119,29 +122,52 @@
      * one of the two aforementioned states.
      */
     private final Queue<String> mPendingNotificationPackages = new LinkedList<>();
+    /**
+     * Set of applications for which we make an exception and do not show the indicator. This gets
+     * populated once - in {@link #AudioRecordingDisclosureBar(Context)}.
+     */
+    private final Set<String> mExemptPackages;
 
-    AudioRecordingDisclosureBar(Context context) {
+    public AudioRecordingDisclosureBar(Context context) {
         mContext = context;
-    }
 
-    void start() {
-        // Register AppOpsManager callback
-        final AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService(
-                Context.APP_OPS_SERVICE);
-        appOpsManager.startWatchingActive(
-                new String[]{AppOpsManager.OPSTR_RECORD_AUDIO},
-                mContext.getMainExecutor(),
-                new OnActiveRecordingListener());
+        mExemptPackages = new ArraySet<>(
+                Arrays.asList(mContext.getResources().getStringArray(
+                        R.array.audio_recording_disclosure_exempt_apps)));
+
+        mAudioActivityObservers = new AudioActivityObserver[]{
+                new RecordAudioAppOpObserver(mContext, this),
+                new MicrophoneForegroundServicesObserver(mContext, this),
+        };
     }
 
     @UiThread
-    private void onStartedRecording(String packageName) {
-        if (!mActiveAudioRecordingPackages.add(packageName)) {
-            // This app is already known to perform recording
+    @Override
+    public void onAudioActivityStateChange(boolean active, String packageName) {
+        if (DEBUG) {
+            Log.d(TAG,
+                    "onAudioActivityStateChange, packageName=" + packageName + ", active="
+                            + active);
+        }
+
+        if (mExemptPackages.contains(packageName)) {
+            if (DEBUG) Log.d(TAG, "   - exempt package: ignoring");
             return;
         }
+
+        if (active) {
+            showIndicatorForPackageIfNeeded(packageName);
+        } else {
+            hideIndicatorIfNeeded();
+        }
+    }
+
+    @UiThread
+    private void showIndicatorForPackageIfNeeded(String packageName) {
+        if (DEBUG) Log.d(TAG, "showIndicatorForPackageIfNeeded, packageName=" + packageName);
         if (!mSessionNotifiedPackages.add(packageName)) {
             // We've already notified user about this app, no need to do it again.
+            if (DEBUG) Log.d(TAG, "   - already notified");
             return;
         }
 
@@ -167,23 +193,33 @@
     }
 
     @UiThread
-    private void onDoneRecording(String packageName) {
-        if (!mActiveAudioRecordingPackages.remove(packageName)) {
-            // Was not marked as an active recorder, do nothing
-            return;
+    private void hideIndicatorIfNeeded() {
+        if (DEBUG) Log.d(TAG, "hideIndicatorIfNeeded");
+        // If not MINIMIZED, will check whether the indicator should be hidden when the indicator
+        // comes to the STATE_MINIMIZED eventually.
+        if (mState != STATE_MINIMIZED) return;
+
+        // If is in the STATE_MINIMIZED, but there are other active recorders - simply ignore.
+        for (int index = mAudioActivityObservers.length - 1; index >= 0; index--) {
+            for (String activePackage : mAudioActivityObservers[index].getActivePackages()) {
+                if (mExemptPackages.contains(activePackage)) continue;
+                if (DEBUG) Log.d(TAG, "   - there are still ongoing activities");
+                return;
+            }
         }
 
-        // If not MINIMIZED, will check whether the indicator should be hidden when the indicator
-        // comes to the STATE_MINIMIZED eventually. If is in the STATE_MINIMIZED, but there are
-        // other active recorders - simply ignore.
-        if (mState == STATE_MINIMIZED && mActiveAudioRecordingPackages.isEmpty()) {
-            mSessionNotifiedPackages.clear();
-            hide();
-        }
+        // Clear the state and hide the indicator.
+        mSessionNotifiedPackages.clear();
+        hide();
     }
 
     @UiThread
     private void show(String packageName) {
+        final String label = getApplicationLabel(packageName);
+        if (DEBUG) {
+            Log.d(TAG, "Showing indicator for " + packageName + " (" + label + ")...");
+        }
+
         // Inflate the indicator view
         mIndicatorView = LayoutInflater.from(mContext).inflate(
                 R.layout.tv_audio_recording_indicator,
@@ -196,7 +232,6 @@
         mBgRight = mIndicatorView.findViewById(R.id.bg_right);
 
         // Set up the notification text
-        final String label = getApplicationLabel(packageName);
         mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
 
         // Initially change the visibility to INVISIBLE, wait until and receives the size and
@@ -260,6 +295,9 @@
     @UiThread
     private void expand(String packageName) {
         final String label = getApplicationLabel(packageName);
+        if (DEBUG) {
+            Log.d(TAG, "Expanding for " + packageName + " (" + label + ")...");
+        }
         mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
 
         final AnimatorSet set = new AnimatorSet();
@@ -283,6 +321,7 @@
 
     @UiThread
     private void minimize() {
+        if (DEBUG) Log.d(TAG, "Minimizing...");
         final int targetOffset = mTextsContainers.getWidth();
         final AnimatorSet set = new AnimatorSet();
         set.playTogether(
@@ -305,6 +344,7 @@
 
     @UiThread
     private void hide() {
+        if (DEBUG) Log.d(TAG, "Hiding...");
         final int targetOffset =
                 mIndicatorView.getWidth() - (int) mIconTextsContainer.getTranslationX();
         final AnimatorSet set = new AnimatorSet();
@@ -326,6 +366,7 @@
 
     @UiThread
     private void onExpanded() {
+        if (DEBUG) Log.d(TAG, "Expanded");
         mState = STATE_SHOWN;
 
         mIndicatorView.postDelayed(this::minimize, MAXIMIZED_DURATION);
@@ -333,20 +374,21 @@
 
     @UiThread
     private void onMinimized() {
+        if (DEBUG) Log.d(TAG, "Minimized");
         mState = STATE_MINIMIZED;
 
         if (!mPendingNotificationPackages.isEmpty()) {
             // There is a new application that started recording, tell the user about it.
             expand(mPendingNotificationPackages.poll());
-        } else if (mActiveAudioRecordingPackages.isEmpty()) {
-            // Nobody is recording anymore, clear state and remove the indicator.
-            mSessionNotifiedPackages.clear();
-            hide();
+        } else {
+            hideIndicatorIfNeeded();
         }
     }
 
     @UiThread
     private void onHidden() {
+        if (DEBUG) Log.d(TAG, "Hidden");
+
         final WindowManager windowManager = (WindowManager) mContext.getSystemService(
                 Context.WINDOW_SERVICE);
         windowManager.removeView(mIndicatorView);
@@ -392,35 +434,4 @@
         }
         return pm.getApplicationLabel(appInfo).toString();
     }
-
-    private class OnActiveRecordingListener implements AppOpsManager.OnOpActiveChangedListener {
-        private final Set<String> mExemptApps;
-
-        private OnActiveRecordingListener() {
-            mExemptApps = new ArraySet<>(Arrays.asList(mContext.getResources().getStringArray(
-                    R.array.audio_recording_disclosure_exempt_apps)));
-        }
-
-        @Override
-        public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
-            if (DEBUG) {
-                Log.d(TAG,
-                        "OP_RECORD_AUDIO active change, active=" + active + ", app="
-                                + packageName);
-            }
-
-            if (mExemptApps.contains(packageName)) {
-                if (DEBUG) {
-                    Log.d(TAG, "\t- exempt app");
-                }
-                return;
-            }
-
-            if (active) {
-                onStartedRecording(packageName);
-            } else {
-                onDoneRecording(packageName);
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java
new file mode 100644
index 0000000..1ede88a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/MicrophoneForegroundServicesObserver.java
@@ -0,0 +1,189 @@
+/*
+ * 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.statusbar.tv.micdisclosure;
+
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
+
+import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG;
+
+import android.annotation.UiThread;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.app.IProcessObserver;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The purpose of these class is to detect packages that are running foreground services of type
+ * 'microphone' and to report back to {@link AudioRecordingDisclosureBar}.
+ */
+class MicrophoneForegroundServicesObserver extends AudioActivityObserver {
+    private static final String TAG = "MicrophoneForegroundServicesObserver";
+    private static final boolean ENABLED = true;
+
+    private final IActivityManager mActivityManager;
+    /**
+     * A dictionary that maps PIDs to the package names. We only keep track of the PIDs that are
+     * "active" (those that are running FGS with FOREGROUND_SERVICE_TYPE_MICROPHONE flag).
+     */
+    private final SparseArray<String[]> mPidToPackages = new SparseArray<>();
+    /**
+     * A dictionary that maps "active" packages to the number of the "active" processes associated
+     * with those packages. We really only need this in case when one application is running in
+     * multiple processes, so that we don't lose track of the package when one of its "active"
+     * processes ceases, while others remain "active".
+     */
+    private final Map<String, Integer> mPackageToProcessCount = new ArrayMap<>();
+
+    MicrophoneForegroundServicesObserver(Context context,
+            OnAudioActivityStateChangeListener listener) {
+        super(context, listener);
+
+        mActivityManager = ActivityManager.getService();
+        try {
+            mActivityManager.registerProcessObserver(mProcessObserver);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't register process observer", e);
+        }
+    }
+
+    @Override
+    Set<String> getActivePackages() {
+        return ENABLED ? mPackageToProcessCount.keySet() : Collections.emptySet();
+    }
+
+    @UiThread
+    private void onProcessForegroundServicesChanged(int pid, boolean hasMicFgs) {
+        final String[] changedPackages;
+        if (hasMicFgs) {
+            if (mPidToPackages.contains(pid)) {
+                // We are already tracking this pid - ignore.
+                changedPackages = null;
+            } else {
+                changedPackages = getPackageNames(pid);
+                mPidToPackages.append(pid, changedPackages);
+            }
+        } else {
+            changedPackages = mPidToPackages.removeReturnOld(pid);
+        }
+
+        if (changedPackages == null) {
+            return;
+        }
+
+        for (int index = changedPackages.length - 1; index >= 0; index--) {
+            final String packageName = changedPackages[index];
+            int processCount = mPackageToProcessCount.getOrDefault(packageName, 0);
+            final boolean shouldNotify;
+            if (hasMicFgs) {
+                processCount++;
+                shouldNotify = processCount == 1;
+            } else {
+                processCount--;
+                shouldNotify = processCount == 0;
+            }
+            if (processCount > 0) {
+                mPackageToProcessCount.put(packageName, processCount);
+            } else {
+                mPackageToProcessCount.remove(packageName);
+            }
+            if (shouldNotify) notifyPackageStateChanged(packageName, hasMicFgs);
+        }
+    }
+
+    @UiThread
+    private void onProcessDied(int pid) {
+        final String[] packages = mPidToPackages.removeReturnOld(pid);
+        if (packages == null) {
+            // This PID was not active - ignore.
+            return;
+        }
+
+        for (int index = packages.length - 1; index >= 0; index--) {
+            final String packageName = packages[index];
+            int processCount = mPackageToProcessCount.getOrDefault(packageName, 0);
+            if (processCount <= 0) {
+                Log.e(TAG, "Bookkeeping error, process count for " + packageName + " is "
+                        + processCount);
+                continue;
+            }
+            processCount--;
+            if (processCount > 0) {
+                mPackageToProcessCount.put(packageName, processCount);
+            } else {
+                mPackageToProcessCount.remove(packageName);
+                notifyPackageStateChanged(packageName, false);
+            }
+        }
+    }
+
+    @UiThread
+    private void notifyPackageStateChanged(String packageName, boolean active) {
+        if (active) {
+            if (DEBUG) Log.d(TAG, "New microphone fgs detected, package=" + packageName);
+        } else {
+            if (DEBUG) Log.d(TAG, "Microphone fgs is gone, package=" + packageName);
+        }
+
+        if (ENABLED) mListener.onAudioActivityStateChange(active, packageName);
+    }
+
+    @UiThread
+    private String[] getPackageNames(int pid) {
+        final List<ActivityManager.RunningAppProcessInfo> runningApps;
+        try {
+            runningApps = mActivityManager.getRunningAppProcesses();
+        } catch (RemoteException e) {
+            Log.d(TAG, "Couldn't get package name for pid=" + pid);
+            return null;
+        }
+        if (runningApps == null) {
+            Log.wtf(TAG, "No running apps reported");
+        }
+        for (ActivityManager.RunningAppProcessInfo app : runningApps) {
+            if (app.pid == pid) {
+                return app.pkgList;
+            }
+        }
+        return null;
+    }
+
+    private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+        @Override
+        public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {}
+
+        @Override
+        public void onForegroundServicesChanged(int pid, int uid, int serviceTypes) {
+            mContext.getMainExecutor().execute(() -> onProcessForegroundServicesChanged(pid,
+                    (serviceTypes & FOREGROUND_SERVICE_TYPE_MICROPHONE) != 0));
+        }
+
+        @Override
+        public void onProcessDied(int pid, int uid) {
+            mContext.getMainExecutor().execute(
+                    () -> MicrophoneForegroundServicesObserver.this.onProcessDied(pid));
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java
new file mode 100644
index 0000000..b5b1c2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/RecordAudioAppOpObserver.java
@@ -0,0 +1,80 @@
+/*
+ * 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.statusbar.tv.micdisclosure;
+
+import static com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar.DEBUG;
+
+import android.annotation.UiThread;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Set;
+
+/**
+ * The purpose of these class is to detect packages that are conducting audio recording (according
+ * to {@link AppOpsManager}) and report this to {@link AudioRecordingDisclosureBar}.
+ */
+class RecordAudioAppOpObserver extends AudioActivityObserver implements
+        AppOpsManager.OnOpActiveChangedListener {
+    private static final String TAG = "RecordAudioAppOpObserver";
+
+    /**
+     * Set of the applications that currently are conducting audio recording according to {@link
+     * AppOpsManager}.
+     */
+    private final Set<String> mActiveAudioRecordingPackages = new ArraySet<>();
+
+    RecordAudioAppOpObserver(Context context, OnAudioActivityStateChangeListener listener) {
+        super(context, listener);
+
+        // Register AppOpsManager callback
+        final AppOpsManager appOpsManager = (AppOpsManager) mContext.getSystemService(
+                Context.APP_OPS_SERVICE);
+        appOpsManager.startWatchingActive(
+                new String[]{AppOpsManager.OPSTR_RECORD_AUDIO},
+                mContext.getMainExecutor(),
+                this);
+    }
+
+    @UiThread
+    @Override
+    Set<String> getActivePackages() {
+        return mActiveAudioRecordingPackages;
+    }
+
+    @UiThread
+    @Override
+    public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
+        if (DEBUG) {
+            Log.d(TAG,
+                    "OP_RECORD_AUDIO active change, active=" + active + ", package="
+                            + packageName);
+        }
+
+        if (active) {
+            if (mActiveAudioRecordingPackages.add(packageName)) {
+                mListener.onAudioActivityStateChange(true, packageName);
+            }
+        } else {
+            if (mActiveAudioRecordingPackages.remove(packageName)) {
+                mListener.onAudioActivityStateChange(false, packageName);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 9ccb9bf..9b465ae 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -24,10 +24,10 @@
 import android.content.res.Resources;
 import android.os.IBinder;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.View;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
 import android.widget.ToastPresenter;
 
 import com.android.internal.R;
@@ -48,9 +48,8 @@
     private static final String TAG = "ToastUI";
 
     private final CommandQueue mCommandQueue;
-    private final WindowManager mWindowManager;
     private final INotificationManager mNotificationManager;
-    private final AccessibilityManager mAccessibilityManager;
+    private final IAccessibilityManager mAccessibilityManager;
     private final int mGravity;
     private final int mY;
     @Nullable private ToastPresenter mPresenter;
@@ -59,18 +58,17 @@
     @Inject
     public ToastUI(Context context, CommandQueue commandQueue) {
         this(context, commandQueue,
-                (WindowManager) context.getSystemService(Context.WINDOW_SERVICE),
                 INotificationManager.Stub.asInterface(
                         ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
-                AccessibilityManager.getInstance(context));
+                IAccessibilityManager.Stub.asInterface(
+                        ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)));
     }
 
     @VisibleForTesting
-    ToastUI(Context context, CommandQueue commandQueue, WindowManager windowManager,
-            INotificationManager notificationManager, AccessibilityManager accessibilityManager) {
+    ToastUI(Context context, CommandQueue commandQueue, INotificationManager notificationManager,
+            @Nullable IAccessibilityManager accessibilityManager) {
         super(context);
         mCommandQueue = commandQueue;
-        mWindowManager = windowManager;
         mNotificationManager = notificationManager;
         mAccessibilityManager = accessibilityManager;
         Resources resources = mContext.getResources();
@@ -85,15 +83,16 @@
 
     @Override
     @MainThread
-    public void showToast(String packageName, IBinder token, CharSequence text,
+    public void showToast(int uid, String packageName, IBinder token, CharSequence text,
             IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) {
         if (mPresenter != null) {
             hideCurrentToast();
         }
-        View view = ToastPresenter.getTextToastView(mContext, text);
+        Context context = mContext.createContextAsUser(UserHandle.getUserHandleForUid(uid), 0);
+        View view = ToastPresenter.getTextToastView(context, text);
         mCallback = callback;
-        mPresenter = new ToastPresenter(mContext, mWindowManager, mAccessibilityManager,
-                mNotificationManager, packageName);
+        mPresenter = new ToastPresenter(context, mAccessibilityManager, mNotificationManager,
+                packageName);
         mPresenter.show(view, token, windowToken, duration, mGravity, 0, mY, 0, 0, mCallback);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
index 812a1e4..f27bdbf 100644
--- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
@@ -160,6 +160,18 @@
     lateinit var magnetListener: MagnetizedObject.MagnetListener
 
     /**
+     * Optional update listener to provide to the PhysicsAnimator that is used to spring the object
+     * into the target.
+     */
+    var physicsAnimatorUpdateListener: PhysicsAnimator.UpdateListener<T>? = null
+
+    /**
+     * Optional end listener to provide to the PhysicsAnimator that is used to spring the object
+     * into the target.
+     */
+    var physicsAnimatorEndListener: PhysicsAnimator.EndListener<T>? = null
+
+    /**
      * Sets whether forcefully flinging the object vertically towards a target causes it to be
      * attracted to the target and then released immediately, despite never being dragged within the
      * magnetic field.
@@ -479,6 +491,14 @@
                 .spring(yProperty, yProperty.getValue(underlyingObject) + yDiff, velY,
                         springConfig)
 
+        if (physicsAnimatorUpdateListener != null) {
+            animator.addUpdateListener(physicsAnimatorUpdateListener!!)
+        }
+
+        if (physicsAnimatorEndListener != null) {
+            animator.addEndListener(physicsAnimatorEndListener!!)
+        }
+
         if (after != null) {
             animator.withEndActions(after)
         }
@@ -560,13 +580,15 @@
         private val tempLoc = IntArray(2)
 
         fun updateLocationOnScreen() {
-            targetView.getLocationOnScreen(tempLoc)
+            targetView.post {
+                targetView.getLocationOnScreen(tempLoc)
 
-            // Add half of the target size to get the center, and subtract translation since the
-            // target could be animating in while we're doing this calculation.
-            centerOnScreen.set(
-                    tempLoc[0] + targetView.width / 2f - targetView.translationX,
-                    tempLoc[1] + targetView.height / 2f - targetView.translationY)
+                // Add half of the target size to get the center, and subtract translation since the
+                // target could be animating in while we're doing this calculation.
+                centerOnScreen.set(
+                        tempLoc[0] + targetView.width / 2f - targetView.translationX,
+                        tempLoc[1] + targetView.height / 2f - targetView.translationY)
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index bae5bb4..c22b718f 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -272,13 +272,18 @@
                     }
                     mAnimation.cancel();
                 }
-                mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
                 final float defaultY = mImeSourceControl.getSurfacePosition().y;
                 final float x = mImeSourceControl.getSurfacePosition().x;
                 final float hiddenY = defaultY + imeSource.getFrame().height();
                 final float shownY = defaultY;
                 final float startY = show ? hiddenY : shownY;
                 final float endY = show ? shownY : hiddenY;
+                if (mAnimationDirection == DIRECTION_NONE && mImeShowing && show) {
+                    // IME is already showing, so set seek to end
+                    seekValue = shownY;
+                    seek = true;
+                }
+                mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
                 mImeShowing = show;
                 mAnimation = ValueAnimator.ofFloat(startY, endY);
                 mAnimation.setDuration(
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index 381ccdb..e5da603 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -194,7 +194,9 @@
                 return;
             }
             final Display display = mDisplayController.getDisplay(mDisplayId);
-            SurfaceControlViewHost viewRoot = new SurfaceControlViewHost(mContext, display, wwm);
+            SurfaceControlViewHost viewRoot =
+                    new SurfaceControlViewHost(mContext, display, wwm,
+                            true /* useSfChoreographer */);
             attrs.flags |= FLAG_HARDWARE_ACCELERATED;
             viewRoot.setView(view, attrs);
             mViewRoots.put(view, viewRoot);
@@ -256,12 +258,13 @@
                 Rect outVisibleInsets, Rect outStableInsets,
                 DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
                 SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-                Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
+                InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
+                SurfaceControl outBLASTSurfaceControl) {
             int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight,
                     viewVisibility, flags, frameNumber, outFrame, outOverscanInsets,
                     outContentInsets, outVisibleInsets, outStableInsets,
                     cutout, mergedConfiguration, outSurfaceControl, outInsetsState,
-                    outSurfaceSize, outBLASTSurfaceControl);
+                    outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
             if (res != 0) {
                 return res;
             }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
index 072bc44..4bcf917 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
@@ -16,8 +16,12 @@
 
 package com.android.keyguard
 
+import android.app.Notification
 import android.graphics.drawable.Icon
 import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
@@ -28,7 +32,9 @@
 
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.media.MediaControllerFactory
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
@@ -38,6 +44,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.any
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
@@ -48,9 +55,12 @@
 public class KeyguardMediaPlayerTest : SysuiTestCase() {
 
     private lateinit var keyguardMediaPlayer: KeyguardMediaPlayer
+    @Mock private lateinit var mockMediaFactory: MediaControllerFactory
+    @Mock private lateinit var mockMediaController: MediaController
+    private lateinit var playbackState: PlaybackState
     private lateinit var fakeExecutor: FakeExecutor
     private lateinit var mediaMetadata: MediaMetadata.Builder
-    private lateinit var entry: NotificationEntryBuilder
+    private lateinit var entry: NotificationEntry
     @Mock private lateinit var mockView: View
     private lateinit var songView: TextView
     private lateinit var artistView: TextView
@@ -70,8 +80,16 @@
 
     @Before
     public fun setup() {
+        playbackState = PlaybackState.Builder().run {
+            build()
+        }
+        mockMediaController = mock(MediaController::class.java)
+        whenever(mockMediaController.getPlaybackState()).thenReturn(playbackState)
+        mockMediaFactory = mock(MediaControllerFactory::class.java)
+        whenever(mockMediaFactory.create(any())).thenReturn(mockMediaController)
+
         fakeExecutor = FakeExecutor(FakeSystemClock())
-        keyguardMediaPlayer = KeyguardMediaPlayer(context, fakeExecutor)
+        keyguardMediaPlayer = KeyguardMediaPlayer(context, mockMediaFactory, fakeExecutor)
         mockIcon = mock(Icon::class.java)
 
         mockView = mock(View::class.java)
@@ -81,7 +99,9 @@
         whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(artistView)
 
         mediaMetadata = MediaMetadata.Builder()
-        entry = NotificationEntryBuilder()
+        entry = NotificationEntryBuilder().build()
+        entry.getSbn().getNotification().extras.putParcelable(Notification.EXTRA_MEDIA_SESSION,
+                MediaSession.Token(1, null))
 
         ArchTaskExecutor.getInstance().setDelegate(taskExecutor)
 
@@ -109,7 +129,7 @@
 
     @Test
     public fun testUpdateControls() {
-        keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+        keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
         FakeExecutor.exhaustExecutors(fakeExecutor)
         verify(mockView).setVisibility(View.VISIBLE)
     }
@@ -122,11 +142,22 @@
     }
 
     @Test
+    public fun testUpdateControlsNullPlaybackState() {
+        // GIVEN that the playback state is null (ie. the media session was destroyed)
+        whenever(mockMediaController.getPlaybackState()).thenReturn(null)
+        // WHEN updated
+        keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
+        FakeExecutor.exhaustExecutors(fakeExecutor)
+        // THEN the controls are cleared (ie. visibility is set to GONE)
+        verify(mockView).setVisibility(View.GONE)
+    }
+
+    @Test
     public fun testSongName() {
         val song: String = "Song"
         mediaMetadata.putText(MediaMetadata.METADATA_KEY_TITLE, song)
 
-        keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+        keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
 
         assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
         assertThat(songView.getText()).isEqualTo(song)
@@ -137,7 +168,7 @@
         val artist: String = "Artist"
         mediaMetadata.putText(MediaMetadata.METADATA_KEY_ARTIST, artist)
 
-        keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+        keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
 
         assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
         assertThat(artistView.getText()).isEqualTo(artist)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 475023e..5227aaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -160,7 +160,7 @@
                 LOW_BMP_HEIGHT /* bmpHeight */,
                 LOW_BMP_WIDTH /* surfaceWidth */,
                 LOW_BMP_HEIGHT /* surfaceHeight */,
-                true /* assertion */);
+                false /* assertion */);
     }
 
     @Test
@@ -172,7 +172,7 @@
                 INVALID_BMP_HEIGHT /* bmpHeight */,
                 ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
                 ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */,
-                true /* assertion */);
+                false /* assertion */);
     }
 
     private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index c9bb401..9985d21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -20,8 +20,10 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.testing.AndroidTestingRunner;
@@ -56,6 +58,7 @@
         mDockManagerFake = spy(new DockManagerFake());
         mDockHandler = new DozeDockHandler(mConfig, mMachine, mDockManagerFake);
 
+        when(mMachine.getState()).thenReturn(State.DOZE_AOD);
         doReturn(true).when(mConfig).alwaysOnEnabled(anyInt());
         mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
     }
@@ -101,4 +104,31 @@
 
         verify(mMachine).requestState(eq(State.DOZE));
     }
+
+    @Test
+    public void onEvent_dockedWhilePulsing_wontRequestStateChange() {
+        when(mMachine.getState()).thenReturn(State.DOZE_PULSING);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
+
+        verify(mMachine, never()).requestState(any(State.class));
+    }
+
+    @Test
+    public void onEvent_noneWhilePulsing_wontRequestStateChange() {
+        when(mMachine.getState()).thenReturn(State.DOZE_PULSING);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
+
+        verify(mMachine, never()).requestState(any(State.class));
+    }
+
+    @Test
+    public void onEvent_hideWhilePulsing_wontRequestStateChange() {
+        when(mMachine.getState()).thenReturn(State.DOZE_PULSING);
+
+        mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED_HIDE);
+
+        verify(mMachine, never()).requestState(any(State.class));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index c483314..1f07f46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -254,6 +254,17 @@
     }
 
     @Test
+    public void testPulseDone_whileDockedAoD_staysDockedAod() {
+        when(mDockManager.isDocked()).thenReturn(true);
+        mMachine.requestState(INITIALIZED);
+        mMachine.requestState(DOZE_AOD_DOCKED);
+
+        mMachine.requestState(DOZE_PULSE_DONE);
+
+        verify(mPartMock, never()).transitionTo(DOZE_AOD_DOCKED, DOZE_PULSE_DONE);
+    }
+
+    @Test
     public void testPulseDone_dozeSuppressed_afterDocked_goesToDoze() {
         when(mHost.isDozeSuppressed()).thenReturn(true);
         when(mDockManager.isDocked()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
index 56a7484..0d66340 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -51,6 +51,7 @@
 
     private PipAnimationController mPipAnimationController;
 
+    @Mock
     private SurfaceControl mLeash;
 
     @Mock
@@ -60,10 +61,6 @@
     public void setUp() throws Exception {
         mPipAnimationController = new PipAnimationController(
                 mContext, new PipSurfaceTransactionHelper(mContext));
-        mLeash = new SurfaceControl.Builder()
-                .setContainerLayer()
-                .setName("FakeLeash")
-                .build();
         MockitoAnnotations.initMocks(this);
     }
 
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 0bf0f04..425bf88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.pip;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.ComponentName;
@@ -56,11 +57,15 @@
 
     private PipBoundsHandler mPipBoundsHandler;
     private DisplayInfo mDefaultDisplayInfo;
+    private ComponentName mTestComponentName1;
+    private ComponentName mTestComponentName2;
 
     @Before
     public void setUp() throws Exception {
         initializeMockResources();
         mPipBoundsHandler = new PipBoundsHandler(mContext, new PipSnapAlgorithm(mContext));
+        mTestComponentName1 = new ComponentName(mContext, "component1");
+        mTestComponentName2 = new ComponentName(mContext, "component2");
 
         mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo);
     }
@@ -121,7 +126,7 @@
         };
         for (float aspectRatio : aspectRatios) {
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+                    mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
             final float actualAspectRatio =
                     destinationBounds.width() / (destinationBounds.height() * 1f);
             assertEquals("Destination bounds matches the given aspect ratio",
@@ -137,7 +142,7 @@
         };
         for (float aspectRatio : invalidAspectRatios) {
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+                    mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
             final float actualAspectRatio =
                     destinationBounds.width() / (destinationBounds.height() * 1f);
             assertEquals("Destination bounds fallbacks to default aspect ratio",
@@ -153,7 +158,7 @@
         currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
 
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE);
+                mTestComponentName1, aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE);
 
         final float actualAspectRatio =
                 destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -177,7 +182,7 @@
             final float aspectRatio = aspectRatios[i];
             final Size minimalSize = minimalSizes[i];
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize);
+                    mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize);
             assertTrue("Destination bounds is no smaller than minimal requirement",
                     (destinationBounds.width() == minimalSize.getWidth()
                             && destinationBounds.height() >= minimalSize.getHeight())
@@ -198,7 +203,7 @@
         final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);
 
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                aspectRatio, currentBounds, minSize);
+                mTestComponentName1, aspectRatio, currentBounds, minSize);
 
         assertTrue("Destination bounds ignores minimal size",
                 destinationBounds.width() > minSize.getWidth()
@@ -206,81 +211,92 @@
     }
 
     @Test
+    public void getDestinationBounds_withDifferentComponentName_ignoreLastPosition() {
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+        oldPosition.offset(0, -100);
+        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition);
+
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName2,
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
+
+        assertNonBoundsInclusionWithMargin("ignore saved bounds", oldPosition, newPosition);
+    }
+
+    @Test
     public void setShelfHeight_offsetBounds() {
         final int shelfHeight = 100;
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         mPipBoundsHandler.setShelfHeight(true, shelfHeight);
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -shelfHeight);
-        assertBoundsWithMargin("offsetBounds by shelf", oldPosition, newPosition);
+        assertBoundsInclusionWithMargin("offsetBounds by shelf", oldPosition, newPosition);
     }
 
     @Test
     public void onImeVisibilityChanged_offsetBounds() {
         final int imeHeight = 100;
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -imeHeight);
-        assertBoundsWithMargin("offsetBounds by IME", oldPosition, newPosition);
+        assertBoundsInclusionWithMargin("offsetBounds by IME", oldPosition, newPosition);
     }
 
     @Test
     public void onSaveReentryBounds_restoreLastPosition() {
-        final ComponentName componentName = new ComponentName(mContext, "component1");
-        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+        final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition);
+        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition);
 
-        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+        final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
-        assertBoundsWithMargin("restoreLastPosition", oldPosition, newPosition);
+        assertBoundsInclusionWithMargin("restoreLastPosition", oldPosition, newPosition);
     }
 
     @Test
     public void onResetReentryBounds_useDefaultBounds() {
-        final ComponentName componentName = new ComponentName(mContext, "component1");
-        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
         final Rect newBounds = new Rect(defaultBounds);
         newBounds.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
+        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds);
 
-        mPipBoundsHandler.onResetReentryBounds(componentName);
-        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+        mPipBoundsHandler.onResetReentryBounds(mTestComponentName1);
+        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
-        assertBoundsWithMargin("useDefaultBounds", defaultBounds, actualBounds);
+        assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds);
     }
 
     @Test
     public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
-        final ComponentName componentName = new ComponentName(mContext, "component1");
-        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
+        final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
         final Rect newBounds = new Rect(defaultBounds);
         newBounds.offset(0, -100);
-        mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
+        mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds);
 
-        mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2"));
-        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
+        mPipBoundsHandler.onResetReentryBounds(mTestComponentName2);
+        final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1,
                 DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
-        assertBoundsWithMargin("restoreLastPosition", newBounds, actualBounds);
+        assertBoundsInclusionWithMargin("restoreLastPosition", newBounds, actualBounds);
     }
 
-    private void assertBoundsWithMargin(String from, Rect expected, Rect actual) {
+    private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
         final Rect expectedWithMargin = new Rect(expected);
         expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
         assertTrue(from + ": expect " + expected
@@ -288,4 +304,13 @@
                 + " with error margin " + ROUNDING_ERROR_MARGIN,
                 expectedWithMargin.contains(actual));
     }
+
+    private void assertNonBoundsInclusionWithMargin(String from, Rect expected, Rect actual) {
+        final Rect expectedWithMargin = new Rect(expected);
+        expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN);
+        assertFalse(from + ": expect " + expected
+                        + " not contains " + actual
+                        + " with error margin " + ROUNDING_ERROR_MARGIN,
+                expectedWithMargin.contains(actual));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 6b7a3bf..c874915 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -117,12 +117,27 @@
     }
 
     @Test
-    fun updateGlobalDialogVisibility_appliesBlur() {
+    fun updateGlobalDialogVisibility_animatesBlur() {
         notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
         verify(globalActionsSpring).animateTo(eq(maxBlur / 2), safeEq(root))
     }
 
     @Test
+    fun updateGlobalDialogVisibility_appliesBlur_withoutHomeControls() {
+        `when`(globalActionsSpring.radius).thenReturn(maxBlur)
+        notificationShadeDepthController.updateBlurCallback.doFrame(0)
+        verify(blurUtils).applyBlur(any(), eq(maxBlur))
+    }
+
+    @Test
+    fun updateGlobalDialogVisibility_appliesBlur_unlessHomeControls() {
+        notificationShadeDepthController.showingHomeControls = true
+        `when`(globalActionsSpring.radius).thenReturn(maxBlur)
+        notificationShadeDepthController.updateBlurCallback.doFrame(0)
+        verify(blurUtils).applyBlur(any(), eq(0))
+    }
+
+    @Test
     fun updateBlurCallback_setsBlurAndZoom() {
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(wallpaperManager).setWallpaperZoomOut(any(), anyFloat())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 65fbe79..0a10ab2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -16,29 +16,43 @@
 
 package com.android.systemui.toast;
 
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+import static android.widget.ToastPresenter.TEXT_TOAST_LAYOUT;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
 import android.app.ITransientNotificationCallback;
+import android.content.Context;
 import android.os.Binder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 import android.widget.Toast;
+import android.widget.ToastPresenter;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
+import com.android.internal.util.IntPair;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.CommandQueue;
 
@@ -49,32 +63,53 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class ToastUITest extends SysuiTestCase {
+    private static final int ANDROID_UID = 1000;
+    private static final int SYSTEMUI_UID = 10140;
+
+    private static final int UID_1 = 10255;
     private static final String PACKAGE_NAME_1 = "com.example1.test";
     private static final Binder TOKEN_1 = new Binder();
     private static final Binder WINDOW_TOKEN_1 = new Binder();
+
+    private static final int UID_2 = 10256;
     private static final String PACKAGE_NAME_2 = "com.example2.test";
     private static final Binder TOKEN_2 = new Binder();
     private static final Binder WINDOW_TOKEN_2 = new Binder();
+
     private static final String TEXT = "Hello World";
     private static final int MESSAGE_RES_ID = R.id.message;
 
+    private Context mContextSpy;
+    private ToastUI mToastUI;
+    @Mock private LayoutInflater mLayoutInflater;
     @Mock private CommandQueue mCommandQueue;
     @Mock private WindowManager mWindowManager;
     @Mock private INotificationManager mNotificationManager;
-    @Mock private AccessibilityManager mAccessibilityManager;
+    @Mock private IAccessibilityManager mAccessibilityManager;
     @Mock private ITransientNotificationCallback mCallback;
     @Captor private ArgumentCaptor<View> mViewCaptor;
     @Captor private ArgumentCaptor<ViewGroup.LayoutParams> mParamsCaptor;
-    private ToastUI mToastUI;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mToastUI = new ToastUI(mContext, mCommandQueue, mWindowManager, mNotificationManager,
+
+        // This is because inflate will result in WindowManager (WM) calls, which will fail since we
+        // are mocking it, so we mock LayoutInflater with the view obtained before mocking WM.
+        View view = ToastPresenter.getTextToastView(mContext, TEXT);
+        when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn(view);
+        mContext.addMockSystemService(LayoutInflater.class, mLayoutInflater);
+
+        mContext.addMockSystemService(WindowManager.class, mWindowManager);
+        mContextSpy = spy(mContext);
+        doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt());
+
+        mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager,
                 mAccessibilityManager);
     }
 
@@ -87,7 +122,8 @@
 
     @Test
     public void testShowToast_addsCorrectViewToWindowManager() throws Exception {
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null);
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+                null);
 
         verify(mWindowManager).addView(mViewCaptor.capture(), any());
         View view = mViewCaptor.getValue();
@@ -96,13 +132,14 @@
 
     @Test
     public void testShowToast_addsViewWithCorrectLayoutParamsToWindowManager() throws Exception {
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null);
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+                null);
 
         verify(mWindowManager).addView(any(), mParamsCaptor.capture());
         ViewGroup.LayoutParams params = mParamsCaptor.getValue();
         assertThat(params).isInstanceOf(WindowManager.LayoutParams.class);
         WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
-        assertThat(windowParams.packageName).isEqualTo(mContext.getPackageName());
+        assertThat(windowParams.packageName).isEqualTo(mContextSpy.getPackageName());
         assertThat(windowParams.getTitle()).isEqualTo("Toast");
         assertThat(windowParams.token).isEqualTo(WINDOW_TOKEN_1);
         assertThat(windowParams.privateFlags
@@ -111,19 +148,7 @@
 
     @Test
     public void testShowToast_forAndroidPackage_addsAllUserFlag() throws Exception {
-        mToastUI.showToast("android", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null);
-
-        verify(mWindowManager).addView(any(), mParamsCaptor.capture());
-        ViewGroup.LayoutParams params = mParamsCaptor.getValue();
-        assertThat(params).isInstanceOf(WindowManager.LayoutParams.class);
-        WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
-        assertThat(windowParams.privateFlags
-                & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isNotEqualTo(0);
-    }
-
-    @Test
-    public void testShowToast_forSystemUiPackage_addsAllUserFlag() throws Exception {
-        mToastUI.showToast("com.android.systemui", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+        mToastUI.showToast(ANDROID_UID, "android", TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 null);
 
         verify(mWindowManager).addView(any(), mParamsCaptor.capture());
@@ -135,8 +160,21 @@
     }
 
     @Test
+    public void testShowToast_forSystemUiPackage_addsAllUserFlag() throws Exception {
+        mToastUI.showToast(SYSTEMUI_UID, "com.android.systemui", TOKEN_1, TEXT, WINDOW_TOKEN_1,
+                Toast.LENGTH_LONG, null);
+
+        verify(mWindowManager).addView(any(), mParamsCaptor.capture());
+        ViewGroup.LayoutParams params = mParamsCaptor.getValue();
+        assertThat(params).isInstanceOf(WindowManager.LayoutParams.class);
+        WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
+        assertThat(windowParams.privateFlags
+                & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isNotEqualTo(0);
+    }
+
+    @Test
     public void testShowToast_callsCallback() throws Exception {
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
 
         verify(mCallback).onToastShown();
@@ -144,14 +182,24 @@
 
     @Test
     public void testShowToast_sendsAccessibilityEvent() throws Exception {
-        when(mAccessibilityManager.isEnabled()).thenReturn(true);
+        // Enable accessibility
+        when(mAccessibilityManager.addClient(any(), anyInt())).thenReturn(
+                IntPair.of(STATE_FLAG_ACCESSIBILITY_ENABLED, AccessibilityEvent.TYPES_ALL_MASK));
+        // AccessibilityManager recycles the event that goes over the wire after making the binder
+        // call to the service. Since we are mocking the service, that call is local, so if we use
+        // ArgumentCaptor or ArgumentMatcher it will retain a reference to the recycled event, which
+        // will already have its state reset by the time we verify its contents. So, instead, we
+        // serialize it at call-time and later on deserialize it to verity its contents.
+        Parcel eventParcel = Parcel.obtain();
+        doAnswer(writeArgumentToParcel(0, eventParcel)).when(
+                mAccessibilityManager).sendAccessibilityEvent(any(), anyInt());
 
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, null);
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+                null);
 
-        ArgumentCaptor<AccessibilityEvent> eventCaptor = ArgumentCaptor.forClass(
-                AccessibilityEvent.class);
-        verify(mAccessibilityManager).sendAccessibilityEvent(eventCaptor.capture());
-        AccessibilityEvent event = eventCaptor.getValue();
+        eventParcel.setDataPosition(0);
+        assertThat(eventParcel.dataSize()).isGreaterThan(0);
+        AccessibilityEvent event = AccessibilityEvent.CREATOR.createFromParcel(eventParcel);
         assertThat(event.getEventType()).isEqualTo(
                 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
         assertThat(event.getClassName()).isEqualTo(Toast.class.getName());
@@ -160,7 +208,7 @@
 
     @Test
     public void testHideToast_removesView() throws Exception {
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
         View view = verifyWmAddViewAndAttachToParent();
 
@@ -171,7 +219,7 @@
 
     @Test
     public void testHideToast_finishesToken() throws Exception {
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
 
         mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
@@ -181,7 +229,7 @@
 
     @Test
     public void testHideToast_callsCallback() throws Exception {
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
 
         mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
@@ -191,7 +239,7 @@
 
     @Test
     public void testHideToast_whenNotCurrentToastToken_doesNotHideToast() throws Exception {
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
 
         mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_2);
@@ -201,7 +249,7 @@
 
     @Test
     public void testHideToast_whenNotCurrentToastPackage_doesNotHideToast() throws Exception {
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
 
         mToastUI.hideToast(PACKAGE_NAME_2, TOKEN_1);
@@ -211,11 +259,12 @@
 
     @Test
     public void testShowToast_afterShowToast_hidesCurrentToast() throws Exception {
-        mToastUI.showToast(PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
         View view = verifyWmAddViewAndAttachToParent();
 
-        mToastUI.showToast(PACKAGE_NAME_2, TOKEN_2, TEXT, WINDOW_TOKEN_2, Toast.LENGTH_LONG, null);
+        mToastUI.showToast(UID_2, PACKAGE_NAME_2, TOKEN_2, TEXT, WINDOW_TOKEN_2, Toast.LENGTH_LONG,
+                null);
 
         verify(mWindowManager).removeViewImmediate(view);
         verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1);
@@ -227,8 +276,15 @@
         verify(mWindowManager).addView(viewCaptor.capture(), any());
         View view = viewCaptor.getValue();
         // Simulate attaching to view hierarchy
-        ViewGroup parent = new FrameLayout(mContext);
+        ViewGroup parent = new FrameLayout(mContextSpy);
         parent.addView(view);
         return view;
     }
+
+    private Answer<Void> writeArgumentToParcel(int i, Parcel dest) {
+        return inv -> {
+            inv.<Parcelable>getArgument(i).writeToParcel(dest, 0);
+            return null;
+        };
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
index f1672b1..f6b7b74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
@@ -106,6 +106,10 @@
                 location[1] = targetCenterY - targetSize / 2 // y = 800
             }
         }.`when`(targetView).getLocationOnScreen(ArgumentMatchers.any())
+        doAnswer { invocation ->
+            (invocation.arguments[0] as Runnable).run()
+            true
+        }.`when`(targetView).post(ArgumentMatchers.any())
         `when`(targetView.context).thenReturn(context)
 
         magneticTarget = MagnetizedObject.MagneticTarget(targetView, magneticFieldRadius)
@@ -408,6 +412,10 @@
         `when`(secondTargetView.width).thenReturn(targetSize)  // width = 200
         `when`(secondTargetView.height).thenReturn(targetSize) // height = 200
         doAnswer { invocation ->
+            (invocation.arguments[0] as Runnable).run()
+            true
+        }.`when`(secondTargetView).post(ArgumentMatchers.any())
+        doAnswer { invocation ->
             (invocation.arguments[0] as IntArray).also { location ->
                 // Return the top left of the target.
                 location[0] = secondTargetCenterX - targetSize / 2 // x = 0
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index c5478d2..5b6fe91 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -33,6 +33,7 @@
 import android.net.LinkProperties;
 import android.net.MacAddress;
 import android.net.RouteInfo;
+import android.net.TetherOffloadRuleParcel;
 import android.net.TetheredClient;
 import android.net.TetheringManager;
 import android.net.TetheringRequestParcel;
@@ -40,7 +41,7 @@
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
 import android.net.dhcp.DhcpServingParamsParcelExt;
-import android.net.dhcp.IDhcpLeaseCallbacks;
+import android.net.dhcp.IDhcpEventCallbacks;
 import android.net.dhcp.IDhcpServer;
 import android.net.ip.IpNeighborMonitor.NeighborEvent;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
@@ -279,6 +280,19 @@
             return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
                     dstMac);
         }
+
+        // Don't manipulate TetherOffloadRuleParcel directly because implementing onNewUpstream()
+        // would be error-prone due to generated stable AIDL classes not having a copy constructor.
+        public TetherOffloadRuleParcel toTetherOffloadRuleParcel() {
+            final TetherOffloadRuleParcel parcel = new TetherOffloadRuleParcel();
+            parcel.inputInterfaceIndex = upstreamIfindex;
+            parcel.outputInterfaceIndex = downstreamIfindex;
+            parcel.destination = address.getAddress();
+            parcel.prefixLength = 128;
+            parcel.srcL2Address = srcMac.toByteArray();
+            parcel.dstL2Address = dstMac.toByteArray();
+            return parcel;
+        }
     }
     private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules =
             new LinkedHashMap<>();
@@ -448,7 +462,7 @@
         }
     }
 
-    private class DhcpLeaseCallback extends IDhcpLeaseCallbacks.Stub {
+    private class DhcpLeaseCallback extends IDhcpEventCallbacks.Stub {
         @Override
         public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) {
             final ArrayList<TetheredClient> leases = new ArrayList<>();
@@ -482,6 +496,11 @@
         }
 
         @Override
+        public void onNewPrefixRequest(IpPrefix currentPrefix) {
+            //TODO: add specific implementation.
+        }
+
+        @Override
         public int getInterfaceVersion() {
             return this.VERSION;
         }
@@ -815,9 +834,7 @@
 
     private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
         try {
-            mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex,
-                    rule.address.getAddress(),  mInterfaceParams.macAddr.toByteArray(),
-                    rule.dstMac.toByteArray());
+            mNetd.tetherOffloadRuleAdd(rule.toTetherOffloadRuleParcel());
             mIpv6ForwardingRules.put(rule.address, rule);
         } catch (RemoteException | ServiceSpecificException e) {
             mLog.e("Could not add IPv6 downstream rule: ", e);
@@ -826,7 +843,7 @@
 
     private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
         try {
-            mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress());
+            mNetd.tetherOffloadRuleRemove(rule.toTetherOffloadRuleParcel());
             if (removeFromMap) {
                 mIpv6ForwardingRules.remove(rule.address);
             }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index bd60594f..639cf65 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -20,6 +20,7 @@
 import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
 import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_INVALID;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
@@ -537,6 +538,7 @@
     private static boolean isValidDownstreamType(int type) {
         switch (type) {
             case TETHERING_BLUETOOTH:
+            case TETHERING_ETHERNET:
             case TETHERING_USB:
             case TETHERING_WIFI:
                 return true;
@@ -650,6 +652,11 @@
 
     private void handleRequestLatestTetheringEntitlementValue(int downstream,
             ResultReceiver receiver, boolean showEntitlementUi) {
+        if (!isValidDownstreamType(downstream)) {
+            receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null);
+            return;
+        }
+
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
         if (!isTetherProvisioningRequired(config)) {
             receiver.send(TETHER_ERROR_NO_ERROR, null);
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index 1a1c30d..620261b 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -39,4 +39,9 @@
         "android.test.base",
         "android.test.mock",
     ],
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
 }
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 843a4f1..b02bb23 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
 import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -57,14 +58,17 @@
 
 import java.io.FileDescriptor;
 import java.net.Inet4Address;
+import java.net.InterfaceAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
 import java.util.Collection;
 import java.util.List;
 import java.util.Random;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 @RunWith(AndroidJUnit4.class)
 @MediumTest
@@ -109,7 +113,7 @@
     }
 
     private void cleanUp() throws Exception {
-        mTm.stopTethering(TetheringManager.TETHERING_ETHERNET);
+        mTm.stopTethering(TETHERING_ETHERNET);
         if (mTetheringEventCallback != null) {
             mTetheringEventCallback.awaitInterfaceUntethered();
             mTetheringEventCallback.unregister();
@@ -150,10 +154,7 @@
         Log.d(TAG, "Including test interfaces");
         mEm.setIncludeTestInterfaces(true);
 
-        Log.d(TAG, "Requesting tethered interface");
-        mTetheredInterfaceRequester.requestInterface();
-
-        final String iface = mTetheredInterfaceRequester.awaitRequestedInterface();
+        final String iface = mTetheredInterfaceRequester.getInterface();
         assertEquals("TetheredInterfaceCallback for unexpected interface",
                 mTestIface.getInterfaceName(), iface);
 
@@ -165,14 +166,13 @@
         // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
         assumeFalse(mEm.isAvailable());
 
-        Log.d(TAG, "Requesting tethered interface");
-        mTetheredInterfaceRequester.requestInterface();
+        CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface();
 
         mEm.setIncludeTestInterfaces(true);
 
         mTestIface = createTestInterface();
 
-        final String iface = mTetheredInterfaceRequester.awaitRequestedInterface();
+        final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         assertEquals("TetheredInterfaceCallback for unexpected interface",
                 mTestIface.getInterfaceName(), iface);
 
@@ -180,12 +180,54 @@
     }
 
     @Test
+    public void testStaticIpv4() throws Exception {
+        assumeFalse(mEm.isAvailable());
+
+        mEm.setIncludeTestInterfaces(true);
+
+        mTestIface = createTestInterface();
+
+        final String iface = mTetheredInterfaceRequester.getInterface();
+        assertEquals("TetheredInterfaceCallback for unexpected interface",
+                mTestIface.getInterfaceName(), iface);
+
+        assertInvalidStaticIpv4Request(iface, null, null);
+        assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64");
+        assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", "2001:db8:2::/28");
+        assertInvalidStaticIpv4Request(iface, "2001:db8:2::/28", "192.0.2.2/28");
+        assertInvalidStaticIpv4Request(iface, "192.0.2.2/28", null);
+        assertInvalidStaticIpv4Request(iface, null, "192.0.2.2/28");
+        assertInvalidStaticIpv4Request(iface, "192.0.2.3/27", "192.0.2.2/28");
+
+        final String localAddr = "192.0.2.3/28";
+        final String clientAddr = "192.0.2.2/28";
+        mTetheringEventCallback = enableEthernetTethering(iface,
+                requestWithStaticIpv4(localAddr, clientAddr));
+
+        mTetheringEventCallback.awaitInterfaceTethered();
+        assertInterfaceHasIpAddress(iface, localAddr);
+
+        byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
+        byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
+
+        FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor();
+        mTapPacketReader = makePacketReader(fd, getMTU(mTestIface));
+        DhcpResults dhcpResults = runDhcp(fd, client1);
+        assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
+
+        try {
+            runDhcp(fd, client2);
+            fail("Only one client should get an IP address");
+        } catch (TimeoutException expected) { }
+
+    }
+
+    @Test
     public void testPhysicalEthernet() throws Exception {
         assumeTrue(mEm.isAvailable());
 
         // Get an interface to use.
-        mTetheredInterfaceRequester.requestInterface();
-        String iface = mTetheredInterfaceRequester.awaitRequestedInterface();
+        final String iface = mTetheredInterfaceRequester.getInterface();
 
         // Enable Ethernet tethering and check that it starts.
         mTetheringEventCallback = enableEthernetTethering(iface);
@@ -275,7 +317,8 @@
         }
     }
 
-    private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception {
+    private MyTetheringEventCallback enableEthernetTethering(String iface,
+            TetheringRequest request) throws Exception {
         MyTetheringEventCallback callback = new MyTetheringEventCallback(mTm, iface);
         mTm.registerTetheringEventCallback(mHandler::post, callback);
 
@@ -286,34 +329,37 @@
             }
         };
         Log.d(TAG, "Starting Ethernet tethering");
-        mTm.startTethering(
-                new TetheringRequest.Builder(TetheringManager.TETHERING_ETHERNET).build(),
-                mHandler::post /* executor */,  startTetheringCallback);
+        mTm.startTethering(request, mHandler::post /* executor */,  startTetheringCallback);
         callback.awaitInterfaceTethered();
         return callback;
     }
 
+    private MyTetheringEventCallback enableEthernetTethering(String iface) throws Exception {
+        return enableEthernetTethering(iface,
+                new TetheringRequest.Builder(TETHERING_ETHERNET).build());
+    }
+
     private int getMTU(TestNetworkInterface iface) throws SocketException {
         NetworkInterface nif = NetworkInterface.getByName(iface.getInterfaceName());
         assertNotNull("Can't get NetworkInterface object for " + iface.getInterfaceName(), nif);
         return nif.getMTU();
     }
 
+    private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) {
+        final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu);
+        mHandler.post(() -> reader.start());
+        HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS);
+        return reader;
+    }
+
     private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception {
         FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
-        mTapPacketReader = new TapPacketReader(mHandler, fd, mtu);
-        mHandler.post(() -> mTapPacketReader.start());
-        HandlerUtilsKt.waitForIdle(mHandler, TIMEOUT_MS);
-
+        mTapPacketReader = makePacketReader(fd, mtu);
         mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName());
         checkTetheredClientCallbacks(fd);
     }
 
-    private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception {
-        // Create a fake client.
-        byte[] clientMacAddr = new byte[6];
-        new Random().nextBytes(clientMacAddr);
-
+    private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception {
         // We have to retransmit DHCP requests because IpServer declares itself to be ready before
         // its DhcpServer is actually started. TODO: fix this race and remove this loop.
         DhcpPacket offerPacket = null;
@@ -323,13 +369,25 @@
             offerPacket = getNextDhcpPacket();
             if (offerPacket instanceof DhcpOfferPacket) break;
         }
-        assertTrue("No DHCPOFFER received on interface within timeout",
-                offerPacket instanceof DhcpOfferPacket);
+        if (!(offerPacket instanceof DhcpOfferPacket)) {
+            throw new TimeoutException("No DHCPOFFER received on interface within timeout");
+        }
 
         sendDhcpRequest(fd, offerPacket, clientMacAddr);
         DhcpPacket ackPacket = getNextDhcpPacket();
-        assertTrue("No DHCPACK received on interface within timeout",
-                ackPacket instanceof DhcpAckPacket);
+        if (!(ackPacket instanceof DhcpAckPacket)) {
+            throw new TimeoutException("No DHCPACK received on interface within timeout");
+        }
+
+        return ackPacket.toDhcpResults();
+    }
+
+    private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception {
+        // Create a fake client.
+        byte[] clientMacAddr = new byte[6];
+        new Random().nextBytes(clientMacAddr);
+
+        DhcpResults dhcpResults = runDhcp(fd, clientMacAddr);
 
         final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected();
         assertEquals(1, clients.size());
@@ -337,7 +395,7 @@
 
         // Check the MAC address.
         assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress());
-        assertEquals(TetheringManager.TETHERING_ETHERNET, client.getTetheringType());
+        assertEquals(TETHERING_ETHERNET, client.getTetheringType());
 
         // Check the hostname.
         assertEquals(1, client.getAddresses().size());
@@ -345,7 +403,6 @@
         assertEquals(DHCP_HOSTNAME, info.getHostname());
 
         // Check the address is the one that was handed out in the DHCP ACK.
-        DhcpResults dhcpResults = offerPacket.toDhcpResults();
         assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
 
         // Check that the lifetime is correct +/- 10s.
@@ -373,8 +430,8 @@
         private final Handler mHandler;
         private final EthernetManager mEm;
 
-        private volatile TetheredInterfaceRequest mRequest;
-        private volatile String mIface;
+        private TetheredInterfaceRequest mRequest;
+        private final CompletableFuture<String> mFuture = new CompletableFuture<>();
 
         TetheredInterfaceRequester(Handler handler, EthernetManager em) {
             mHandler = handler;
@@ -384,25 +441,28 @@
         @Override
         public void onAvailable(String iface) {
             Log.d(TAG, "Ethernet interface available: " + iface);
-            mIface = iface;
-            mInterfaceAvailableLatch.countDown();
+            mFuture.complete(iface);
         }
+
         @Override
-        public void onUnavailable() {}
-
-        public void requestInterface() {
-            assertNull("BUG: more than one tethered interface request", mRequest);
-            mRequest = mEm.requestTetheredInterface(mHandler::post, this);
+        public void onUnavailable() {
+            mFuture.completeExceptionally(new IllegalStateException("onUnavailable received"));
         }
 
-        public String awaitRequestedInterface() throws InterruptedException {
-            assertTrue("No tethered interface available after " + TIMEOUT_MS + "ms",
-                    mInterfaceAvailableLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-            return mIface;
+        public CompletableFuture<String> requestInterface() {
+            assertNull("BUG: more than one tethered interface request", mRequest);
+            Log.d(TAG, "Requesting tethered interface");
+            mRequest = mEm.requestTetheredInterface(mHandler::post, this);
+            return mFuture;
+        }
+
+        public String getInterface() throws Exception {
+            return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         }
 
         public void release() {
             if (mRequest != null) {
+                mFuture.obtrudeException(new IllegalStateException("Request already released"));
                 mRequest.release();
                 mRequest = null;
             }
@@ -442,6 +502,34 @@
         assertEquals("LinkAddress scope does not match", l1.getScope(), l2.getScope());
     }
 
+    private TetheringRequest requestWithStaticIpv4(String local, String client) {
+        LinkAddress localAddr = local == null ? null : new LinkAddress(local);
+        LinkAddress clientAddr = client == null ? null : new LinkAddress(client);
+        return new TetheringRequest.Builder(TETHERING_ETHERNET)
+                .setStaticIpv4Addresses(localAddr, clientAddr).build();
+    }
+
+    private void assertInvalidStaticIpv4Request(String iface, String local, String client)
+            throws Exception {
+        try {
+            enableEthernetTethering(iface, requestWithStaticIpv4(local, client));
+            fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client);
+        } catch (IllegalArgumentException | NullPointerException expected) { }
+    }
+
+    private void assertInterfaceHasIpAddress(String iface, String expected) throws Exception {
+        LinkAddress expectedAddr = new LinkAddress(expected);
+        NetworkInterface nif = NetworkInterface.getByName(iface);
+        for (InterfaceAddress ia : nif.getInterfaceAddresses()) {
+            final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength());
+            if (expectedAddr.equals(addr)) {
+                return;
+            }
+        }
+        fail("Expected " + iface + " to have IP address " + expected + ", found "
+                + nif.getInterfaceAddresses());
+    }
+
     private TestNetworkInterface createTestInterface() throws Exception {
         TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
         TestNetworkInterface iface = tnm.createTapInterface();
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
index 530bc07..4ff1d37 100644
--- a/packages/Tethering/tests/unit/AndroidManifest.xml
+++ b/packages/Tethering/tests/unit/AndroidManifest.xml
@@ -20,7 +20,16 @@
 
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
+        <service
+            android:name="com.android.server.connectivity.tethering.MockTetheringService"
+            android:permission="android.permission.TETHER_PRIVILEGED"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.server.connectivity.tethering.TetheringService"/>
+            </intent-filter>
+        </service>
     </application>
+
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.networkstack.tethering.tests.unit"
         android:label="Tethering service tests">
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 3106e0e..fdfdae8 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -43,7 +43,6 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -66,6 +65,7 @@
 import android.net.LinkProperties;
 import android.net.MacAddress;
 import android.net.RouteInfo;
+import android.net.TetherOffloadRuleParcel;
 import android.net.dhcp.DhcpServingParamsParcel;
 import android.net.dhcp.IDhcpServer;
 import android.net.dhcp.IDhcpServerCallbacks;
@@ -85,6 +85,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Captor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
@@ -92,6 +93,7 @@
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
+import java.util.Arrays;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -514,6 +516,65 @@
         mLooper.dispatchAll();
     }
 
+    /**
+     * Custom ArgumentMatcher for TetherOffloadRuleParcel. This is needed because generated stable
+     * AIDL classes don't have equals(), so we cannot just use eq(). A custom assert, such as:
+     *
+     * private void checkFooCalled(StableParcelable p, ...) {
+     *     ArgumentCaptor<FooParam> captor = ArgumentCaptor.forClass(FooParam.class);
+     *     verify(mMock).foo(captor.capture());
+     *     Foo foo = captor.getValue();
+     *     assertFooMatchesExpectations(foo);
+     * }
+     *
+     * almost works, but not quite. This is because if the code under test calls foo() twice, the
+     * first call to checkFooCalled() matches both the calls, putting both calls into the captor,
+     * and then fails with TooManyActualInvocations. It also makes it harder to use other mockito
+     * features such as never(), inOrder(), etc.
+     *
+     * This approach isn't great because if the match fails, the error message is unhelpful
+     * (actual: "android.net.TetherOffloadRuleParcel@8c827b0" or some such), but at least it does
+     * work.
+     *
+     * See ConnectivityServiceTest#assertRoutesAdded for an alternative approach which solves the
+     * TooManyActualInvocations problem described above by forcing the caller of the custom assert
+     * method to specify all expected invocations in one call. This is useful when the stable
+     * parcelable class being asserted on has a corresponding Java object (eg., RouteInfo and
+     * RouteInfoParcelable), and the caller can just pass in a list of them. It not useful here
+     * because there is no such object.
+     */
+    private static class TetherOffloadRuleParcelMatcher implements
+            ArgumentMatcher<TetherOffloadRuleParcel> {
+        public final int upstreamIfindex;
+        public final InetAddress dst;
+        public final MacAddress dstMac;
+
+        TetherOffloadRuleParcelMatcher(int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
+            this.upstreamIfindex = upstreamIfindex;
+            this.dst = dst;
+            this.dstMac = dstMac;
+        }
+
+        public boolean matches(TetherOffloadRuleParcel parcel) {
+            return upstreamIfindex == parcel.inputInterfaceIndex
+                    && (TEST_IFACE_PARAMS.index == parcel.outputInterfaceIndex)
+                    && Arrays.equals(dst.getAddress(), parcel.destination)
+                    && (128 == parcel.prefixLength)
+                    && Arrays.equals(TEST_IFACE_PARAMS.macAddr.toByteArray(), parcel.srcL2Address)
+                    && Arrays.equals(dstMac.toByteArray(), parcel.dstL2Address);
+        }
+
+        public String toString() {
+            return String.format("TetherOffloadRuleParcelMatcher(%d, %s, %s",
+                    upstreamIfindex, dst.getHostAddress(), dstMac);
+        }
+    }
+
+    private TetherOffloadRuleParcel matches(
+            int upstreamIfindex, InetAddress dst, MacAddress dstMac) {
+        return argThat(new TetherOffloadRuleParcelMatcher(upstreamIfindex, dst, dstMac));
+    }
+
     @Test
     public void addRemoveipv6ForwardingRules() throws Exception {
         initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */);
@@ -537,13 +598,11 @@
 
         // Events on this interface are received and sent to netd.
         recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
-        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
-                eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
         reset(mNetd);
 
         recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
-        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
-                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
         reset(mNetd);
 
         // Link-local and multicast neighbors are ignored.
@@ -554,12 +613,12 @@
 
         // A neighbor that is no longer valid causes the rule to be removed.
         recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA);
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
         reset(mNetd);
 
         // A neighbor that is deleted causes the rule to be removed.
         recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
         reset(mNetd);
 
         // Upstream changes result in deleting and re-adding the rules.
@@ -571,22 +630,16 @@
         LinkProperties lp = new LinkProperties();
         lp.setInterfaceName(UPSTREAM_IFACE2);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp);
-        inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
-                eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
-        inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
-                    eq(neighA.getAddress()));
-        inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
-                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
-        inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
-                eq(neighB.getAddress()));
+        inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA));
+        inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
+        inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB));
+        inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
         reset(mNetd);
 
         // When the upstream is lost, rules are removed.
         dispatchTetherConnectionChanged(null, null);
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
-                eq(neighA.getAddress()));
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
-                eq(neighB.getAddress()));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB));
         reset(mNetd);
 
         // If the upstream is IPv4-only, no rules are added.
@@ -599,31 +652,27 @@
         lp.setInterfaceName(UPSTREAM_IFACE);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
         recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
-        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
-                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
-        verify(mNetd, never()).tetherRuleAddDownstreamIpv6(anyInt(), anyInt(),
-                eq(neighA.getAddress()), any(), any());
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
+        verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
 
         // If upstream IPv6 connectivity is lost, rules are removed.
         reset(mNetd);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE, null);
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
 
         // When the interface goes down, rules are removed.
         lp.setInterfaceName(UPSTREAM_IFACE);
         dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
         recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
         recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
-        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
-                eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
-        verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
-                eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
+        verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
         reset(mNetd);
 
         mIpServer.stop();
         mLooper.dispatchAll();
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
-        verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
+        verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
         reset(mNetd);
     }
 
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 0a7850b..b3a30ab 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -17,8 +17,10 @@
 package com.android.server.connectivity.tethering;
 
 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
@@ -353,6 +355,20 @@
         callbackTimeoutHelper(mCallbacklatch);
         assertEquals(0, mEnMgr.uiProvisionCount);
         mEnMgr.reset();
+        // 8. Test get value for invalid downstream type.
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        receiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode);
+                mCallbacklatch.countDown();
+            }
+        };
+        mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true);
+        mLooper.dispatchAll();
+        callbackTimeoutHelper(mCallbacklatch);
+        assertEquals(0, mEnMgr.uiProvisionCount);
+        mEnMgr.reset();
     }
 
     void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
@@ -471,6 +487,22 @@
         mLooper.dispatchAll();
         assertEquals(0, mEnMgr.uiProvisionCount);
         assertEquals(3, mEnMgr.silentProvisionCount);
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
+        mEnMgr.reset();
+        // 7. start ui provisioning, upstream is mobile, downstream is ethernet
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_ETHERNET, true);
+        mLooper.dispatchAll();
+        assertEquals(1, mEnMgr.uiProvisionCount);
+        assertEquals(0, mEnMgr.silentProvisionCount);
+        assertTrue(mEnMgr.isCellularUpstreamPermitted());
+        mEnMgr.reset();
+        // 8. downstream is invalid
+        mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+        mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI_P2P, true);
+        mLooper.dispatchAll();
+        assertEquals(0, mEnMgr.uiProvisionCount);
+        assertEquals(0, mEnMgr.silentProvisionCount);
         mEnMgr.reset();
     }
 
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java
new file mode 100644
index 0000000..9121243
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinatorTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.server.connectivity.tethering;
+
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.RouteInfo.RTN_UNICAST;
+import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
+import static android.net.ip.IpServer.STATE_TETHERED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.RouteInfo;
+import android.net.ip.IpServer;
+import android.net.util.SharedLog;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+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;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IPv6TetheringCoordinatorTest {
+    private static final String TEST_DNS_SERVER = "2001:4860:4860::8888";
+    private static final String TEST_INTERFACE = "test_rmnet0";
+    private static final String TEST_IPV6_ADDRESS = "2001:db8::1/64";
+    private static final String TEST_IPV4_ADDRESS = "192.168.100.1/24";
+
+    private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
+    private ArrayList<IpServer> mNotifyList;
+
+    @Mock private SharedLog mSharedLog;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
+        mNotifyList = new ArrayList<IpServer>();
+        mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mSharedLog);
+    }
+
+    private UpstreamNetworkState createDualStackUpstream(final int transportType) {
+        final Network network = mock(Network.class);
+        final NetworkCapabilities netCap =
+                new NetworkCapabilities.Builder().addTransportType(transportType).build();
+        final InetAddress dns = InetAddresses.parseNumericAddress(TEST_DNS_SERVER);
+        final LinkProperties linkProp = new LinkProperties();
+        linkProp.setInterfaceName(TEST_INTERFACE);
+        linkProp.addLinkAddress(new LinkAddress(TEST_IPV6_ADDRESS));
+        linkProp.addLinkAddress(new LinkAddress(TEST_IPV4_ADDRESS));
+        linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, TEST_INTERFACE, RTN_UNICAST));
+        linkProp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, TEST_INTERFACE,
+                    RTN_UNICAST));
+        linkProp.addDnsServer(dns);
+        return new UpstreamNetworkState(linkProp, netCap, network);
+    }
+
+    private void assertOnlyOneV6AddressAndNoV4(LinkProperties lp) {
+        assertEquals(lp.getInterfaceName(), TEST_INTERFACE);
+        assertFalse(lp.hasIpv4Address());
+        final List<LinkAddress> addresses = lp.getLinkAddresses();
+        assertEquals(addresses.size(), 1);
+        final LinkAddress v6Address = addresses.get(0);
+        assertEquals(v6Address, new LinkAddress(TEST_IPV6_ADDRESS));
+    }
+
+    @Test
+    public void testUpdateIpv6Upstream() throws Exception {
+        // 1. Add first IpServer.
+        final IpServer firstServer = mock(IpServer.class);
+        mNotifyList.add(firstServer);
+        mIPv6TetheringCoordinator.addActiveDownstream(firstServer, STATE_TETHERED);
+        verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+        verifyNoMoreInteractions(firstServer);
+
+        // 2. Add second IpServer and it would not have ipv6 tethering.
+        final IpServer secondServer = mock(IpServer.class);
+        mNotifyList.add(secondServer);
+        mIPv6TetheringCoordinator.addActiveDownstream(secondServer, STATE_LOCAL_ONLY);
+        verifyNoMoreInteractions(secondServer);
+        reset(firstServer, secondServer);
+
+        // 3. No upstream.
+        mIPv6TetheringCoordinator.updateUpstreamNetworkState(null);
+        verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+        reset(firstServer, secondServer);
+
+        // 4. Update ipv6 mobile upstream.
+        final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR);
+        final ArgumentCaptor<LinkProperties> lp = ArgumentCaptor.forClass(LinkProperties.class);
+        mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream);
+        verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0),
+                lp.capture());
+        final LinkProperties v6OnlyLink = lp.getValue();
+        assertOnlyOneV6AddressAndNoV4(v6OnlyLink);
+        verifyNoMoreInteractions(firstServer);
+        verifyNoMoreInteractions(secondServer);
+        reset(firstServer, secondServer);
+
+        // 5. Remove first IpServer.
+        mNotifyList.remove(firstServer);
+        mIPv6TetheringCoordinator.removeActiveDownstream(firstServer);
+        verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+        verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0),
+                lp.capture());
+        final LinkProperties localOnlyLink = lp.getValue();
+        assertNotNull(localOnlyLink);
+        assertNotEquals(localOnlyLink, v6OnlyLink);
+        reset(firstServer, secondServer);
+
+        // 6. Remove second IpServer.
+        mNotifyList.remove(secondServer);
+        mIPv6TetheringCoordinator.removeActiveDownstream(secondServer);
+        verifyNoMoreInteractions(firstServer);
+        verify(secondServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java
new file mode 100644
index 0000000..355ece9
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/MockTetheringService.java
@@ -0,0 +1,56 @@
+/*
+ * 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.server.connectivity.tethering;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Intent;
+import android.net.ITetheringConnector;
+import android.os.Binder;
+import android.os.IBinder;
+
+public class MockTetheringService extends TetheringService {
+    private final Tethering mTethering = mock(Tethering.class);
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new MockTetheringConnector(super.onBind(intent));
+    }
+
+    @Override
+    public Tethering makeTethering(TetheringDependencies deps) {
+        return mTethering;
+    }
+
+    public Tethering getTethering() {
+        return mTethering;
+    }
+
+    public class MockTetheringConnector extends Binder {
+        final IBinder mBase;
+        MockTetheringConnector(IBinder base) {
+            mBase = base;
+        }
+
+        public ITetheringConnector getTetheringConnector() {
+            return ITetheringConnector.Stub.asInterface(mBase);
+        }
+
+        public MockTetheringService getService() {
+            return MockTetheringService.this;
+        }
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java
new file mode 100644
index 0000000..d9d3e73
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringServiceTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.server.connectivity.tethering;
+
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.net.IIntResultListener;
+import android.net.ITetheringConnector;
+import android.net.ITetheringEventCallback;
+import android.net.TetheringRequestParcel;
+import android.os.ResultReceiver;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ServiceTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.connectivity.tethering.MockTetheringService.MockTetheringConnector;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class TetheringServiceTest {
+    private static final String TEST_IFACE_NAME = "test_wlan0";
+    private static final String TEST_CALLER_PKG = "test_pkg";
+    @Mock private ITetheringEventCallback mITetheringEventCallback;
+    @Rule public ServiceTestRule mServiceTestRule;
+    private Tethering mTethering;
+    private Intent mMockServiceIntent;
+    private ITetheringConnector mTetheringConnector;
+
+    private class TestTetheringResult extends IIntResultListener.Stub {
+        private int mResult = -1; // Default value that does not match any result code.
+        @Override
+        public void onResult(final int resultCode) {
+            mResult = resultCode;
+        }
+
+        public void assertResult(final int expected) {
+            assertEquals(expected, mResult);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mServiceTestRule = new ServiceTestRule();
+        mMockServiceIntent = new Intent(
+                InstrumentationRegistry.getTargetContext(),
+                MockTetheringService.class);
+        final MockTetheringConnector mockConnector =
+                (MockTetheringConnector) mServiceTestRule.bindService(mMockServiceIntent);
+        mTetheringConnector = mockConnector.getTetheringConnector();
+        final MockTetheringService service = mockConnector.getService();
+        mTethering = service.getTethering();
+        verify(mTethering).startStateMachineUpdaters();
+        when(mTethering.hasTetherableConfiguration()).thenReturn(true);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mServiceTestRule.unbindService();
+    }
+
+    @Test
+    public void testTether() throws Exception {
+        when(mTethering.tether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
+        final TestTetheringResult result = new TestTetheringResult();
+        mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, result);
+        verify(mTethering).hasTetherableConfiguration();
+        verify(mTethering).tether(TEST_IFACE_NAME);
+        verifyNoMoreInteractions(mTethering);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testUntether() throws Exception {
+        when(mTethering.untether(TEST_IFACE_NAME)).thenReturn(TETHER_ERROR_NO_ERROR);
+        final TestTetheringResult result = new TestTetheringResult();
+        mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, result);
+        verify(mTethering).hasTetherableConfiguration();
+        verify(mTethering).untether(TEST_IFACE_NAME);
+        verifyNoMoreInteractions(mTethering);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testSetUsbTethering() throws Exception {
+        when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR);
+        final TestTetheringResult result = new TestTetheringResult();
+        mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG, result);
+        verify(mTethering).hasTetherableConfiguration();
+        verify(mTethering).setUsbTethering(true /* enable */);
+        verifyNoMoreInteractions(mTethering);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testStartTethering() throws Exception {
+        final TestTetheringResult result = new TestTetheringResult();
+        final TetheringRequestParcel request = new TetheringRequestParcel();
+        request.tetheringType = TETHERING_WIFI;
+        mTetheringConnector.startTethering(request, TEST_CALLER_PKG, result);
+        verify(mTethering).hasTetherableConfiguration();
+        verify(mTethering).startTethering(eq(request), eq(result));
+        verifyNoMoreInteractions(mTethering);
+    }
+
+    @Test
+    public void testStopTethering() throws Exception {
+        final TestTetheringResult result = new TestTetheringResult();
+        mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG, result);
+        verify(mTethering).hasTetherableConfiguration();
+        verify(mTethering).stopTethering(TETHERING_WIFI);
+        verifyNoMoreInteractions(mTethering);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testRequestLatestTetheringEntitlementResult() throws Exception {
+        final ResultReceiver result = new ResultReceiver(null);
+        mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
+                true /* showEntitlementUi */, TEST_CALLER_PKG);
+        verify(mTethering).hasTetherableConfiguration();
+        verify(mTethering).requestLatestTetheringEntitlementResult(eq(TETHERING_WIFI),
+                eq(result), eq(true) /* showEntitlementUi */);
+        verifyNoMoreInteractions(mTethering);
+    }
+
+    @Test
+    public void testRegisterTetheringEventCallback() throws Exception {
+        mTetheringConnector.registerTetheringEventCallback(mITetheringEventCallback,
+                TEST_CALLER_PKG);
+        verify(mTethering).registerTetheringEventCallback(eq(mITetheringEventCallback));
+        verifyNoMoreInteractions(mTethering);
+    }
+
+    @Test
+    public void testUnregisterTetheringEventCallback() throws Exception {
+        mTetheringConnector.unregisterTetheringEventCallback(mITetheringEventCallback,
+                TEST_CALLER_PKG);
+        verify(mTethering).unregisterTetheringEventCallback(
+                eq(mITetheringEventCallback));
+        verifyNoMoreInteractions(mTethering);
+    }
+
+    @Test
+    public void testStopAllTethering() throws Exception {
+        final TestTetheringResult result = new TestTetheringResult();
+        mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, result);
+        verify(mTethering).hasTetherableConfiguration();
+        verify(mTethering).untetherAll();
+        verifyNoMoreInteractions(mTethering);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+
+    @Test
+    public void testIsTetheringSupported() throws Exception {
+        final TestTetheringResult result = new TestTetheringResult();
+        mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, result);
+        verify(mTethering).hasTetherableConfiguration();
+        verifyNoMoreInteractions(mTethering);
+        result.assertResult(TETHER_ERROR_NO_ERROR);
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index a59c6fd..3a580dd 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -140,7 +140,9 @@
 import com.android.testutils.MiscAssertsKt;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -439,6 +441,18 @@
         return buildMobileUpstreamState(false, true, true);
     }
 
+    // See FakeSettingsProvider#clearSettingsProvider() that this needs to be called before and
+    // after use.
+    @BeforeClass
+    public static void setupOnce() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 93bffe9..52a82dd 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -36,10 +36,12 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Slog;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.PackageMonitor;
 
 import libcore.io.IoUtils;
 
@@ -261,22 +263,7 @@
 
             // And reset to the wallpaper service we should be using
             ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
-            if (servicePackageExists(wpService)) {
-                Slog.i(TAG, "Using wallpaper service " + wpService);
-                mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM);
-                if (!lockImageStage.exists()) {
-                    // We have a live wallpaper and no static lock image,
-                    // allow live wallpaper to show "through" on lock screen.
-                    mWm.clear(FLAG_LOCK);
-                }
-            } else {
-                // If we've restored a live wallpaper, but the component doesn't exist,
-                // we should log it as an error so we can easily identify the problem
-                // in reports from users
-                if (wpService != null) {
-                    Slog.e(TAG, "Wallpaper service " + wpService + " isn't available.");
-                }
-            }
+            updateWallpaperComponent(wpService, !lockImageStage.exists());
         } catch (Exception e) {
             Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage());
         } finally {
@@ -293,6 +280,28 @@
         }
     }
 
+    @VisibleForTesting
+    void updateWallpaperComponent(ComponentName wpService, boolean applyToLock) throws IOException {
+        if (servicePackageExists(wpService)) {
+            Slog.i(TAG, "Using wallpaper service " + wpService);
+            mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM);
+            if (applyToLock) {
+                // We have a live wallpaper and no static lock image,
+                // allow live wallpaper to show "through" on lock screen.
+                mWm.clear(FLAG_LOCK);
+            }
+        } else {
+            // If we've restored a live wallpaper, but the component doesn't exist,
+            // we should log it as an error so we can easily identify the problem
+            // in reports from users
+            if (wpService != null) {
+                applyComponentAtInstall(wpService, applyToLock);
+                Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. "
+                        + " Will try to apply later");
+            }
+        }
+    }
+
     private void restoreFromStage(File stage, File info, String hintTag, int which)
             throws IOException {
         if (stage.exists()) {
@@ -372,7 +381,8 @@
         return (value == null) ? defValue : Integer.parseInt(value);
     }
 
-    private boolean servicePackageExists(ComponentName comp) {
+    @VisibleForTesting
+    boolean servicePackageExists(ComponentName comp) {
         try {
             if (comp != null) {
                 final IPackageManager pm = AppGlobals.getPackageManager();
@@ -401,4 +411,53 @@
             throws IOException {
         // Intentionally blank
     }
+
+    private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock) {
+        PackageMonitor packageMonitor = getWallpaperPackageMonitor(componentName, applyToLock);
+        packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true);
+    }
+
+    @VisibleForTesting
+    PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock) {
+        return new PackageMonitor() {
+            @Override
+            public void onPackageAdded(String packageName, int uid) {
+                if (!isDeviceInRestore()) {
+                    // We don't want to reapply the wallpaper outside a restore.
+                    unregister();
+                    return;
+                }
+
+                if (componentName.getPackageName().equals(packageName)) {
+                    Slog.d(TAG, "Applying component " + componentName);
+                    mWm.setWallpaperComponent(componentName);
+                    if (applyToLock) {
+                        try {
+                            mWm.clear(FLAG_LOCK);
+                        } catch (IOException e) {
+                            Slog.w(TAG, "Failed to apply live wallpaper to lock screen: " + e);
+                        }
+                    }
+                    // We're only expecting to restore the wallpaper component once.
+                    unregister();
+                }
+            }
+        };
+    }
+
+    @VisibleForTesting
+    boolean isDeviceInRestore() {
+        try {
+            boolean isInSetup = Settings.Secure.getInt(getBaseContext().getContentResolver(),
+                    Settings.Secure.USER_SETUP_COMPLETE) == 0;
+            boolean isInDeferredSetup = Settings.Secure.getInt(getBaseContext()
+                            .getContentResolver(),
+                    Settings.Secure.USER_SETUP_PERSONALIZATION_STATE) ==
+                    Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED;
+            return isInSetup || isInDeferredSetup;
+        } catch (Settings.SettingNotFoundException e) {
+            Slog.w(TAG, "Failed to check if the user is in restore: " + e);
+            return false;
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
similarity index 65%
rename from packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java
rename to packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 255fdef..4367075 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wallpaperbackup.tests;
+package com.android.wallpaperbackup;
 
 import static android.app.WallpaperManager.FLAG_LOCK;
 import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -26,17 +26,22 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.WallpaperManager;
 import android.app.backup.FullBackupDataOutput;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.UserHandle;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.wallpaperbackup.WallpaperBackupAgent;
 import com.android.wallpaperbackup.utils.ContextWithServiceOverrides;
 
@@ -58,6 +63,7 @@
 public class WallpaperBackupAgentTest {
     private static final String SYSTEM_GENERATION = "system_gen";
     private static final String LOCK_GENERATION = "lock_gen";
+    private static final String TEST_WALLPAPER_PACKAGE = "wallpaper_package";
 
     private static final int TEST_SYSTEM_WALLPAPER_ID = 1;
     private static final int TEST_LOCK_WALLPAPER_ID = 2;
@@ -66,11 +72,13 @@
     @Mock private WallpaperManager mWallpaperManager;
     @Mock private SharedPreferences mSharedPreferences;
     @Mock private SharedPreferences.Editor mSharedPreferenceEditor;
+    @Mock private Context mMockContext;
 
     @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
 
     private ContextWithServiceOverrides mContext;
     private IsolatedWallpaperBackupAgent mWallpaperBackupAgent;
+    private ComponentName mWallpaperComponent;
 
     @Before
     public void setUp() {
@@ -88,6 +96,8 @@
         mWallpaperBackupAgent = new IsolatedWallpaperBackupAgent(mTemporaryFolder.getRoot());
         mWallpaperBackupAgent.attach(mContext);
         mWallpaperBackupAgent.onCreate();
+
+        mWallpaperComponent = new ComponentName(TEST_WALLPAPER_PACKAGE, "");
     }
 
     @Test
@@ -130,6 +140,69 @@
         inOrder.verify(mSharedPreferenceEditor).apply();
     }
 
+    @Test
+    public void updateWallpaperComponent_doesApplyLater() throws IOException {
+        mWallpaperBackupAgent.mIsDeviceInRestore = true;
+
+        mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+                /* applyToLock */ true);
+
+        // Imitate wallpaper component installation.
+        mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+                /* uid */0);
+
+        verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent);
+        verify(mWallpaperManager, times(1)).clear(eq(FLAG_LOCK));
+    }
+
+    @Test
+    public void updateWallpaperComponent_applyToLockFalse_doesApplyLaterOnlyToMainScreen()
+            throws IOException {
+        mWallpaperBackupAgent.mIsDeviceInRestore = true;
+
+        mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+                /* applyToLock */ false);
+
+        // Imitate wallpaper component installation.
+        mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+                /* uid */0);
+
+        verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent);
+        verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
+    }
+
+    @Test
+    public void updateWallpaperComponent_deviceNotInRestore_doesNotApply()
+            throws IOException {
+        mWallpaperBackupAgent.mIsDeviceInRestore = false;
+
+        mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+                /* applyToLock */ true);
+
+        // Imitate wallpaper component installation.
+        mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+                /* uid */0);
+
+        verify(mWallpaperManager, never()).setWallpaperComponent(mWallpaperComponent);
+        verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
+    }
+
+    @Test
+    public void updateWallpaperComponent_differentPackageInstalled_doesNotApply()
+            throws IOException {
+        mWallpaperBackupAgent.mIsDeviceInRestore = false;
+
+        mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+                /* applyToLock */ true);
+
+        // Imitate "wrong" wallpaper component installation.
+        mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(/* packageName */"",
+                /* uid */0);
+
+        verify(mWallpaperManager, never()).setWallpaperComponent(mWallpaperComponent);
+        verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK));
+    }
+
     private void mockUnbackedUpState() {
         mockCurrentWallpapers(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
         when(mSharedPreferences.getInt(eq(SYSTEM_GENERATION), eq(-1))).thenReturn(-1);
@@ -162,6 +235,8 @@
     private class IsolatedWallpaperBackupAgent extends WallpaperBackupAgent {
         File mWallpaperBaseDirectory;
         List<File> mBackedUpFiles = new ArrayList<>();
+        PackageMonitor mWallpaperPackageMonitor;
+        boolean mIsDeviceInRestore = false;
 
         IsolatedWallpaperBackupAgent(File wallpaperBaseDirectory) {
             mWallpaperBaseDirectory = wallpaperBaseDirectory;
@@ -181,5 +256,27 @@
         public SharedPreferences getSharedPreferences(File file, int mode) {
             return mSharedPreferences;
         }
+
+        @Override
+        boolean servicePackageExists(ComponentName comp) {
+            return false;
+        }
+
+        @Override
+        boolean isDeviceInRestore() {
+            return mIsDeviceInRestore;
+        }
+
+        @Override
+        PackageMonitor getWallpaperPackageMonitor(ComponentName componentName,
+                boolean applyToLock) {
+            mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, applyToLock);
+            return mWallpaperPackageMonitor;
+        }
+
+        @Override
+        public Context getBaseContext() {
+            return mMockContext;
+        }
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index c8b6d8df..132b692 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -43,12 +43,10 @@
 import android.content.pm.ParceledListSlice;
 import android.graphics.GraphicBuffer;
 import android.graphics.ParcelableColorSpace;
-import android.graphics.Point;
-import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.DisplayManagerInternal;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -66,7 +64,6 @@
 import android.view.Display;
 import android.view.KeyEvent;
 import android.view.MagnificationSpec;
-import android.view.SurfaceControl;
 import android.view.SurfaceControl.ScreenshotGraphicBuffer;
 import android.view.View;
 import android.view.WindowInfo;
@@ -1010,53 +1007,29 @@
             return;
         }
 
-        final Display display = DisplayManagerGlobal.getInstance()
-                .getRealDisplay(displayId);
-        if (display == null) {
-            sendScreenshotFailure(AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY,
-                    callback);
+        // Private virtual displays are created by the ap and is not allowed to access by other
+        // aps.  We assume the contents on this display should not be captured.
+        final DisplayManager displayManager =
+                (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+        final Display display = displayManager.getDisplay(displayId);
+        if ((display == null) || (display.getType() == Display.TYPE_VIRTUAL
+                && (display.getFlags() & Display.FLAG_PRIVATE) != 0)) {
+            sendScreenshotFailure(
+                    AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
             return;
         }
 
-        sendScreenshotSuccess(display, callback);
-    }
-
-    private ScreenshotGraphicBuffer takeScreenshotBuffer(Display display) {
-        final Point displaySize = new Point();
-        // TODO (b/145893483): calling new API with the display as a parameter
-        // when surface control supported.
-        final IBinder token = SurfaceControl.getInternalDisplayToken();
-        final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
-        final int rotation = display.getRotation();
-        display.getRealSize(displaySize);
-
-        return SurfaceControl.screenshotToBuffer(token, crop, displaySize.x, displaySize.y,
-                false, rotation);
-    }
-
-    private void sendScreenshotSuccess(Display display, RemoteCallback callback) {
         final long identity = Binder.clearCallingIdentity();
         try {
             mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
-                final ScreenshotGraphicBuffer screenshotBuffer = takeScreenshotBuffer(display);
-                final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer();
-                try (HardwareBuffer hardwareBuffer =
-                             HardwareBuffer.createFromGraphicBuffer(graphicBuffer)) {
-                    final ParcelableColorSpace colorSpace =
-                            new ParcelableColorSpace(screenshotBuffer.getColorSpace());
-
-                    final Bundle payload = new Bundle();
-                    payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
-                            AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
-                    payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
-                            hardwareBuffer);
-                    payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
-                    payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
-                            SystemClock.uptimeMillis());
-
-                    // Send back the result.
-                    callback.sendResult(payload);
-                    hardwareBuffer.close();
+                final ScreenshotGraphicBuffer screenshotBuffer = LocalServices
+                        .getService(DisplayManagerInternal.class)
+                        .screenshotWithoutSecureLayer(displayId);
+                if (screenshotBuffer != null) {
+                    sendScreenshotSuccess(screenshotBuffer, callback);
+                } else {
+                    sendScreenshotFailure(
+                            AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY, callback);
                 }
             }, null).recycleOnUse());
         } finally {
@@ -1064,6 +1037,29 @@
         }
     }
 
+    private void sendScreenshotSuccess(ScreenshotGraphicBuffer screenshotBuffer,
+            RemoteCallback callback) {
+        final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer();
+        try (HardwareBuffer hardwareBuffer =
+                     HardwareBuffer.createFromGraphicBuffer(graphicBuffer)) {
+            final ParcelableColorSpace colorSpace =
+                    new ParcelableColorSpace(screenshotBuffer.getColorSpace());
+
+            final Bundle payload = new Bundle();
+            payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS,
+                    AccessibilityService.TAKE_SCREENSHOT_SUCCESS);
+            payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
+                    hardwareBuffer);
+            payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
+            payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP,
+                    SystemClock.uptimeMillis());
+
+            // Send back the result.
+            callback.sendResult(payload);
+            hardwareBuffer.close();
+        }
+    }
+
     private void sendScreenshotFailure(@AccessibilityService.ScreenshotErrorCode int errorCode,
             RemoteCallback callback) {
         mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1b180e3..7230b00 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -197,7 +197,8 @@
 
     private final MainHandler mMainHandler;
 
-    private final SystemActionPerformer mSystemActionPerformer;
+    // Lazily initialized - access through getSystemActionPerfomer()
+    private SystemActionPerformer mSystemActionPerformer;
 
     private MagnificationController mMagnificationController;
 
@@ -295,8 +296,6 @@
         mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
         mPackageManager = mContext.getPackageManager();
         mSecurityPolicy = new AccessibilitySecurityPolicy(mContext, this);
-        mSystemActionPerformer =
-                new SystemActionPerformer(mContext, mWindowManagerService, null, this);
         mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
                 mWindowManagerService, this, mSecurityPolicy, this);
         mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
@@ -414,7 +413,12 @@
                                     && component.getPackageName().equals(packageName))
                             || userState.mCrashedServices.removeIf(component -> component != null
                                     && component.getPackageName().equals(packageName));
-                    if (reboundAService) {
+                    // Reloads the installed services info to make sure the rebound service could
+                    // get a new one.
+                    userState.mInstalledServices.clear();
+                    final boolean configurationChanged =
+                            readConfigurationForUserStateLocked(userState);
+                    if (reboundAService || configurationChanged) {
                         onUserStateChangedLocked(userState);
                     }
                     migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName);
@@ -667,7 +671,7 @@
         mSecurityPolicy.enforceCallerIsRecentsOrHasPermission(
                 Manifest.permission.MANAGE_ACCESSIBILITY,
                 FUNCTION_REGISTER_SYSTEM_ACTION);
-        mSystemActionPerformer.registerSystemAction(actionId, action);
+        getSystemActionPerformer().registerSystemAction(actionId, action);
     }
 
     /**
@@ -680,7 +684,15 @@
         mSecurityPolicy.enforceCallerIsRecentsOrHasPermission(
                 Manifest.permission.MANAGE_ACCESSIBILITY,
                 FUNCTION_UNREGISTER_SYSTEM_ACTION);
-        mSystemActionPerformer.unregisterSystemAction(actionId);
+        getSystemActionPerformer().unregisterSystemAction(actionId);
+    }
+
+    private SystemActionPerformer getSystemActionPerformer() {
+        if (mSystemActionPerformer == null) {
+            mSystemActionPerformer =
+                    new SystemActionPerformer(mContext, mWindowManagerService, null, this);
+        }
+        return mSystemActionPerformer;
     }
 
     @Override
@@ -792,7 +804,7 @@
         synchronized (mLock) {
             mUiAutomationManager.registerUiTestAutomationServiceLocked(owner, serviceClient,
                     mContext, accessibilityServiceInfo, sIdCounter++, mMainHandler,
-                    mSecurityPolicy, this, mWindowManagerService, mSystemActionPerformer,
+                    mSecurityPolicy, this, mWindowManagerService, getSystemActionPerformer(),
                     mA11yWindowManager, flags);
             onUserStateChangedLocked(getCurrentUserStateLocked());
         }
@@ -1503,7 +1515,7 @@
                 if (service == null) {
                     service = new AccessibilityServiceConnection(userState, mContext, componentName,
                             installedService, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
-                            this, mWindowManagerService, mSystemActionPerformer,
+                            this, mWindowManagerService, getSystemActionPerformer(),
                             mA11yWindowManager, mActivityTaskManagerService);
                 } else if (userState.mBoundServices.contains(service)) {
                     continue;
@@ -2741,7 +2753,7 @@
                     userState, mContext,
                     COMPONENT_NAME, info, sIdCounter++, mMainHandler, mLock, mSecurityPolicy,
                     AccessibilityManagerService.this, mWindowManagerService,
-                    mSystemActionPerformer, mA11yWindowManager, mActivityTaskManagerService) {
+                    getSystemActionPerformer(), mA11yWindowManager, mActivityTaskManagerService) {
                 @Override
                 public boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
                     return true;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
index ff59c24..20a11bd 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java
@@ -17,6 +17,7 @@
 package com.android.server.accessibility;
 
 import android.annotation.NonNull;
+import android.app.ActivityManager;
 import android.os.ShellCommand;
 import android.os.UserHandle;
 
@@ -83,7 +84,7 @@
                 return null;
             }
         }
-        return UserHandle.USER_SYSTEM;
+        return ActivityManager.getCurrentUser();
     }
 
     @Override
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
new file mode 100644
index 0000000..3612e09
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java
@@ -0,0 +1,131 @@
+/*
+ * 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.server.autofill;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+
+/**
+ * Controls the interaction with the IME for the inline suggestion sessions.
+ */
+final class AutofillInlineSessionController {
+    @NonNull
+    private final InputMethodManagerInternal mInputMethodManagerInternal;
+    private final int mUserId;
+    @NonNull
+    private final ComponentName mComponentName;
+    @NonNull
+    private final Object mLock;
+    @NonNull
+    private final Handler mHandler;
+
+    @GuardedBy("mLock")
+    private AutofillInlineSuggestionsRequestSession mSession;
+
+    AutofillInlineSessionController(InputMethodManagerInternal inputMethodManagerInternal,
+            int userId, ComponentName componentName, Handler handler, Object lock) {
+        mInputMethodManagerInternal = inputMethodManagerInternal;
+        mUserId = userId;
+        mComponentName = componentName;
+        mHandler = handler;
+        mLock = lock;
+    }
+
+
+    /**
+     * Requests the IME to create an {@link InlineSuggestionsRequest} for {@code autofillId}.
+     *
+     * @param autofillId      the Id of the field for which the request is for.
+     * @param requestConsumer the callback which will be invoked when IME responded or if it times
+     *                        out waiting for IME response.
+     */
+    @GuardedBy("mLock")
+    void onCreateInlineSuggestionsRequestLocked(@NonNull AutofillId autofillId,
+            @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
+        // TODO(b/151123764): rename the method to better reflect what it does.
+        if (mSession != null) {
+            // Send an empty response to IME and destroy the existing session.
+            mSession.onInlineSuggestionsResponseLocked(mSession.getAutofillIdLocked(),
+                    new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+            mSession.destroySessionLocked();
+        }
+        // TODO(b/151123764): consider reusing the same AutofillInlineSession object for the
+        // same field.
+        mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId,
+                mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras);
+        mSession.onCreateInlineSuggestionsRequestLocked();
+
+    }
+
+    /**
+     * Returns the {@link InlineSuggestionsRequest} provided by IME for the last request.
+     *
+     * <p> The caller is responsible for making sure Autofill hears back from IME before calling
+     * this method, using the {@code requestConsumer} provided when calling {@link
+     * #onCreateInlineSuggestionsRequestLocked(AutofillId, Consumer, Bundle)}.
+     */
+    @GuardedBy("mLock")
+    Optional<InlineSuggestionsRequest> getInlineSuggestionsRequestLocked() {
+        if (mSession != null) {
+            return mSession.getInlineSuggestionsRequestLocked();
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Requests the IME to hide the current suggestions, if any. Returns true if the message is sent
+     * to the IME.
+     */
+    @GuardedBy("mLock")
+    boolean hideInlineSuggestionsUiLocked(@NonNull AutofillId autofillId) {
+        if (mSession != null) {
+            return mSession.onInlineSuggestionsResponseLocked(autofillId,
+                    new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+        }
+        return false;
+    }
+
+    /**
+     * Requests showing the inline suggestion in the IME when the IME becomes visible and is focused
+     * on the {@code autofillId}.
+     *
+     * @return false if there is no session, or if the IME callback is not available in the session.
+     */
+    @GuardedBy("mLock")
+    boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
+            @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
+        // TODO(b/151123764): rename the method to better reflect what it does.
+        if (mSession != null) {
+            return mSession.onInlineSuggestionsResponseLocked(autofillId,
+                    inlineSuggestionsResponse);
+        }
+        return false;
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
new file mode 100644
index 0000000..ca230b6
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -0,0 +1,419 @@
+/*
+ * 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.server.autofill;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.BinderThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.lang.ref.WeakReference;
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Maintains an inline suggestion session with the IME.
+ *
+ * <p> Each session corresponds to one request from the Autofill manager service to create an
+ * {@link InlineSuggestionsRequest}. It's responsible for receiving callbacks from the IME and
+ * sending {@link android.view.inputmethod.InlineSuggestionsResponse} to IME.
+ */
+final class AutofillInlineSuggestionsRequestSession {
+
+    private static final String TAG = AutofillInlineSuggestionsRequestSession.class.getSimpleName();
+    private static final int INLINE_REQUEST_TIMEOUT_MS = 200;
+
+    @NonNull
+    private final InputMethodManagerInternal mInputMethodManagerInternal;
+    private final int mUserId;
+    @NonNull
+    private final ComponentName mComponentName;
+    @NonNull
+    private final Object mLock;
+    @NonNull
+    private final Handler mHandler;
+    @NonNull
+    private final Bundle mUiExtras;
+
+    @GuardedBy("mLock")
+    @NonNull
+    private AutofillId mAutofillId;
+    @GuardedBy("mLock")
+    @Nullable
+    private Consumer<InlineSuggestionsRequest> mImeRequestConsumer;
+
+    @GuardedBy("mLock")
+    private boolean mImeRequestReceived;
+    @GuardedBy("mLock")
+    @Nullable
+    private InlineSuggestionsRequest mImeRequest;
+    @GuardedBy("mLock")
+    @Nullable
+    private IInlineSuggestionsResponseCallback mResponseCallback;
+    @GuardedBy("mLock")
+    @Nullable
+    private Runnable mTimeoutCallback;
+
+    @GuardedBy("mLock")
+    @Nullable
+    private AutofillId mImeCurrentFieldId;
+    @GuardedBy("mLock")
+    private boolean mImeInputStarted;
+    @GuardedBy("mLock")
+    private boolean mImeInputViewStarted;
+    @GuardedBy("mLock")
+    @Nullable
+    private InlineSuggestionsResponse mInlineSuggestionsResponse;
+    @GuardedBy("mLock")
+    private boolean mPreviousResponseIsNotEmpty;
+
+    @GuardedBy("mLock")
+    private boolean mDestroyed = false;
+
+    AutofillInlineSuggestionsRequestSession(
+            @NonNull InputMethodManagerInternal inputMethodManagerInternal, int userId,
+            @NonNull ComponentName componentName, @NonNull Handler handler, @NonNull Object lock,
+            @NonNull AutofillId autofillId,
+            @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) {
+        mInputMethodManagerInternal = inputMethodManagerInternal;
+        mUserId = userId;
+        mComponentName = componentName;
+        mHandler = handler;
+        mLock = lock;
+        mUiExtras = uiExtras;
+
+        mAutofillId = autofillId;
+        mImeRequestConsumer = requestConsumer;
+    }
+
+    @GuardedBy("mLock")
+    @NonNull
+    AutofillId getAutofillIdLocked() {
+        return mAutofillId;
+    }
+
+    /**
+     * Returns the {@link InlineSuggestionsRequest} provided by IME.
+     *
+     * <p> The caller is responsible for making sure Autofill hears back from IME before calling
+     * this method, using the {@link #mImeRequestConsumer}.
+     */
+    @GuardedBy("mLock")
+    Optional<InlineSuggestionsRequest> getInlineSuggestionsRequestLocked() {
+        if (mDestroyed) {
+            return Optional.empty();
+        }
+        return Optional.ofNullable(mImeRequest);
+    }
+
+    /**
+     * Requests showing the inline suggestion in the IME when the IME becomes visible and is focused
+     * on the {@code autofillId}.
+     *
+     * @return false if the IME callback is not available.
+     */
+    @GuardedBy("mLock")
+    boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
+            @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
+        if (mDestroyed) {
+            return false;
+        }
+        if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked called for:" + autofillId);
+        if (mImeRequest == null || mResponseCallback == null) {
+            return false;
+        }
+        // TODO(b/151123764): each session should only correspond to one field.
+        mAutofillId = autofillId;
+        mInlineSuggestionsResponse = inlineSuggestionsResponse;
+        maybeUpdateResponseToImeLocked();
+        return true;
+    }
+
+    /**
+     * This method must be called when the session is destroyed, to avoid further callbacks from/to
+     * the IME.
+     */
+    @GuardedBy("mLock")
+    void destroySessionLocked() {
+        mDestroyed = true;
+    }
+
+    /**
+     * Requests the IME to create an {@link InlineSuggestionsRequest}.
+     *
+     * <p> This method should only be called once per session.
+     */
+    @GuardedBy("mLock")
+    void onCreateInlineSuggestionsRequestLocked() {
+        if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequestLocked called: " + mAutofillId);
+        if (mDestroyed) {
+            return;
+        }
+        mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(mUserId,
+                new InlineSuggestionsRequestInfo(mComponentName, mAutofillId, mUiExtras),
+                new InlineSuggestionsRequestCallbackImpl(this));
+        mTimeoutCallback = () -> {
+            Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
+            handleOnReceiveImeRequest(null, null);
+        };
+        mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
+    }
+
+    /**
+     * Optionally sends inline response to the IME, depending on the current state.
+     */
+    @GuardedBy("mLock")
+    private void maybeUpdateResponseToImeLocked() {
+        if (sDebug) Log.d(TAG, "maybeUpdateResponseToImeLocked called");
+        if (mDestroyed || mResponseCallback == null) {
+            return;
+        }
+        if (!mImeInputViewStarted && mPreviousResponseIsNotEmpty) {
+            // 1. if previous response is not empty, and IME just become invisible, then send
+            // empty response to make sure existing responses don't stick around on the IME.
+            // Although the inline suggestions should disappear when IME hides which removes them
+            // from the view hierarchy, but we still send an empty response to be extra safe.
+
+            // TODO(b/149945531): clear the existing suggestions when IME is hide, once the bug is
+            //  fixed.
+            //if (sDebug) Log.d(TAG, "Send empty inline response");
+            //updateResponseToImeUncheckLocked(new InlineSuggestionsResponse(Collections
+            // .EMPTY_LIST));
+            //mPreviousResponseIsNotEmpty = false;
+        } else if (mImeInputViewStarted && mInlineSuggestionsResponse != null && match(mAutofillId,
+                mImeCurrentFieldId)) {
+            // 2. if IME is visible, and response is not null, send the response
+            boolean isEmptyResponse = mInlineSuggestionsResponse.getInlineSuggestions().isEmpty();
+            if (isEmptyResponse && !mPreviousResponseIsNotEmpty) {
+                // No-op if both the previous response and current response are empty.
+                return;
+            }
+            if (sDebug) {
+                Log.d(TAG, "Send inline response: "
+                        + mInlineSuggestionsResponse.getInlineSuggestions().size());
+            }
+            updateResponseToImeUncheckLocked(mInlineSuggestionsResponse);
+            // TODO(b/149945531): don't set the response to null so it's cached, once the bug is
+            //  fixed.
+            mInlineSuggestionsResponse = null;
+            mPreviousResponseIsNotEmpty = !isEmptyResponse;
+        }
+    }
+
+    /**
+     * Sends the {@code response} to the IME, assuming all the relevant checks are already done.
+     */
+    @GuardedBy("mLock")
+    private void updateResponseToImeUncheckLocked(InlineSuggestionsResponse response) {
+        if (mDestroyed) {
+            return;
+        }
+        try {
+            mResponseCallback.onInlineSuggestionsResponse(mAutofillId, response);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME");
+        }
+    }
+
+    /**
+     * Handles the {@code request} and {@code callback} received from the IME.
+     *
+     * <p> Should only invoked in the {@link #mHandler} thread.
+     */
+    private void handleOnReceiveImeRequest(@Nullable InlineSuggestionsRequest request,
+            @Nullable IInlineSuggestionsResponseCallback callback) {
+        synchronized (mLock) {
+            if (mDestroyed || mImeRequestReceived) {
+                return;
+            }
+            mImeRequestReceived = true;
+
+            if (mTimeoutCallback != null) {
+                if (sDebug) Log.d(TAG, "removing timeout callback");
+                mHandler.removeCallbacks(mTimeoutCallback);
+                mTimeoutCallback = null;
+            }
+            if (request != null && callback != null) {
+                mImeRequest = request;
+                mResponseCallback = callback;
+                handleOnReceiveImeStatusUpdated(mAutofillId, true, false);
+            }
+            if (mImeRequestConsumer != null) {
+                // Note that mImeRequest is only set if both request and callback are non-null.
+                mImeRequestConsumer.accept(mImeRequest);
+                mImeRequestConsumer = null;
+            }
+        }
+    }
+
+    /**
+     * Handles the IME status updates received from the IME.
+     *
+     * <p> Should only be invoked in the {@link #mHandler} thread.
+     */
+    private void handleOnReceiveImeStatusUpdated(boolean imeInputStarted,
+            boolean imeInputViewStarted) {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                return;
+            }
+            if (mImeCurrentFieldId != null) {
+                boolean imeInputStartedChanged = (mImeInputStarted != imeInputStarted);
+                boolean imeInputViewStartedChanged = (mImeInputViewStarted != imeInputViewStarted);
+                mImeInputStarted = imeInputStarted;
+                mImeInputViewStarted = imeInputViewStarted;
+                if (imeInputStartedChanged || imeInputViewStartedChanged) {
+                    maybeUpdateResponseToImeLocked();
+                }
+            }
+        }
+    }
+
+    /**
+     * Handles the IME status updates received from the IME.
+     *
+     * <p> Should only be invoked in the {@link #mHandler} thread.
+     */
+    private void handleOnReceiveImeStatusUpdated(@Nullable AutofillId imeFieldId,
+            boolean imeInputStarted, boolean imeInputViewStarted) {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                return;
+            }
+            if (imeFieldId != null) {
+                mImeCurrentFieldId = imeFieldId;
+            }
+            handleOnReceiveImeStatusUpdated(imeInputStarted, imeInputViewStarted);
+        }
+    }
+
+    private static final class InlineSuggestionsRequestCallbackImpl extends
+            IInlineSuggestionsRequestCallback.Stub {
+
+        private final WeakReference<AutofillInlineSuggestionsRequestSession> mSession;
+
+        private InlineSuggestionsRequestCallbackImpl(
+                AutofillInlineSuggestionsRequestSession session) {
+            mSession = new WeakReference<>(session);
+        }
+
+        @BinderThread
+        @Override
+        public void onInlineSuggestionsUnsupported() throws RemoteException {
+            if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
+            final AutofillInlineSuggestionsRequestSession session = mSession.get();
+            if (session != null) {
+                session.mHandler.sendMessage(obtainMessage(
+                        AutofillInlineSuggestionsRequestSession::handleOnReceiveImeRequest, session,
+                        null, null));
+            }
+        }
+
+        @BinderThread
+        @Override
+        public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+                IInlineSuggestionsResponseCallback callback) {
+            if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
+            final AutofillInlineSuggestionsRequestSession session = mSession.get();
+            if (session != null) {
+                session.mHandler.sendMessage(obtainMessage(
+                        AutofillInlineSuggestionsRequestSession::handleOnReceiveImeRequest, session,
+                        request, callback));
+            }
+        }
+
+        @Override
+        public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+            if (sDebug) Log.d(TAG, "onInputMethodStartInput() received on " + imeFieldId);
+            final AutofillInlineSuggestionsRequestSession session = mSession.get();
+            if (session != null) {
+                session.mHandler.sendMessage(obtainMessage(
+                        AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+                        session, imeFieldId, true, false));
+            }
+        }
+
+        @Override
+        public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+            if (sDebug) {
+                Log.d(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
+            }
+        }
+
+        @BinderThread
+        @Override
+        public void onInputMethodStartInputView() {
+            if (sDebug) Log.d(TAG, "onInputMethodStartInputView() received");
+            final AutofillInlineSuggestionsRequestSession session = mSession.get();
+            if (session != null) {
+                session.mHandler.sendMessage(obtainMessage(
+                        AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+                        session, true, true));
+            }
+        }
+
+        @BinderThread
+        @Override
+        public void onInputMethodFinishInputView() {
+            if (sDebug) Log.d(TAG, "onInputMethodFinishInputView() received");
+            final AutofillInlineSuggestionsRequestSession session = mSession.get();
+            if (session != null) {
+                session.mHandler.sendMessage(obtainMessage(
+                        AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+                        session, true, false));
+            }
+        }
+
+        @Override
+        public void onInputMethodFinishInput() throws RemoteException {
+            if (sDebug) Log.d(TAG, "onInputMethodFinishInput() received");
+            final AutofillInlineSuggestionsRequestSession session = mSession.get();
+            if (session != null) {
+                session.mHandler.sendMessage(obtainMessage(
+                        AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
+                        session, false, false));
+            }
+        }
+    }
+
+    private static boolean match(@Nullable AutofillId autofillId,
+            @Nullable AutofillId imeClientFieldId) {
+        // The IME doesn't have information about the virtual view id for the child views in the
+        // web view, so we are only comparing the parent view id here. This means that for cases
+        // where there are two input fields in the web view, they will have the same view id
+        // (although different virtual child id), and we will not be able to distinguish them.
+        return autofillId != null && imeClientFieldId != null
+                && autofillId.getViewId() == imeClientFieldId.getViewId();
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
deleted file mode 100644
index e2d5112..0000000
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * 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.server.autofill;
-
-import static com.android.server.autofill.Helper.sDebug;
-
-import android.annotation.BinderThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-import android.view.autofill.AutofillId;
-import android.view.inputmethod.InlineSuggestionsRequest;
-import android.view.inputmethod.InlineSuggestionsResponse;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInlineSuggestionsResponseCallback;
-import com.android.internal.view.InlineSuggestionsRequestInfo;
-import com.android.server.inputmethod.InputMethodManagerInternal;
-
-import java.util.Collections;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-
-/**
- * Maintains an autofill inline suggestion session that communicates with the IME.
- *
- * <p>
- * The same session may be reused for multiple input fields involved in the same autofill
- * {@link Session}. Therefore, one {@link InlineSuggestionsRequest} and one
- * {@link IInlineSuggestionsResponseCallback} may be used to generate and callback with inline
- * suggestions for different input fields.
- *
- * <p>
- * This class is the sole place in Autofill responsible for directly communicating with the IME. It
- * receives the IME input view start/finish events, with the associated IME field Id. It uses the
- * information to decide when to send the {@link InlineSuggestionsResponse} to IME. As a result,
- * some of the response will be cached locally and only be sent when the IME is ready to show them.
- *
- * <p>
- * See {@link android.inputmethodservice.InlineSuggestionSession} comments for InputMethodService
- * side flow.
- *
- * <p>
- * This class should hold the same lock as {@link Session} as they call into each other.
- */
-final class InlineSuggestionSession {
-
-    private static final String TAG = "AfInlineSuggestionSession";
-    private static final int INLINE_REQUEST_TIMEOUT_MS = 200;
-
-    @NonNull
-    private final InputMethodManagerInternal mInputMethodManagerInternal;
-    private final int mUserId;
-    @NonNull
-    private final ComponentName mComponentName;
-    @NonNull
-    private final Object mLock;
-    @NonNull
-    private final ImeStatusListener mImeStatusListener;
-    @NonNull
-    private final Handler mHandler;
-
-    /**
-     * To avoid the race condition, one should not access {@code mPendingImeResponse} without
-     * holding the {@code mLock}. For consuming the existing value, tt's recommended to use
-     * {@link #getPendingImeResponse()} to get a copy of the reference to avoid blocking call.
-     */
-    @GuardedBy("mLock")
-    @Nullable
-    private CompletableFuture<ImeResponse> mPendingImeResponse;
-
-    @GuardedBy("mLock")
-    @Nullable
-    private AutofillResponse mPendingAutofillResponse;
-
-    @GuardedBy("mLock")
-    private boolean mIsLastResponseNonEmpty = false;
-
-    @Nullable
-    @GuardedBy("mLock")
-    private AutofillId mImeFieldId = null;
-
-    @GuardedBy("mLock")
-    private boolean mImeInputViewStarted = false;
-
-    InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
-            int userId, ComponentName componentName, Handler handler, Object lock) {
-        mInputMethodManagerInternal = inputMethodManagerInternal;
-        mUserId = userId;
-        mComponentName = componentName;
-        mHandler = handler;
-        mLock = lock;
-        mImeStatusListener = new ImeStatusListener() {
-            @Override
-            public void onInputMethodStartInput(AutofillId imeFieldId) {
-                synchronized (mLock) {
-                    mImeFieldId = imeFieldId;
-                    mImeInputViewStarted = false;
-                }
-            }
-
-            @Override
-            public void onInputMethodStartInputView() {
-                synchronized (mLock) {
-                    mImeInputViewStarted = true;
-                    AutofillResponse pendingAutofillResponse = mPendingAutofillResponse;
-                    if (pendingAutofillResponse != null
-                            && pendingAutofillResponse.mAutofillId.equalsIgnoreSession(
-                            mImeFieldId)) {
-                        mPendingAutofillResponse = null;
-                        onInlineSuggestionsResponseLocked(pendingAutofillResponse.mAutofillId,
-                                pendingAutofillResponse.mResponse);
-                    }
-                }
-            }
-
-            @Override
-            public void onInputMethodFinishInputView() {
-                synchronized (mLock) {
-                    mImeInputViewStarted = false;
-                }
-            }
-
-            @Override
-            public void onInputMethodFinishInput() {
-                synchronized (mLock) {
-                    mImeFieldId = null;
-                }
-            }
-        };
-    }
-
-    public void onCreateInlineSuggestionsRequest(@NonNull AutofillId autofillId,
-            @NonNull Consumer<InlineSuggestionsRequest> requestConsumer) {
-        if (sDebug) Log.d(TAG, "onCreateInlineSuggestionsRequest called for " + autofillId);
-
-        synchronized (mLock) {
-            // Clean up all the state about the previous request.
-            hideInlineSuggestionsUi(autofillId);
-            mImeFieldId = null;
-            mImeInputViewStarted = false;
-            if (mPendingImeResponse != null && !mPendingImeResponse.isDone()) {
-                mPendingImeResponse.complete(null);
-            }
-            mPendingImeResponse = new CompletableFuture<>();
-            // TODO(b/146454892): pipe the uiExtras from the ExtServices.
-            mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
-                    mUserId,
-                    new InlineSuggestionsRequestInfo(mComponentName, autofillId, new Bundle()),
-                    new InlineSuggestionsRequestCallbackImpl(autofillId, mPendingImeResponse,
-                            mImeStatusListener, requestConsumer, mHandler, mLock));
-        }
-    }
-
-    public Optional<InlineSuggestionsRequest> getInlineSuggestionsRequest() {
-        final CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
-        if (pendingImeResponse == null || !pendingImeResponse.isDone()) {
-            return Optional.empty();
-        }
-        return Optional.ofNullable(pendingImeResponse.getNow(null)).map(ImeResponse::getRequest);
-    }
-
-    public boolean hideInlineSuggestionsUi(@NonNull AutofillId autofillId) {
-        synchronized (mLock) {
-            if (mIsLastResponseNonEmpty) {
-                return onInlineSuggestionsResponseLocked(autofillId,
-                        new InlineSuggestionsResponse(Collections.EMPTY_LIST));
-            }
-            return false;
-        }
-    }
-
-    public boolean onInlineSuggestionsResponse(@NonNull AutofillId autofillId,
-            @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
-        synchronized (mLock) {
-            return onInlineSuggestionsResponseLocked(autofillId, inlineSuggestionsResponse);
-        }
-    }
-
-    private boolean onInlineSuggestionsResponseLocked(@NonNull AutofillId autofillId,
-            @NonNull InlineSuggestionsResponse inlineSuggestionsResponse) {
-        final CompletableFuture<ImeResponse> completedImsResponse = getPendingImeResponse();
-        if (completedImsResponse == null || !completedImsResponse.isDone()) {
-            if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked without IMS request");
-            return false;
-        }
-        // There is no need to wait on the CompletableFuture since it should have been completed.
-        ImeResponse imeResponse = completedImsResponse.getNow(null);
-        if (imeResponse == null) {
-            if (sDebug) Log.d(TAG, "onInlineSuggestionsResponseLocked with pending IMS response");
-            return false;
-        }
-
-        // TODO(b/151846600): IME doesn't have access to the virtual id of the webview, so we
-        //  only compare the view id for now.
-        if (!mImeInputViewStarted || mImeFieldId == null
-                || autofillId.getViewId() != mImeFieldId.getViewId()) {
-            if (sDebug) {
-                Log.d(TAG,
-                        "onInlineSuggestionsResponseLocked not sent because input view is not "
-                                + "started for " + autofillId);
-            }
-            mPendingAutofillResponse = new AutofillResponse(autofillId, inlineSuggestionsResponse);
-            // TODO(b/149442582): Although we are not sending the response to IME right away, we
-            //  still return true to indicate that the response may be sent eventually, such that
-            //  the dropdown UI will not be shown. This may not be the desired behavior in the
-            //  auto-focus case where IME isn't shown after switching back to an activity. We may
-            //  revisit this.
-            return true;
-        }
-
-        try {
-            imeResponse.mCallback.onInlineSuggestionsResponse(autofillId,
-                    inlineSuggestionsResponse);
-            mIsLastResponseNonEmpty = !inlineSuggestionsResponse.getInlineSuggestions().isEmpty();
-            if (sDebug) {
-                Log.d(TAG, "Autofill sends inline response to IME: "
-                        + inlineSuggestionsResponse.getInlineSuggestions().size());
-            }
-            return true;
-        } catch (RemoteException e) {
-            Slog.e(TAG, "RemoteException sending InlineSuggestionsResponse to IME");
-            return false;
-        }
-    }
-
-    @Nullable
-    @GuardedBy("mLock")
-    private CompletableFuture<ImeResponse> getPendingImeResponse() {
-        synchronized (mLock) {
-            return mPendingImeResponse;
-        }
-    }
-
-    private static final class InlineSuggestionsRequestCallbackImpl
-            extends IInlineSuggestionsRequestCallback.Stub {
-
-        private final Object mLock;
-        private final AutofillId mAutofillId;
-        @GuardedBy("mLock")
-        private final CompletableFuture<ImeResponse> mResponse;
-        @GuardedBy("mLock")
-        private final Consumer<InlineSuggestionsRequest> mRequestConsumer;
-        private final ImeStatusListener mImeStatusListener;
-        private final Handler mHandler;
-        private final Runnable mTimeoutCallback;
-
-        private InlineSuggestionsRequestCallbackImpl(AutofillId autofillId,
-                CompletableFuture<ImeResponse> response,
-                ImeStatusListener imeStatusListener,
-                Consumer<InlineSuggestionsRequest> requestConsumer,
-                Handler handler, Object lock) {
-            mAutofillId = autofillId;
-            mResponse = response;
-            mImeStatusListener = imeStatusListener;
-            mRequestConsumer = requestConsumer;
-            mLock = lock;
-
-            mHandler = handler;
-            mTimeoutCallback = () -> {
-                Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
-                completeIfNot(null);
-            };
-            mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
-        }
-
-        private void completeIfNot(@Nullable ImeResponse response) {
-            synchronized (mLock) {
-                if (mResponse.isDone()) {
-                    return;
-                }
-                mResponse.complete(response);
-                mRequestConsumer.accept(response == null ? null : response.mRequest);
-                mHandler.removeCallbacks(mTimeoutCallback);
-            }
-        }
-
-        @BinderThread
-        @Override
-        public void onInlineSuggestionsUnsupported() throws RemoteException {
-            if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
-            completeIfNot(null);
-        }
-
-        @BinderThread
-        @Override
-        public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
-                IInlineSuggestionsResponseCallback callback) {
-            if (sDebug) Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
-            mImeStatusListener.onInputMethodStartInput(mAutofillId);
-            if (request != null && callback != null) {
-                completeIfNot(new ImeResponse(request, callback));
-            } else {
-                completeIfNot(null);
-            }
-        }
-
-        @Override
-        public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
-            if (sDebug) Log.d(TAG, "onInputMethodStartInput() received on " + imeFieldId);
-            mImeStatusListener.onInputMethodStartInput(imeFieldId);
-        }
-
-        @Override
-        public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
-            if (sDebug) {
-                Log.d(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
-            }
-            // TODO(b/151123764): use this signal to adjust the timeout on Autofill side waiting for
-            //  IME to show.
-        }
-
-        @BinderThread
-        @Override
-        public void onInputMethodStartInputView() {
-            if (sDebug) Log.d(TAG, "onInputMethodStartInputView() received");
-            mImeStatusListener.onInputMethodStartInputView();
-        }
-
-        @BinderThread
-        @Override
-        public void onInputMethodFinishInputView() {
-            if (sDebug) Log.d(TAG, "onInputMethodFinishInputView() received");
-            mImeStatusListener.onInputMethodFinishInputView();
-        }
-
-        @Override
-        public void onInputMethodFinishInput() throws RemoteException {
-            if (sDebug) Log.d(TAG, "onInputMethodFinishInput() received");
-            mImeStatusListener.onInputMethodFinishInput();
-        }
-    }
-
-    private interface ImeStatusListener {
-        void onInputMethodStartInput(AutofillId imeFieldId);
-
-        void onInputMethodStartInputView();
-
-        void onInputMethodFinishInputView();
-
-        void onInputMethodFinishInput();
-    }
-
-    /**
-     * A data class wrapping Autofill responses for the inline suggestion request.
-     */
-    private static class AutofillResponse {
-        @NonNull
-        final AutofillId mAutofillId;
-
-        @NonNull
-        final InlineSuggestionsResponse mResponse;
-
-        AutofillResponse(@NonNull AutofillId autofillId,
-                @NonNull InlineSuggestionsResponse response) {
-            mAutofillId = autofillId;
-            mResponse = response;
-        }
-
-    }
-
-    /**
-     * A data class wrapping IME responses for the create inline suggestions request.
-     */
-    private static class ImeResponse {
-        @NonNull
-        final InlineSuggestionsRequest mRequest;
-
-        @NonNull
-        final IInlineSuggestionsResponseCallback mCallback;
-
-        ImeResponse(@NonNull InlineSuggestionsRequest request,
-                @NonNull IInlineSuggestionsResponseCallback callback) {
-            mRequest = request;
-            mCallback = callback;
-        }
-
-        InlineSuggestionsRequest getRequest() {
-            return mRequest;
-        }
-    }
-}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 1290569..4ecffd8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -304,7 +304,7 @@
     private boolean mForAugmentedAutofillOnly;
 
     @Nullable
-    private final InlineSuggestionSession mInlineSuggestionSession;
+    private final AutofillInlineSessionController mInlineSessionController;
 
     /**
      * Receiver of assist data from the app's {@link Activity}.
@@ -720,8 +720,9 @@
             Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
                     mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true);
             if (inlineSuggestionsRequestConsumer != null) {
-                mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
-                        inlineSuggestionsRequestConsumer);
+                // TODO(b/146454892): pipe the uiExtras from the ExtServices.
+                mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId,
+                        inlineSuggestionsRequestConsumer, Bundle.EMPTY);
             }
         } else {
             mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false);
@@ -777,8 +778,8 @@
         mForAugmentedAutofillOnly = forAugmentedAutofillOnly;
         setClientLocked(client);
 
-        mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId,
-                componentName, handler, mLock);
+        mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal,
+                userId, componentName, handler, mLock);
 
         mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
@@ -2561,7 +2562,7 @@
                     if (sVerbose) Slog.v(TAG, "Exiting view " + id);
                     mUi.hideFillUi(this);
                     hideAugmentedAutofillLocked(viewState);
-                    mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
+                    mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
                     mCurrentViewId = null;
                 }
                 break;
@@ -2779,7 +2780,7 @@
     private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
             @Nullable String filterText) {
         final Optional<InlineSuggestionsRequest> inlineSuggestionsRequest =
-                mInlineSuggestionSession.getInlineSuggestionsRequest();
+                mInlineSessionController.getInlineSuggestionsRequestLocked();
         if (!inlineSuggestionsRequest.isPresent()) {
             Log.w(TAG, "InlineSuggestionsRequest unavailable");
             return false;
@@ -2801,7 +2802,8 @@
                         inlineSuggestionsRequest.get(), response, filterText, mCurrentViewId,
                         this, () -> {
                             synchronized (mLock) {
-                                mInlineSuggestionSession.hideInlineSuggestionsUi(mCurrentViewId);
+                                mInlineSessionController.hideInlineSuggestionsUiLocked(
+                                        mCurrentViewId);
                             }
                         }, remoteRenderService);
         if (inlineSuggestionsResponse == null) {
@@ -2809,7 +2811,7 @@
             return false;
         }
 
-        return mInlineSuggestionSession.onInlineSuggestionsResponse(mCurrentViewId,
+        return mInlineSessionController.onInlineSuggestionsResponseLocked(mCurrentViewId,
                 inlineSuggestionsResponse);
     }
 
@@ -3106,8 +3108,13 @@
                             focusedId,
                             currentValue, inlineSuggestionsRequest,
                             /*inlineSuggestionsCallback=*/
-                            response -> mInlineSuggestionSession.onInlineSuggestionsResponse(
-                                    mCurrentViewId, response),
+                            response -> {
+                                synchronized (mLock) {
+                                    return mInlineSessionController
+                                            .onInlineSuggestionsResponseLocked(
+                                            mCurrentViewId, response);
+                                }
+                            },
                             /*onErrorCallback=*/ () -> {
                                 synchronized (mLock) {
                                     cancelAugmentedAutofillLocked();
@@ -3125,11 +3132,12 @@
                 && (mForAugmentedAutofillOnly
                 || !isInlineSuggestionsEnabledByAutofillProviderLocked())) {
             if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
-            mInlineSuggestionSession.onCreateInlineSuggestionsRequest(mCurrentViewId,
-                    /*requestConsumer=*/ requestAugmentedAutofill);
+            // TODO(b/146454892): pipe the uiExtras from the ExtServices.
+            mInlineSessionController.onCreateInlineSuggestionsRequestLocked(mCurrentViewId,
+                    /*requestConsumer=*/ requestAugmentedAutofill, Bundle.EMPTY);
         } else {
             requestAugmentedAutofill.accept(
-                    mInlineSuggestionSession.getInlineSuggestionsRequest().orElse(null));
+                    mInlineSessionController.getInlineSuggestionsRequestLocked().orElse(null));
         }
         if (mAugmentedAutofillDestroyer == null) {
             mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5faed43..a8bc2b4 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -91,7 +91,6 @@
     ],
 
     libs: [
-        "android_system_server_stubs_current",
         "services.net",
         "android.hardware.light-V2.0-java",
         "android.hardware.power-java",
@@ -101,6 +100,7 @@
         "android.net.ipsec.ike.stubs.module_libs_api",
         "app-compat-annotations",
         "framework-tethering-stubs-module_libs_api",
+        "service-permission-stubs",
     ],
 
     required: [
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 192ea72..3c0d880 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -119,8 +119,6 @@
     private static final int MESSAGE_DISABLE = 2;
     private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3;
     private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4;
-    private static final int MESSAGE_REGISTER_ADAPTER = 20;
-    private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
     private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
     private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
     private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
@@ -642,10 +640,9 @@
             Slog.w(TAG, "Callback is null in registerAdapter");
             return null;
         }
-        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_ADAPTER);
-        msg.obj = callback;
-        mHandler.sendMessage(msg);
-
+        synchronized (mCallbacks) {
+            mCallbacks.register(callback);
+        }
         return mBluetooth;
     }
 
@@ -655,9 +652,9 @@
             return;
         }
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
-        msg.obj = callback;
-        mHandler.sendMessage(msg);
+        synchronized (mCallbacks) {
+            mCallbacks.unregister(callback);
+        }
     }
 
     public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
@@ -1559,18 +1556,20 @@
      * Inform BluetoothAdapter instances that Adapter service is up
      */
     private void sendBluetoothServiceUpCallback() {
-        try {
-            int n = mCallbacks.beginBroadcast();
-            Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
-            for (int i = 0; i < n; i++) {
-                try {
-                    mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+        synchronized (mCallbacks) {
+            try {
+                int n = mCallbacks.beginBroadcast();
+                Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+                for (int i = 0; i < n; i++) {
+                    try {
+                        mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+                    }
                 }
+            } finally {
+                mCallbacks.finishBroadcast();
             }
-        } finally {
-            mCallbacks.finishBroadcast();
         }
     }
 
@@ -1578,18 +1577,20 @@
      * Inform BluetoothAdapter instances that Adapter service is down
      */
     private void sendBluetoothServiceDownCallback() {
-        try {
-            int n = mCallbacks.beginBroadcast();
-            Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
-            for (int i = 0; i < n; i++) {
-                try {
-                    mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+        synchronized (mCallbacks) {
+            try {
+                int n = mCallbacks.beginBroadcast();
+                Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+                for (int i = 0; i < n; i++) {
+                    try {
+                        mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
+                    }
                 }
+            } finally {
+                mCallbacks.finishBroadcast();
             }
-        } finally {
-            mCallbacks.finishBroadcast();
         }
     }
 
@@ -1917,17 +1918,6 @@
                                 mContext.getPackageName());
                     }
                     break;
-
-                case MESSAGE_REGISTER_ADAPTER: {
-                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
-                    mCallbacks.register(callback);
-                    break;
-                }
-                case MESSAGE_UNREGISTER_ADAPTER: {
-                    IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
-                    mCallbacks.unregister(callback);
-                    break;
-                }
                 case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
                     IBluetoothStateChangeCallback callback =
                             (IBluetoothStateChangeCallback) msg.obj;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2eaa766..8a1de1f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3153,7 +3153,7 @@
             }
         }
 
-        nai.clatd.setNat64Prefix(prefix);
+        nai.clatd.setNat64PrefixFromDns(prefix);
         handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
     }
 
@@ -5865,9 +5865,9 @@
             @NonNull LinkProperties oldLp) {
         int netId = networkAgent.network.netId;
 
-        // The NetworkAgentInfo does not know whether clatd is running on its network or not, or
-        // whether there is a NAT64 prefix. Before we do anything else, make sure its LinkProperties
-        // are accurate.
+        // The NetworkAgent does not know whether clatd is running on its network or not, or whether
+        // a NAT64 prefix was discovered by the DNS resolver. Before we do anything else, make sure
+        // the LinkProperties for the network are accurate.
         networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
 
         updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 8ccff76..7f25de6b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1836,12 +1836,13 @@
 
     @Override
     public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
-            PendingIntent intent, String packageName, String featureId) {
+            PendingIntent intent, String packageName, String featureId, String listenerId) {
         if (request == null) {
             request = DEFAULT_LOCATION_REQUEST;
         }
 
-        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId);
+        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId,
+                listenerId);
         identity.enforceLocationPermission();
 
         WorkSource workSource = request.getWorkSource();
@@ -2027,7 +2028,7 @@
     @Override
     public boolean getCurrentLocation(LocationRequest locationRequest,
             ICancellationSignal remoteCancellationSignal, ILocationListener listener,
-            String packageName, String featureId) {
+            String packageName, String featureId, String listenerId) {
         // side effect of validating locationRequest and packageName
         Location lastLocation = getLastLocation(locationRequest, packageName, featureId);
         if (lastLocation != null) {
@@ -2052,7 +2053,7 @@
             }
         }
 
-        requestLocationUpdates(locationRequest, listener, null, packageName, featureId);
+        requestLocationUpdates(locationRequest, listener, null, packageName, featureId, listenerId);
         CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
                 remoteCancellationSignal);
         if (cancellationSignal != null) {
@@ -2598,8 +2599,6 @@
     @Override
     @NonNull
     public List<LocationRequest> getTestProviderCurrentRequests(String provider) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG, null);
-
         LocationProviderManager manager = getLocationProviderManager(provider);
         if (manager == null) {
             throw new IllegalArgumentException("provider doesn't exist: " + provider);
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index d9e7c38..e77458c 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -315,28 +315,31 @@
         // causing any elapsed time to be deducted from all existing packages before we add new
         // packages. This maintains the invariant that the elapsed time for ALL (new and existing)
         // packages is the same.
-        syncState("observing new packages");
+        mLongTaskHandler.post(() -> {
+            syncState("observing new packages");
 
-        synchronized (mLock) {
-            ObserverInternal oldObserver = mAllObservers.get(observer.getName());
-            if (oldObserver == null) {
-                Slog.d(TAG, observer.getName() + " started monitoring health "
-                        + "of packages " + packageNames);
-                mAllObservers.put(observer.getName(),
-                        new ObserverInternal(observer.getName(), packages));
-            } else {
-                Slog.d(TAG, observer.getName() + " added the following "
-                        + "packages to monitor " + packageNames);
-                oldObserver.updatePackagesLocked(packages);
+            synchronized (mLock) {
+                ObserverInternal oldObserver = mAllObservers.get(observer.getName());
+                if (oldObserver == null) {
+                    Slog.d(TAG, observer.getName() + " started monitoring health "
+                            + "of packages " + packageNames);
+                    mAllObservers.put(observer.getName(),
+                            new ObserverInternal(observer.getName(), packages));
+                } else {
+                    Slog.d(TAG, observer.getName() + " added the following "
+                            + "packages to monitor " + packageNames);
+                    oldObserver.updatePackagesLocked(packages);
+                }
             }
-        }
 
-        // Register observer in case not already registered
-        registerHealthObserver(observer);
+            // Register observer in case not already registered
+            registerHealthObserver(observer);
 
-        // Sync after we add the new packages to the observers. We may have received packges
-        // requiring an earlier schedule than we are currently scheduled for.
-        syncState("updated observers");
+            // Sync after we add the new packages to the observers. We may have received packges
+            // requiring an earlier schedule than we are currently scheduled for.
+            syncState("updated observers");
+        });
+
     }
 
     /**
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0671477..9aefc8d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4468,9 +4468,8 @@
                             String.format("/storage/emulated/%d/Android/data/%s/",
                                     userId, pkg);
 
-                    int appUid =
-                            UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
                     // Create package obb and data dir if it doesn't exist.
+                    int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
                     File file = new File(packageObbDir);
                     if (!file.exists()) {
                         vold.setupAppDir(packageObbDir, appUid);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 061ff42..58972a5 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -123,6 +123,8 @@
             "android.hardware.neuralnetworks@1.0::IDevice",
             "android.hardware.power.stats@1.0::IPowerStats",
             "android.hardware.sensors@1.0::ISensors",
+            "android.hardware.sensors@2.0::ISensors",
+            "android.hardware.sensors@2.1::ISensors",
             "android.hardware.vr@1.0::IVr",
             "android.system.suspend@1.0::ISystemSuspend"
     );
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7523710d..21760cd 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1440,7 +1440,7 @@
                                 active.mPackageName = r.packageName;
                                 active.mUid = r.appInfo.uid;
                                 active.mShownWhileScreenOn = mScreenOn;
-                                if (r.app != null) {
+                                if (r.app != null && r.app.uidRecord != null) {
                                     active.mAppOnTop = active.mShownWhileTop =
                                             r.app.uidRecord.getCurProcState()
                                                     <= ActivityManager.PROCESS_STATE_TOP;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 62d7eb1..e41ba0e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12121,8 +12121,6 @@
 
     void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage) {
-        boolean needSep = false;
-
         pw.println("ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity allowed-associations)");
         boolean printed = false;
         if (mAllowedAssociations != null) {
@@ -12130,21 +12128,16 @@
                 final String pkg = mAllowedAssociations.keyAt(i);
                 final ArraySet<String> asc =
                         mAllowedAssociations.valueAt(i).getAllowedPackageAssociations();
-                boolean printedHeader = false;
+                if (!printed) {
+                    pw.println("  Allowed associations (by restricted package):");
+                    printed = true;
+                }
+                pw.print("  * ");
+                pw.print(pkg);
+                pw.println(":");
                 for (int j = 0; j < asc.size(); j++) {
                     if (dumpPackage == null || pkg.equals(dumpPackage)
                             || asc.valueAt(j).equals(dumpPackage)) {
-                        if (!printed) {
-                            pw.println("  Allowed associations (by restricted package):");
-                            printed = true;
-                            needSep = true;
-                        }
-                        if (!printedHeader) {
-                            pw.print("  * ");
-                            pw.print(pkg);
-                            pw.println(":");
-                            printedHeader = true;
-                        }
                         pw.print("      Allow: ");
                         pw.println(asc.valueAt(j));
                     }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index dbcb3da..2d6ef81 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -231,7 +231,8 @@
         final ServiceThread adjusterThread =
                 new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST, false /* allowIo */);
         adjusterThread.start();
-        Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP);
+        adjusterThread.getThreadHandler().post(() -> Process.setThreadGroupAndCpuset(
+                adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP));
         return adjusterThread;
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 595275d..786e9cf 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -97,6 +97,7 @@
 import android.system.Os;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.LongSparseArray;
 import android.util.Pair;
@@ -137,6 +138,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Activity manager code dealing with processes.
@@ -1846,11 +1848,13 @@
                 runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
             }
 
-            // Enable heap pointer tagging, unless disabled by the app manifest, target sdk level,
-            // or the compat feature.
-            if (app.info.allowsNativeHeapPointerTagging()
-                    && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
-                runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+            if (Zygote.nativeSupportsTaggedPointers()) {
+                // Enable heap pointer tagging if supported by the kernel, unless disabled by the
+                // app manifest, target sdk level, or compat feature.
+                if (app.info.allowsNativeHeapPointerTagging()
+                        && mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+                    runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+                }
             }
 
             runtimeFlags |= decideGwpAsanLevel(app);
@@ -2127,18 +2131,11 @@
         for (String packageName : packages) {
             String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid();
             long inode = pmInt.getCeDataInode(packageName, userId);
-            if (inode != 0) {
-                result.put(packageName, Pair.create(volumeUuid, inode));
+            if (inode == 0) {
+                Slog.w(TAG, packageName + " inode == 0 (b/152760674)");
+                return null;
             }
-        }
-        if (mAppDataIsolationWhitelistedApps != null) {
-            for (String packageName : mAppDataIsolationWhitelistedApps) {
-                String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid();
-                long inode = pmInt.getCeDataInode(packageName, userId);
-                if (inode != 0) {
-                    result.put(packageName, Pair.create(volumeUuid, inode));
-                }
-            }
+            result.put(packageName, Pair.create(volumeUuid, inode));
         }
 
         return result;
@@ -2160,35 +2157,56 @@
                 app.setHasForegroundActivities(true);
             }
 
+            final Map<String, Pair<String, Long>> pkgDataInfoMap;
+            final Map<String, Pair<String, Long>> whitelistedAppDataInfoMap;
+            boolean bindMountAppStorageDirs = false;
+            boolean bindMountAppsData = mAppDataIsolationEnabled
+                    && UserHandle.isApp(app.uid)
+                    && mPlatformCompat.isChangeEnabled(APP_DATA_DIRECTORY_ISOLATION, app.info);
+
+            // Get all packages belongs to the same shared uid. sharedPackages is empty array
+            // if it doesn't have shared uid.
+            final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
+            final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
+                    app.info.packageName, app.userId);
+            final String[] targetPackagesList = sharedPackages.length == 0
+                    ? new String[]{app.info.packageName} : sharedPackages;
+
+            pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid);
+            if (pkgDataInfoMap == null) {
+                // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
+                // tmp free pass.
+                bindMountAppsData = false;
+            }
+
+            // Remove all packages in pkgDataInfoMap from mAppDataIsolationWhitelistedApps, so
+            // it won't be mounted twice.
+            final Set<String> whitelistedApps = new ArraySet<>(mAppDataIsolationWhitelistedApps);
+            for (String pkg : targetPackagesList) {
+                whitelistedApps.remove(pkg);
+            }
+
+            whitelistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt,
+                    whitelistedApps.toArray(new String[0]), uid);
+            if (whitelistedAppDataInfoMap == null) {
+                // TODO(b/152760674): Handle inode == 0 case properly, now we just give it a
+                // tmp free pass.
+                bindMountAppsData = false;
+            }
+
+            int userId = UserHandle.getUserId(uid);
             StorageManagerInternal storageManagerInternal = LocalServices.getService(
                     StorageManagerInternal.class);
-            final Map<String, Pair<String, Long>> pkgDataInfoMap;
-            boolean bindMountAppStorageDirs = false;
-
-            if (mAppDataIsolationEnabled && UserHandle.isApp(app.uid)
-                    && mPlatformCompat.isChangeEnabled(APP_DATA_DIRECTORY_ISOLATION, app.info)) {
-                // Get all packages belongs to the same shared uid. sharedPackages is empty array
-                // if it doesn't have shared uid.
-                final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
-                final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
-                        app.info.packageName, app.userId);
-                pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0
-                        ? new String[]{app.info.packageName} : sharedPackages, uid);
-
-                int userId = UserHandle.getUserId(uid);
-                if (mVoldAppDataIsolationEnabled
-                        && !storageManagerInternal.isExternalStorageService(uid)) {
-                    bindMountAppStorageDirs = true;
-                    if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
-                            app.processName)) {
-                        // Cannot prepare Android/app and Android/obb directory,
-                        // so we won't mount it in zygote.
-                        app.bindMountPending = true;
-                        bindMountAppStorageDirs = false;
-                    }
+            if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid)
+                    && !storageManagerInternal.isExternalStorageService(uid)) {
+                bindMountAppStorageDirs = true;
+                if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
+                        app.processName)) {
+                    // Cannot prepare Android/app and Android/obb directory,
+                    // so we won't mount it in zygote.
+                    app.bindMountPending = true;
+                    bindMountAppStorageDirs = false;
                 }
-            } else {
-                pkgDataInfoMap = null;
             }
 
             final Process.ProcessStartResult startResult;
@@ -2206,7 +2224,8 @@
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
                         /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
-                        app.mDisabledCompatChanges, pkgDataInfoMap, bindMountAppStorageDirs,
+                        app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
+                        bindMountAppsData, bindMountAppStorageDirs,
                         new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
@@ -2214,7 +2233,7 @@
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                         isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
-                        bindMountAppStorageDirs,
+                        whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                         new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             }
             checkSlow(startTime, "startProcess: returned from zygote!");
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 4431abe..808f8c21 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -558,8 +558,10 @@
             FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                     statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
 
+            ClientMonitor newClient = mPendingClient;
             mCurrentClient = null;
-            startClient(mPendingClient, false);
+            mPendingClient = null;
+            startClient(newClient, false);
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 3ecf87c..ad73b64 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -382,8 +382,10 @@
             checkPermission(MANAGE_BIOMETRIC);
             updateActiveGroup(userId, opPackageName);
 
-            mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
-                    UserHandle.CURRENT);
+            mHandler.post(() -> {
+                mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
+                        UserHandle.CURRENT);
+            });
 
             final boolean restricted = isRestricted();
             final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 82465f8..741cb5b 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -81,7 +81,7 @@
         RUNNING,      // start() called, and the stacked iface is known to be up.
     }
 
-    private IpPrefix mNat64Prefix;
+    private IpPrefix mNat64PrefixFromDns;
     private String mBaseIface;
     private String mIface;
     private Inet6Address mIPv6Address;
@@ -100,7 +100,7 @@
      * currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to
      * enable NAT64 prefix discovery.
      *
-     * @param network the NetworkAgentInfo corresponding to the network.
+     * @param nai the NetworkAgentInfo corresponding to the network.
      * @return true if the network requires clat, false otherwise.
      */
     @VisibleForTesting
@@ -180,7 +180,7 @@
 
         String addrStr = null;
         try {
-            addrStr = mNetd.clatdStart(baseIface, mNat64Prefix.toString());
+            addrStr = mNetd.clatdStart(baseIface, getNat64Prefix().toString());
         } catch (RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
         }
@@ -213,12 +213,10 @@
         }
         mIface = null;
         mBaseIface = null;
-        mState = State.IDLE;
         if (requiresClat(mNetwork)) {
             mState = State.DISCOVERING;
         } else {
             stopPrefixDiscovery();
-            mState = State.IDLE;
         }
     }
 
@@ -285,6 +283,7 @@
     private void stopPrefixDiscovery() {
         try {
             mDnsResolver.stopPrefix64Discovery(getNetId());
+            mState = State.IDLE;
         } catch (RemoteException | ServiceSpecificException e) {
             Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
         }
@@ -294,32 +293,52 @@
      * Starts/stops NAT64 prefix discovery and clatd as necessary.
      */
     public void update() {
-        // TODO: turn this class into a proper StateMachine. // http://b/126113090
-        if (requiresClat(mNetwork)) {
-            if (!isPrefixDiscoveryStarted()) {
-                startPrefixDiscovery();
-            } else if (shouldStartClat(mNetwork)) {
-                // NAT64 prefix detected. Start clatd.
-                // TODO: support the NAT64 prefix changing after it's been discovered. There is no
-                // need to support this at the moment because it cannot happen without changes to
-                // the Dns64Configuration code in netd.
-                start();
-            } else {
-                // NAT64 prefix removed. Stop clatd and go back into DISCOVERING state.
-                stop();
-            }
-        } else {
-            // Network no longer requires clat. Stop clat and prefix discovery.
-            if (isStarted()) {
-                stop();
-            } else if (isPrefixDiscoveryStarted()) {
-                leaveStartedState();
-            }
+        // TODO: turn this class into a proper StateMachine. http://b/126113090
+        switch (mState) {
+            case IDLE:
+                if (requiresClat(mNetwork)) {
+                    // Network is detected to be IPv6-only.
+                    // TODO: consider going to STARTING directly if the NAT64 prefix is already
+                    // known. This would however result in clatd running without prefix discovery
+                    // running, which might be a surprising combination.
+                    startPrefixDiscovery();  // Enters DISCOVERING state.
+                    return;
+                }
+                break;
+
+            case DISCOVERING:
+                if (shouldStartClat(mNetwork)) {
+                    // NAT64 prefix detected. Start clatd.
+                    start();  // Enters STARTING state.
+                    return;
+                }
+                if (!requiresClat(mNetwork)) {
+                    // IPv4 address added. Go back to IDLE state.
+                    stopPrefixDiscovery();
+                    return;
+                }
+                break;
+
+            case STARTING:
+            case RUNNING:
+                // NAT64 prefix removed, or IPv4 address added.
+                // Stop clatd and go back into DISCOVERING or idle.
+                if (!shouldStartClat(mNetwork)) {
+                    stop();
+                }
+                break;
+                // TODO: support the NAT64 prefix changing after it's been discovered. There is
+                // no need to support this at the moment because it cannot happen without
+                // changes to the Dns64Configuration code in netd.
         }
     }
 
-    public void setNat64Prefix(IpPrefix nat64Prefix) {
-        mNat64Prefix = nat64Prefix;
+    private IpPrefix getNat64Prefix() {
+        return mNat64PrefixFromDns;
+    }
+
+    public void setNat64PrefixFromDns(IpPrefix prefix) {
+        mNat64PrefixFromDns = prefix;
     }
 
     /**
@@ -328,7 +347,7 @@
      * has no idea that 464xlat is running on top of it.
      */
     public void fixupLinkProperties(@NonNull LinkProperties oldLp, @NonNull LinkProperties lp) {
-        lp.setNat64Prefix(mNat64Prefix);
+        lp.setNat64Prefix(getNat64Prefix());
 
         if (!isRunning()) {
             return;
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 61b18ee..962f337 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -80,6 +80,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -398,78 +399,93 @@
      */
     @Override
     public void notifyChange(Uri[] uris, IContentObserver observer,
-            boolean observerWantsSelfNotifications, int flags, int userHandle,
+            boolean observerWantsSelfNotifications, int flags, int userId,
             int targetSdkVersion, String callingPackage) {
-        final ObserverCollector collector = new ObserverCollector();
-        for (Uri uri : uris) {
-            notifyChange(uri, observer, observerWantsSelfNotifications, flags, userHandle,
-                    targetSdkVersion, callingPackage, collector);
-        }
-        final long token = clearCallingIdentity();
-        try {
-            collector.dispatch();
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public void notifyChange(Uri uri, IContentObserver observer,
-            boolean observerWantsSelfNotifications, int flags, int userHandle,
-            int targetSdkVersion, String callingPackage, ObserverCollector collector) {
-        if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
-                + " from observer " + observer + ", flags " + Integer.toHexString(flags));
-
-        if (uri == null) {
-            throw new NullPointerException("Uri must not be null");
+        if (DEBUG) {
+            Slog.d(TAG, "Notifying update of " + Arrays.toString(uris) + " for user " + userId
+                    + ", observer " + observer + ", flags " + Integer.toHexString(flags));
         }
 
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
-        final int callingUserHandle = UserHandle.getCallingUserId();
+        final int callingUserId = UserHandle.getCallingUserId();
 
-        userHandle = handleIncomingUser(uri, callingPid, callingUid,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true, userHandle);
+        // Set of notification events that we need to dispatch
+        final ObserverCollector collector = new ObserverCollector();
 
-        final String msg = LocalServices.getService(ActivityManagerInternal.class)
-                .checkContentProviderAccess(uri.getAuthority(), userHandle);
-        if (msg != null) {
-            if (targetSdkVersion >= Build.VERSION_CODES.O) {
-                throw new SecurityException(msg);
-            } else {
-                if (msg.startsWith("Failed to find provider")) {
-                    // Sigh, we need to quietly let apps targeting older API
-                    // levels notify on non-existent providers.
-                } else {
-                    Log.w(TAG, "Ignoring notify for " + uri + " from " + callingUid + ": " + msg);
-                    return;
+        // Set of content provider authorities that we've validated the caller
+        // has access to, mapped to the package name hosting that provider
+        final ArrayMap<Pair<String, Integer>, String> validatedProviders = new ArrayMap<>();
+
+        for (Uri uri : uris) {
+            // Validate that calling app has access to this provider
+            final int resolvedUserId = handleIncomingUser(uri, callingPid, callingUid,
+                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true, userId);
+            final Pair<String, Integer> provider = Pair.create(uri.getAuthority(), resolvedUserId);
+            if (!validatedProviders.containsKey(provider)) {
+                final String msg = LocalServices.getService(ActivityManagerInternal.class)
+                        .checkContentProviderAccess(uri.getAuthority(), resolvedUserId);
+                if (msg != null) {
+                    if (targetSdkVersion >= Build.VERSION_CODES.O) {
+                        throw new SecurityException(msg);
+                    } else {
+                        if (msg.startsWith("Failed to find provider")) {
+                            // Sigh, we need to quietly let apps targeting older API
+                            // levels notify on non-existent providers.
+                        } else {
+                            Log.w(TAG, "Ignoring notify for " + uri + " from "
+                                    + callingUid + ": " + msg);
+                            continue;
+                        }
+                    }
                 }
+
+                // Remember that we've validated this access
+                final String packageName = getProviderPackageName(uri, resolvedUserId);
+                validatedProviders.put(provider, packageName);
+            }
+
+            // No concerns raised above, so caller has access; let's collect the
+            // notifications that should be dispatched
+            synchronized (mRootNode) {
+                final int segmentCount = ObserverNode.countUriSegments(uri);
+                mRootNode.collectObserversLocked(uri, segmentCount, 0, observer,
+                        observerWantsSelfNotifications, flags, resolvedUserId, collector);
             }
         }
 
-        // This makes it so that future permission checks will be in the context of this
-        // process rather than the caller's process. We will restore this before returning.
-        long identityToken = clearCallingIdentity();
+        final long token = clearCallingIdentity();
         try {
-            synchronized (mRootNode) {
-                mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
-                        flags, userHandle, collector);
-            }
-            if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
-                SyncManager syncManager = getSyncManager();
-                if (syncManager != null) {
-                    syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle,
-                            callingUid,
-                            uri.getAuthority(), getSyncExemptionForCaller(callingUid),
-                            callingUid, callingPid, callingPackage);
+            // Actually dispatch all the notifications we collected
+            collector.dispatch();
+
+            for (int i = 0; i < validatedProviders.size(); i++) {
+                final String authority = validatedProviders.keyAt(i).first;
+                final int resolvedUserId = validatedProviders.keyAt(i).second;
+                final String packageName = validatedProviders.valueAt(i);
+
+                // Kick off sync adapters for any authorities we touched
+                if ((flags & ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
+                    SyncManager syncManager = getSyncManager();
+                    if (syncManager != null) {
+                        syncManager.scheduleLocalSync(null /* all accounts */, callingUserId,
+                                callingUid,
+                                authority, getSyncExemptionForCaller(callingUid),
+                                callingUid, callingPid, callingPackage);
+                    }
+                }
+
+                // Invalidate caches for any authorities we touched
+                synchronized (mCache) {
+                    for (Uri uri : uris) {
+                        if (Objects.equals(uri.getAuthority(), authority)) {
+                            invalidateCacheLocked(resolvedUserId, packageName, uri);
+                        }
+                    }
                 }
             }
-
-            synchronized (mCache) {
-                final String providerPackageName = getProviderPackageName(uri, userHandle);
-                invalidateCacheLocked(userHandle, providerPackageName, uri);
-            }
         } finally {
-            restoreCallingIdentity(identityToken);
+            Binder.restoreCallingIdentity(token);
         }
     }
 
@@ -1533,7 +1549,7 @@
             }
         }
 
-        private String getUriSegment(Uri uri, int index) {
+        public static String getUriSegment(Uri uri, int index) {
             if (uri != null) {
                 if (index == 0) {
                     return uri.getAuthority();
@@ -1545,7 +1561,7 @@
             }
         }
 
-        private int countUriSegments(Uri uri) {
+        public static int countUriSegments(Uri uri) {
             if (uri == null) {
                 return 0;
             }
@@ -1669,14 +1685,21 @@
             }
         }
 
+        @VisibleForTesting
+        public void collectObserversLocked(Uri uri, int index,
+                IContentObserver observer, boolean observerWantsSelfNotifications, int flags,
+                int targetUserHandle, ObserverCollector collector) {
+            collectObserversLocked(uri, countUriSegments(uri), index, observer,
+                    observerWantsSelfNotifications, flags, targetUserHandle, collector);
+        }
+
         /**
          * targetUserHandle is either a hard user handle or is USER_ALL
          */
-        public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
-                                           boolean observerWantsSelfNotifications, int flags,
-                                           int targetUserHandle, ObserverCollector collector) {
+        public void collectObserversLocked(Uri uri, int segmentCount, int index,
+                IContentObserver observer, boolean observerWantsSelfNotifications, int flags,
+                int targetUserHandle, ObserverCollector collector) {
             String segment = null;
-            int segmentCount = countUriSegments(uri);
             if (index >= segmentCount) {
                 // This is the leaf node, notify all observers
                 if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
@@ -1696,7 +1719,7 @@
                 ObserverNode node = mChildren.get(i);
                 if (segment == null || node.mName.equals(segment)) {
                     // We found the child,
-                    node.collectObserversLocked(uri, index + 1, observer,
+                    node.collectObserversLocked(uri, segmentCount, index + 1, observer,
                             observerWantsSelfNotifications, flags, targetUserHandle, collector);
                     if (segment != null) {
                         break;
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 6178e6c..f4d7f9a 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -200,7 +200,7 @@
     // Context-sensitive brightness configurations require keeping track of the foreground app's
     // package name and category, which is done by registering a TaskStackListener to call back to
     // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
-    // package namd and PackageManager to get its category (so might as well cache them).
+    // package name and PackageManager to get its category (so might as well cache them).
     private String mForegroundAppPackageName;
     private String mPendingForegroundAppPackageName;
     private @ApplicationInfo.Category int mForegroundAppCategory;
@@ -210,6 +210,7 @@
     private PackageManager mPackageManager;
     private Context mContext;
 
+    private DisplayDeviceConfig mDisplayDeviceConfig;
     private final Injector mInjector;
 
     AutomaticBrightnessController(Callbacks callbacks, Looper looper,
@@ -218,12 +219,14 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, Context context) {
+            HysteresisLevels screenBrightnessThresholds, Context context, DisplayDeviceConfig
+            displayDeviceConfig) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
-                ambientBrightnessThresholds, screenBrightnessThresholds, context);
+                ambientBrightnessThresholds, screenBrightnessThresholds, context,
+                displayDeviceConfig);
     }
 
     @VisibleForTesting
@@ -233,7 +236,8 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, Context context) {
+            HysteresisLevels screenBrightnessThresholds, Context context, DisplayDeviceConfig
+            displayDeviceConfig) {
         mInjector = injector;
         mContext = context;
         mCallbacks = callbacks;
@@ -260,7 +264,7 @@
         mScreenBrightnessThresholds = screenBrightnessThresholds;
         mShortTermModelValid = true;
         mShortTermModelAnchor = -1;
-
+        mDisplayDeviceConfig = displayDeviceConfig;
         mHandler = new AutomaticBrightnessHandler(looper);
         mAmbientLightRingBuffer =
             new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon);
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 696daca..3516981 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -37,6 +37,7 @@
     private final DisplayAdapter mDisplayAdapter;
     private final IBinder mDisplayToken;
     private final String mUniqueId;
+    private DisplayDeviceConfig mDisplayDeviceConfig;
 
     // The display device does not manage these properties itself, they are set by
     // the display manager service.  The display device shouldn't really be looking at these.
@@ -68,6 +69,16 @@
         return mDisplayAdapter;
     }
 
+    /*
+     * Gets the DisplayDeviceConfig for this DisplayDevice.
+     * Returns null for this device but is overridden in LocalDisplayDevice.
+     *
+     * @return The DisplayDeviceConfig.
+     */
+    public DisplayDeviceConfig getDisplayDeviceConfig() {
+        return mDisplayDeviceConfig;
+    }
+
     /**
      * Gets the Surface Flinger display token for this display.
      *
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3afbf66..a87fb8b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1362,7 +1362,8 @@
         return null;
     }
 
-    private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId) {
+    private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId,
+            boolean captureSecureLayer) {
         synchronized (mSyncRoot) {
             final IBinder token = getDisplayToken(displayId);
             if (token == null) {
@@ -1374,9 +1375,15 @@
             }
 
             final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
-            return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(),
-                    displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
-                    false /* useIdentityTransform */, 0 /* rotation */);
+            if (captureSecureLayer) {
+                return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(),
+                        displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
+                        false /* useIdentityTransform */, 0 /* rotation */);
+            } else {
+                return SurfaceControl.screenshotToBuffer(token, new Rect(),
+                        displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(),
+                        false /* useIdentityTransform */, 0 /* rotation */);
+            }
         }
     }
 
@@ -2469,7 +2476,8 @@
                     }
                 };
                 mDisplayPowerController = new DisplayPowerController(
-                        mContext, callbacks, handler, sensorManager, blanker);
+                        mContext, callbacks, handler, sensorManager, blanker,
+                        mDisplayDevices.get(Display.DEFAULT_DISPLAY));
                 mSensorManager = sensorManager;
             }
 
@@ -2494,7 +2502,12 @@
 
         @Override
         public SurfaceControl.ScreenshotGraphicBuffer screenshot(int displayId) {
-            return screenshotInternal(displayId);
+            return screenshotInternal(displayId, true);
+        }
+
+        @Override
+        public SurfaceControl.ScreenshotGraphicBuffer screenshotWithoutSecureLayer(int displayId) {
+            return screenshotInternal(displayId, false);
         }
 
         @Override
@@ -2503,6 +2516,17 @@
         }
 
         @Override
+        public Point getDisplayPosition(int displayId) {
+            synchronized (mSyncRoot) {
+                LogicalDisplay display = mLogicalDisplays.get(displayId);
+                if (display != null) {
+                    return display.getDisplayPosition();
+                }
+                return null;
+            }
+        }
+
+        @Override
         public void registerDisplayTransactionListener(DisplayTransactionListener listener) {
             if (listener == null) {
                 throw new IllegalArgumentException("listener must not be null");
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 197842e..f82ec82 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -161,6 +161,9 @@
     // The display blanker.
     private final DisplayBlanker mBlanker;
 
+    // The display device.
+    private final DisplayDevice mDisplayDevice;
+
     // Tracker for brightness changes.
     private final BrightnessTracker mBrightnessTracker;
 
@@ -348,11 +351,11 @@
     @Nullable
     private BrightnessConfiguration mBrightnessConfiguration;
 
-    // The last brightness that was set by the user and not temporary. Set to -1 when a brightness
-    // has yet to be recorded.
+    // The last brightness that was set by the user and not temporary. Set to
+    // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded.
     private float mLastUserSetScreenBrightness;
 
-    // The screen brightenss setting has changed but not taken effect yet. If this is different
+    // The screen brightness setting has changed but not taken effect yet. If this is different
     // from the current screen brightness setting then this is coming from something other than us
     // and should be considered a user interaction.
     private float mPendingScreenBrightnessSetting;
@@ -377,8 +380,8 @@
     private float mPendingAutoBrightnessAdjustment;
 
     // The temporary auto brightness adjustment. Typically set when a user is interacting with the
-    // adjustment slider but hasn't settled on a choice yet. Set to Float.NaN when there's no
-    // temporary adjustment set.
+    // adjustment slider but hasn't settled on a choice yet. Set to
+    // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set.
     private float mTemporaryAutoBrightnessAdjustment;
 
     // Animators.
@@ -386,27 +389,29 @@
     private ObjectAnimator mColorFadeOffAnimator;
     private RampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
 
-    private BrightnessSynchronizer mBrightnessSynchronizer;
 
     /**
      * Creates the display power controller.
      */
     public DisplayPowerController(Context context,
             DisplayPowerCallbacks callbacks, Handler handler,
-            SensorManager sensorManager, DisplayBlanker blanker) {
+            SensorManager sensorManager, DisplayBlanker blanker, DisplayDevice displayDevice) {
         mHandler = new DisplayControllerHandler(handler.getLooper());
         mBrightnessTracker = new BrightnessTracker(context, null);
         mSettingsObserver = new SettingsObserver(mHandler);
         mCallbacks = callbacks;
-        mBrightnessSynchronizer = new BrightnessSynchronizer(context);
         mBatteryStats = BatteryStatsService.getService();
         mSensorManager = sensorManager;
         mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
         mBlanker = blanker;
         mContext = context;
+        mDisplayDevice = displayDevice;
 
         PowerManager pm =  context.getSystemService(PowerManager.class);
+        DisplayDeviceConfig displayDeviceConfig = mDisplayDevice.getDisplayDeviceConfig();
+
         final Resources resources = context.getResources();
+
         final float screenBrightnessSettingMinimumFloat = clampAbsoluteBrightness(
                 pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM));
 
@@ -498,7 +503,7 @@
                         mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
                         initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                         autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
-                        screenBrightnessThresholds, context);
+                        screenBrightnessThresholds, context, displayDeviceConfig);
             } else {
                 mUseSoftwareAutoBrightnessConfig = false;
             }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 8eb7710..6132467 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -203,6 +203,8 @@
         private Spline mNitsToHalBrightness;
         private boolean mHalBrightnessSupport;
 
+        private DisplayDeviceConfig mDisplayDeviceConfig;
+
         LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
                 SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
                 int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
@@ -224,7 +226,7 @@
             mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
             mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
             mHalBrightnessSupport = SurfaceControl.getDisplayBrightnessSupport(displayToken);
-
+            mDisplayDeviceConfig = null;
             // Defer configuration file loading
             BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
                     LocalDisplayDevice::loadDisplayConfigurationBrightnessMapping, this));
@@ -373,17 +375,23 @@
             return true;
         }
 
+        @Override
+        public DisplayDeviceConfig getDisplayDeviceConfig() {
+            return mDisplayDeviceConfig;
+        }
+
         private void loadDisplayConfigurationBrightnessMapping() {
             Spline nitsToHal = null;
             Spline sysToNits = null;
 
             // Load the mapping from nits to HAL brightness range (display-device-config.xml)
             DisplayDeviceConfig config = DisplayDeviceConfig.create(mPhysicalDisplayId);
+            mDisplayDeviceConfig = config;
             if (config == null) {
                 return;
             }
-            final float[] halNits = config.getNits();
-            final float[] halBrightness = config.getBrightness();
+            final float[] halNits = mDisplayDeviceConfig.getNits();
+            final float[] halBrightness = mDisplayDeviceConfig.getBrightness();
             if (halNits == null || halBrightness == null) {
                 return;
             }
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 3a5aa93..0261f38 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerInternal;
 import android.view.Display;
@@ -98,6 +99,11 @@
     private int mDisplayOffsetY;
 
     /**
+     * The position of the display projection sent to SurfaceFlinger
+     */
+    private final Point mDisplayPosition = new Point();
+
+    /**
      * {@code true} if display scaling is disabled, or {@code false} if the default scaling mode
      * is used.
      * @see #isDisplayScalingDisabled()
@@ -335,6 +341,16 @@
     }
 
     /**
+     * Returns the position of the display's projection.
+     *
+     * @return The x, y coordinates of the display. The return object must be treated as immutable.
+     */
+    Point getDisplayPosition() {
+        // Allocate a new object to avoid a data race.
+        return new Point(mDisplayPosition);
+    }
+
+    /**
      * Applies the layer stack and transformation to the given display device
      * so that it shows the contents of this logical display.
      *
@@ -445,6 +461,8 @@
         } else {  // Surface.ROTATION_270
             mTempDisplayRect.offset(-mDisplayOffsetY, mDisplayOffsetX);
         }
+
+        mDisplayPosition.set(mTempDisplayRect.left, mTempDisplayRect.top);
         device.setProjectionLocked(t, orientation, mTempLayerStackRect, mTempDisplayRect);
     }
 
diff --git a/services/core/java/com/android/server/display/color/AppSaturationController.java b/services/core/java/com/android/server/display/color/AppSaturationController.java
index e42be02..6a685bf 100644
--- a/services/core/java/com/android/server/display/color/AppSaturationController.java
+++ b/services/core/java/com/android/server/display/color/AppSaturationController.java
@@ -17,6 +17,7 @@
 package com.android.server.display.color;
 
 import android.annotation.UserIdInt;
+import android.util.ArrayMap;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -61,11 +62,12 @@
      * Set the saturation level ({@code ColorDisplayManager#SaturationLevel} constant for a given
      * package name and userId.
      */
-    public boolean setSaturationLevel(String packageName, @UserIdInt int userId,
+    public boolean setSaturationLevel(String callingPackageName, String affectedPackageName,
+            @UserIdInt int userId,
             int saturationLevel) {
         synchronized (mLock) {
-            return getSaturationControllerLocked(packageName, userId)
-                    .setSaturationLevel(saturationLevel);
+            return getSaturationControllerLocked(affectedPackageName, userId)
+                    .setSaturationLevel(callingPackageName, saturationLevel);
         }
     }
 
@@ -148,13 +150,19 @@
 
     private static class SaturationController {
 
+        private static final int FULL_SATURATION = 100;
+
         private final List<WeakReference<ColorTransformController>> mControllerRefs =
                 new ArrayList<>();
-        private int mSaturationLevel = 100;
+        private final ArrayMap<String, Integer> mSaturationLevels = new ArrayMap<>();
         private float[] mTransformMatrix = new float[9];
 
-        private boolean setSaturationLevel(int saturationLevel) {
-            mSaturationLevel = saturationLevel;
+        private boolean setSaturationLevel(String callingPackageName, int saturationLevel) {
+            if (saturationLevel == FULL_SATURATION) {
+                mSaturationLevels.remove(callingPackageName);
+            } else {
+                mSaturationLevels.put(callingPackageName, saturationLevel);
+            }
             if (!mControllerRefs.isEmpty()) {
                 return updateState();
             }
@@ -163,17 +171,27 @@
 
         private boolean addColorTransformController(
                 WeakReference<ColorTransformController> controller) {
+            clearExpiredReferences();
             mControllerRefs.add(controller);
-            if (mSaturationLevel != 100) {
+            if (!mSaturationLevels.isEmpty()) {
                 return updateState();
-            } else {
-                clearExpiredReferences();
             }
             return false;
         }
 
+        private int calculateSaturationLevel() {
+            int saturationLevel = FULL_SATURATION;
+            for (int i = 0; i < mSaturationLevels.size(); i++) {
+                final int level = mSaturationLevels.valueAt(i);
+                if (level < saturationLevel) {
+                    saturationLevel = level;
+                }
+            }
+            return saturationLevel;
+        }
+
         private boolean updateState() {
-            computeGrayscaleTransformMatrix(mSaturationLevel / 100f, mTransformMatrix);
+            computeGrayscaleTransformMatrix(calculateSaturationLevel() / 100f, mTransformMatrix);
 
             boolean updated = false;
             final Iterator<WeakReference<ColorTransformController>> iterator = mControllerRefs
@@ -190,7 +208,6 @@
                 }
             }
             return updated;
-
         }
 
         private void clearExpiredReferences() {
@@ -206,7 +223,7 @@
         }
 
         private void dump(PrintWriter pw) {
-            pw.println("            mSaturationLevel: " + mSaturationLevel);
+            pw.println("            mSaturationLevels: " + mSaturationLevels);
             pw.println("            mControllerRefs count: " + mControllerRefs.size());
         }
     }
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 2dc2cf0..95a98f1 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -44,6 +44,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.display.ColorDisplayManager;
@@ -73,6 +74,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.server.DisplayThread;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
@@ -817,9 +819,11 @@
         return LocalDateTime.MIN;
     }
 
-    private boolean setAppSaturationLevelInternal(String packageName, int saturationLevel) {
+    private boolean setAppSaturationLevelInternal(String callingPackageName,
+            String affectedPackageName, int saturationLevel) {
         return mAppSaturationController
-                .setSaturationLevel(packageName, mCurrentUser, saturationLevel);
+                .setSaturationLevel(callingPackageName, affectedPackageName, mCurrentUser,
+                        saturationLevel);
     }
 
     private void setColorModeInternal(@ColorMode int colorMode) {
@@ -1533,9 +1537,11 @@
             getContext().enforceCallingPermission(
                     Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
                     "Permission required to set display saturation level");
+            final String callingPackageName = LocalServices.getService(PackageManagerInternal.class)
+                    .getNameForUid(Binder.getCallingUid());
             final long token = Binder.clearCallingIdentity();
             try {
-                return setAppSaturationLevelInternal(packageName, level);
+                return setAppSaturationLevelInternal(callingPackageName, packageName, level);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/location/AppOpsHelper.java b/services/core/java/com/android/server/location/AppOpsHelper.java
index 9c27916..cb64c50 100644
--- a/services/core/java/com/android/server/location/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/AppOpsHelper.java
@@ -191,7 +191,7 @@
                     callerIdentity.uid,
                     callerIdentity.packageName,
                     callerIdentity.featureId,
-                    null) == AppOpsManager.MODE_ALLOWED;
+                    callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -210,7 +210,7 @@
                     callerIdentity.packageName,
                     false,
                     callerIdentity.featureId,
-                    null) == AppOpsManager.MODE_ALLOWED;
+                    callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -245,7 +245,7 @@
                     callerIdentity.uid,
                     callerIdentity.packageName,
                     callerIdentity.featureId,
-                    null) == AppOpsManager.MODE_ALLOWED;
+                    callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java
index b84fd13..8d508bb 100644
--- a/services/core/java/com/android/server/location/CallerIdentity.java
+++ b/services/core/java/com/android/server/location/CallerIdentity.java
@@ -83,12 +83,22 @@
      */
     public static CallerIdentity fromBinder(Context context, String packageName,
             @Nullable String featureId) {
+        return fromBinder(context, packageName, featureId, null);
+    }
+
+    /**
+     * Creates a CallerIdentity from the current binder identity, using the given package, feature
+     * id, and listener id. The package will be checked to enforce it belongs to the calling uid,
+     * and a security exception will be thrown if it is invalid.
+     */
+    public static CallerIdentity fromBinder(Context context, String packageName,
+            @Nullable String featureId, @Nullable String listenerId) {
         int uid = Binder.getCallingUid();
         if (!ArrayUtils.contains(context.getPackageManager().getPackagesForUid(uid), packageName)) {
             throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid);
         }
 
-        return fromBinderUnsafe(context, packageName, featureId);
+        return fromBinderUnsafe(context, packageName, featureId, listenerId);
     }
 
     /**
@@ -99,8 +109,19 @@
      */
     public static CallerIdentity fromBinderUnsafe(Context context, String packageName,
             @Nullable String featureId) {
+        return fromBinderUnsafe(context, packageName, featureId, null);
+    }
+
+    /**
+     * Creates a CallerIdentity from the current binder identity, using the given package, feature
+     * id, and listener id. The package will not be checked to enforce that it belongs to the
+     * calling uid - this method should only be used if the package will be validated by some other
+     * means, such as an appops call.
+     */
+    public static CallerIdentity fromBinderUnsafe(Context context, String packageName,
+            @Nullable String featureId, @Nullable String listenerId) {
         return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(),
-                UserHandle.getCallingUserId(), packageName, featureId,
+                UserHandle.getCallingUserId(), packageName, featureId, listenerId,
                 getBinderPermissionLevel(context));
     }
 
@@ -157,6 +178,9 @@
     /** The calling feature id. */
     public final @Nullable String featureId;
 
+    /** The calling listener id. */
+    public final @Nullable String listenerId;
+
     /**
      * The calling location permission level. This field should only be used for validating
      * permissions for API access. It should not be used for validating permissions for location
@@ -167,11 +191,18 @@
     @VisibleForTesting
     public CallerIdentity(int uid, int pid, int userId, String packageName,
             @Nullable String featureId, @PermissionLevel int permissionLevel) {
+        this(uid, pid, userId, packageName, featureId, null, permissionLevel);
+    }
+
+    private CallerIdentity(int uid, int pid, int userId, String packageName,
+            @Nullable String featureId, @Nullable String listenerId,
+            @PermissionLevel int permissionLevel) {
         this.uid = uid;
         this.pid = pid;
         this.userId = userId;
         this.packageName = Objects.requireNonNull(packageName);
         this.featureId = featureId;
+        this.listenerId = listenerId;
         this.permissionLevel = Preconditions.checkArgumentInRange(permissionLevel, PERMISSION_NONE,
                 PERMISSION_FINE, "permissionLevel");
     }
@@ -216,7 +247,8 @@
         return uid == that.uid
                 && pid == that.pid
                 && packageName.equals(that.packageName)
-                && Objects.equals(featureId, that.featureId);
+                && Objects.equals(featureId, that.featureId)
+                && Objects.equals(listenerId, that.listenerId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index ad3c8a6..d8acf0e 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -809,11 +809,11 @@
         locationRequest.setProvider(provider);
 
         // Ignore location settings if in emergency mode. This is only allowed for
-        // isUserEmergency request (introduced in HAL v2.0), or DBH request in HAL v1.1.
+        // isUserEmergency request (introduced in HAL v2.0), or HAL v1.1.
         if (mNIHandler.getInEmergency()) {
             GnssConfiguration.HalInterfaceVersion halVersion =
                     mGnssConfiguration.getHalInterfaceVersion();
-            if (isUserEmergency || (halVersion.mMajor < 2 && !independentFromGnss)) {
+            if (isUserEmergency || halVersion.mMajor < 2) {
                 locationRequest.setLocationSettingsIgnored(true);
                 durationMillis *= EMERGENCY_LOCATION_UPDATE_DURATION_MULTIPLIER;
             }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 52e9d7c..1931079 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -808,7 +808,7 @@
             return;
         }
 
-        // Can be null if the session is system's.
+        // Can be null if the session is system's or RCN.
         RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
                 .findRouterforSessionLocked(uniqueSessionId);
 
@@ -829,7 +829,7 @@
             return;
         }
 
-        // Can be null if the session is system's.
+        // Can be null if the session is system's or RCN.
         RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
                 .findRouterforSessionLocked(uniqueSessionId);
 
@@ -850,7 +850,7 @@
             return;
         }
 
-        // Can be null if the session is system's.
+        // Can be null if the session is system's or RCN.
         RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
                 .findRouterforSessionLocked(uniqueSessionId);
 
@@ -1232,7 +1232,7 @@
                     route.getOriginalId(), sessionHints);
         }
 
-        // routerRecord can be null if the session is system's.
+        // routerRecord can be null if the session is system's or RCN.
         private void selectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord,
                 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
             if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route,
@@ -1250,7 +1250,7 @@
                     route.getOriginalId());
         }
 
-        // routerRecord can be null if the session is system's.
+        // routerRecord can be null if the session is system's or RCN.
         private void deselectRouteOnHandler(long uniqueRequestId,
                 @Nullable RouterRecord routerRecord,
                 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
@@ -1270,7 +1270,7 @@
                     route.getOriginalId());
         }
 
-        // routerRecord can be null if the session is system's.
+        // routerRecord can be null if the session is system's or RCN.
         private void transferToRouteOnHandler(long uniqueRequestId,
                 @Nullable RouterRecord routerRecord,
                 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
@@ -1289,6 +1289,8 @@
                     route.getOriginalId());
         }
 
+        // routerRecord is null if and only if the session is created without the request, which
+        // includes the system's session and RCN cases.
         private boolean checkArgumentsForSessionControl(@Nullable RouterRecord routerRecord,
                 @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route,
                 @NonNull String description) {
@@ -1305,12 +1307,6 @@
                 return true;
             }
 
-            //TODO(b/152950479): Handle RCN case.
-            if (routerRecord == null) {
-                Slog.w(TAG, "Ignoring " + description + " route from unknown router.");
-                return false;
-            }
-
             RouterRecord matchingRecord = mSessionToRouterMap.get(uniqueSessionId);
             if (matchingRecord != routerRecord) {
                 Slog.w(TAG, "Ignoring " + description + " route from non-matching router. "
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
new file mode 100644
index 0000000..0bdf3f2
--- /dev/null
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -0,0 +1,211 @@
+/*
+ * 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.server.net;
+
+import static android.net.NetworkTemplate.getCollapsedRatType;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.telephony.Annotation;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class that watches for events that are triggered per subscription.
+ */
+// TODO (b/152176562): Write tests to verify subscription changes generate corresponding
+//  register/unregister calls.
+public class NetworkStatsSubscriptionsMonitor extends
+        SubscriptionManager.OnSubscriptionsChangedListener {
+
+    /**
+     * Interface that this monitor uses to delegate event handling to NetworkStatsService.
+     */
+    public interface Delegate {
+        /**
+         * Notify that the collapsed RAT type has been changed for any subscription. The method
+         * will also be triggered for any existing sub when start and stop monitoring.
+         *
+         * @param subscriberId IMSI of the subscription.
+         * @param collapsedRatType collapsed RAT type.
+         *                         @see android.net.NetworkTemplate#getCollapsedRatType(int).
+         */
+        void onCollapsedRatTypeChanged(@NonNull String subscriberId,
+                @Annotation.NetworkType int collapsedRatType);
+    }
+    private final Delegate mDelegate;
+
+    /**
+     * Receivers that watches for {@link ServiceState} changes for each subscription, to
+     * monitor the transitioning between Radio Access Technology(RAT) types for each sub.
+     */
+    @NonNull
+    private final CopyOnWriteArrayList<RatTypeListener> mRatListeners =
+            new CopyOnWriteArrayList<>();
+
+    @NonNull
+    private final SubscriptionManager mSubscriptionManager;
+    @NonNull
+    private final TelephonyManager mTeleManager;
+
+    @NonNull
+    private final Executor mExecutor;
+
+    NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Executor executor,
+            @NonNull Delegate delegate) {
+        super();
+        mSubscriptionManager = (SubscriptionManager) context.getSystemService(
+                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        mExecutor = executor;
+        mDelegate = delegate;
+    }
+
+    @Override
+    public void onSubscriptionsChanged() {
+        // Collect active subId list, hidden subId such as opportunistic subscriptions are
+        // also needed to track CBRS.
+        final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);
+
+        for (final int subId : newSubs) {
+            final RatTypeListener match = CollectionUtils.find(mRatListeners,
+                    it -> it.mSubId == subId);
+            if (match != null) continue;
+
+            // Create listener for every newly added sub. Also store subscriberId into it to
+            // prevent binder call to telephony when querying RAT.
+            final String subscriberId = mTeleManager.getSubscriberId(subId);
+            if (TextUtils.isEmpty(subscriberId)) {
+                Log.wtf(NetworkStatsService.TAG,
+                        "Empty subscriberId for newly added sub: " + subId);
+            }
+            final RatTypeListener listener =
+                    new RatTypeListener(mExecutor, this, subId, subscriberId);
+            mRatListeners.add(listener);
+
+            // Register listener to the telephony manager that associated with specific sub.
+            mTeleManager.createForSubscriptionId(subId)
+                    .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
+        }
+
+        for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
+            // If the new list contains the subId of the listener, keeps it.
+            final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId);
+            if (match != null) continue;
+
+            handleRemoveRatTypeListener(listener);
+        }
+    }
+
+    @NonNull
+    private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) {
+        final ArrayList<Integer> ret = new ArrayList<>();
+        final int[] ids = subscriptionManager.getCompleteActiveSubscriptionIdList();
+        for (int id : ids) ret.add(id);
+        return ret;
+    }
+
+    /**
+     * Get a collapsed RatType for the given subscriberId.
+     *
+     * @param subscriberId the target subscriberId
+     * @return collapsed RatType for the given subscriberId
+     */
+    public int getRatTypeForSubscriberId(@NonNull String subscriberId) {
+        final RatTypeListener match = CollectionUtils.find(mRatListeners,
+                it -> TextUtils.equals(subscriberId, it.mSubscriberId));
+        return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    }
+
+    /**
+     * Start monitoring events that triggered per subscription.
+     */
+    public void start() {
+        mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, this);
+    }
+
+    /**
+     * Unregister subscription changes and all listeners for each subscription.
+     */
+    public void stop() {
+        mSubscriptionManager.removeOnSubscriptionsChangedListener(this);
+
+        for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
+            handleRemoveRatTypeListener(listener);
+        }
+    }
+
+    private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
+        mTeleManager.createForSubscriptionId(listener.mSubId)
+                .listen(listener, PhoneStateListener.LISTEN_NONE);
+        mRatListeners.remove(listener);
+
+        // Removal of subscriptions doesn't generate RAT changed event, fire it for every
+        // RatTypeListener.
+        mDelegate.onCollapsedRatTypeChanged(
+                listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+    }
+
+    static class RatTypeListener extends PhoneStateListener {
+        // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}.
+        @NonNull
+        private final int mSubId;
+
+        // IMSI to identifying the corresponding network from {@link NetworkState}.
+        // See {@link TelephonyManager#getSubscriberId}.
+        @NonNull
+        private final String mSubscriberId;
+
+        private volatile int mLastCollapsedRatType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        @NonNull
+        private final NetworkStatsSubscriptionsMonitor mMonitor;
+
+        RatTypeListener(@NonNull Executor executor,
+                @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId,
+                @NonNull String subscriberId) {
+            super(executor);
+            mSubId = subId;
+            mSubscriberId = subscriberId;
+            mMonitor = monitor;
+        }
+
+        @Override
+        public void onServiceStateChanged(@NonNull ServiceState ss) {
+            final int networkType = ss.getDataNetworkType();
+            final int collapsedRatType = getCollapsedRatType(networkType);
+            if (collapsedRatType == mLastCollapsedRatType) return;
+
+            if (NetworkStatsService.LOGD) {
+                Log.d(NetworkStatsService.TAG, "subtype changed for sub(" + mSubId + "): "
+                        + mLastCollapsedRatType + " -> " + collapsedRatType);
+            }
+            mLastCollapsedRatType = collapsedRatType;
+            mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ed3b9f1..f9fc82b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -214,7 +214,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
-import android.util.FeatureFlagUtils;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
@@ -2745,15 +2744,15 @@
         return userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId;
     }
 
-    private ToastRecord getToastRecord(int pid, String packageName, IBinder token,
+    private ToastRecord getToastRecord(int uid, int pid, String packageName, IBinder token,
             @Nullable CharSequence text, @Nullable ITransientNotification callback, int duration,
             Binder windowToken, int displayId,
             @Nullable ITransientNotificationCallback textCallback) {
         if (callback == null) {
-            return new TextToastRecord(this, mStatusBar, pid, packageName, token, text, duration,
-                    windowToken, displayId, textCallback);
+            return new TextToastRecord(this, mStatusBar, uid, pid, packageName, token, text,
+                    duration, windowToken, displayId, textCallback);
         } else {
-            return new CustomToastRecord(this, pid, packageName, token, callback, duration,
+            return new CustomToastRecord(this, uid, pid, packageName, token, callback, duration,
                     windowToken, displayId);
         }
     }
@@ -2878,8 +2877,8 @@
 
                         Binder windowToken = new Binder();
                         mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId);
-                        record = getToastRecord(callingPid, pkg, token, text, callback, duration,
-                                windowToken, displayId, textCallback);
+                        record = getToastRecord(callingUid, callingPid, pkg, token, text, callback,
+                                duration, windowToken, displayId, textCallback);
                         mToastQueue.add(record);
                         index = mToastQueue.size() - 1;
                         keepProcessAliveForToastIfNeededLocked(callingPid);
@@ -5617,18 +5616,16 @@
 
         mUsageStats.registerEnqueuedByApp(pkg);
 
+        final StatusBarNotification n = new StatusBarNotification(
+                pkg, opPkg, id, tag, notificationUid, callingPid, notification,
+                user, null, System.currentTimeMillis());
+
         // setup local book-keeping
         String channelId = notification.getChannelId();
         if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
             channelId = (new Notification.TvExtender(notification)).getChannelId();
         }
-        String shortcutId = notification.getShortcutId();
-        if (FeatureFlagUtils.isEnabled(getContext(),
-                FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)
-            && shortcutId == null
-            && notification.getNotificationStyle() == Notification.MessagingStyle.class) {
-            shortcutId = id + tag + NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
-        }
+        String shortcutId = n.getShortcutId(getContext());
         final NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
                 pkg, notificationUid, channelId, shortcutId,
                 true /* parent ok */, false /* includeDeleted */);
@@ -5656,9 +5653,6 @@
             return;
         }
 
-        final StatusBarNotification n = new StatusBarNotification(
-                pkg, opPkg, id, tag, notificationUid, callingPid, notification,
-                user, null, System.currentTimeMillis());
         final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
         r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));
         r.setPostSilently(postSilently);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 54a0f9f..192df41 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1383,9 +1383,8 @@
                 || !Notification.MessagingStyle.class.equals(notification.getNotificationStyle())) {
             return false;
         }
-        if (mShortcutInfo == null
-                && !FeatureFlagUtils.isEnabled(
-                        mContext, FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)) {
+        if (mShortcutInfo == null && Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 1) {
             return false;
         }
         if (mIsNotConversationOverride) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index dbb246e..8154988 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -47,7 +47,6 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.FeatureFlagUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -181,8 +180,8 @@
         updateBadgingEnabled();
         updateBubblesEnabled();
         syncChannelsBypassingDnd(mContext.getUserId());
-        mAllowInvalidShortcuts = FeatureFlagUtils.isEnabled(mContext,
-                FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ);
+        mAllowInvalidShortcuts = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0) == 0;
     }
 
     public void readXml(XmlPullParser parser, boolean forRestore, int userId)
diff --git a/services/core/java/com/android/server/notification/toast/CustomToastRecord.java b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java
index aca6f48..2b91a00 100644
--- a/services/core/java/com/android/server/notification/toast/CustomToastRecord.java
+++ b/services/core/java/com/android/server/notification/toast/CustomToastRecord.java
@@ -23,6 +23,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.server.notification.NotificationManagerService;
@@ -35,11 +36,10 @@
 
     public final ITransientNotification callback;
 
-    public CustomToastRecord(
-            NotificationManagerService notificationManager, int pid, String packageName,
-            IBinder token, ITransientNotification callback, int duration, Binder windowToken,
-            int displayId) {
-        super(notificationManager, pid, packageName, token, duration, windowToken, displayId);
+    public CustomToastRecord(NotificationManagerService notificationManager, int uid, int pid,
+            String packageName, IBinder token, ITransientNotification callback, int duration,
+            Binder windowToken, int displayId) {
+        super(notificationManager, uid, pid, packageName, token, duration, windowToken, displayId);
         this.callback = checkNotNull(callback);
     }
 
@@ -74,8 +74,8 @@
     public String toString() {
         return "CustomToastRecord{"
                 + Integer.toHexString(System.identityHashCode(this))
+                + " " + pid + ":" +  pkg + "/" + UserHandle.formatUid(uid)
                 + " token=" + token
-                + " packageName=" + pkg
                 + " callback=" + callback
                 + " duration=" + getDuration()
                 + "}";
diff --git a/services/core/java/com/android/server/notification/toast/TextToastRecord.java b/services/core/java/com/android/server/notification/toast/TextToastRecord.java
index 3c231b4..544520e 100644
--- a/services/core/java/com/android/server/notification/toast/TextToastRecord.java
+++ b/services/core/java/com/android/server/notification/toast/TextToastRecord.java
@@ -23,6 +23,7 @@
 import android.app.ITransientNotificationCallback;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.server.notification.NotificationManagerService;
@@ -41,10 +42,10 @@
     private final ITransientNotificationCallback mCallback;
 
     public TextToastRecord(NotificationManagerService notificationManager,
-            @Nullable StatusBarManagerInternal statusBarManager, int pid, String packageName,
-            IBinder token, CharSequence text, int duration, Binder windowToken, int displayId,
-            @Nullable ITransientNotificationCallback callback) {
-        super(notificationManager, pid, packageName, token, duration, windowToken, displayId);
+            @Nullable StatusBarManagerInternal statusBarManager, int uid, int pid,
+            String packageName, IBinder token, CharSequence text, int duration, Binder windowToken,
+            int displayId, @Nullable ITransientNotificationCallback callback) {
+        super(notificationManager, uid, pid, packageName, token, duration, windowToken, displayId);
         mStatusBar = statusBarManager;
         mCallback = callback;
         this.text = checkNotNull(text);
@@ -59,7 +60,7 @@
             Slog.w(TAG, "StatusBar not available to show text toast for package " + pkg);
             return false;
         }
-        mStatusBar.showToast(pkg, token, text, windowToken, getDuration(), mCallback);
+        mStatusBar.showToast(uid, pkg, token, text, windowToken, getDuration(), mCallback);
         return true;
     }
 
@@ -75,8 +76,8 @@
     public String toString() {
         return "TextToastRecord{"
                 + Integer.toHexString(System.identityHashCode(this))
+                + " " + pid + ":" +  pkg + "/" + UserHandle.formatUid(uid)
                 + " token=" + token
-                + " packageName=" + pkg
                 + " text=" + text
                 + " duration=" + getDuration()
                 + "}";
diff --git a/services/core/java/com/android/server/notification/toast/ToastRecord.java b/services/core/java/com/android/server/notification/toast/ToastRecord.java
index ef75a6f..7915f70 100644
--- a/services/core/java/com/android/server/notification/toast/ToastRecord.java
+++ b/services/core/java/com/android/server/notification/toast/ToastRecord.java
@@ -28,6 +28,7 @@
  * Represents a toast, a transient notification.
  */
 public abstract class ToastRecord {
+    public final int uid;
     public final int pid;
     public final String pkg;
     public final IBinder token;
@@ -36,11 +37,10 @@
     protected final NotificationManagerService mNotificationManager;
     private int mDuration;
 
-    protected ToastRecord(
-            NotificationManagerService notificationManager,
-            int pid, String pkg, IBinder token, int duration,
-            Binder windowToken, int displayId) {
+    protected ToastRecord(NotificationManagerService notificationManager, int uid, int pid,
+            String pkg, IBinder token, int duration, Binder windowToken, int displayId) {
         this.mNotificationManager = notificationManager;
+        this.uid = uid;
         this.pid = pid;
         this.pkg = pkg;
         this.token = token;
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index e8e3834..7df8fc7 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -18,20 +18,19 @@
 
 import static android.content.Context.IDMAP_SERVICE;
 
-import static com.android.server.om.OverlayManagerService.DEBUG;
 import static com.android.server.om.OverlayManagerService.TAG;
 
 import android.os.IBinder;
 import android.os.IIdmap2;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
+import android.os.SystemClock;
+import android.os.SystemService;
 import android.util.Slog;
 
 import com.android.server.FgThread;
 
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -45,13 +44,14 @@
 
     // The amount of time in milliseconds to wait when attempting to connect to idmap service.
     private static final int SERVICE_CONNECT_TIMEOUT_MS = 5000;
+    private static final int SERVICE_CONNECT_INTERVAL_SLEEP_MS = 200;
 
-    private static final Object IDMAP_TOKEN = new Object();
     private static final String IDMAP_DAEMON = "idmap2d";
 
     private static IdmapDaemon sInstance;
     private volatile IIdmap2 mService;
     private final AtomicInteger mOpenedCount = new AtomicInteger();
+    private final Object mIdmapToken = new Object();
 
     /**
      * An {@link AutoCloseable} connection to the idmap service. When the connection is closed or
@@ -62,14 +62,14 @@
         private boolean mOpened = true;
 
         private Connection() {
-            synchronized (IDMAP_TOKEN) {
+            synchronized (mIdmapToken) {
                 mOpenedCount.incrementAndGet();
             }
         }
 
         @Override
         public void close() {
-            synchronized (IDMAP_TOKEN) {
+            synchronized (mIdmapToken) {
                 if (!mOpened) {
                     return;
                 }
@@ -82,7 +82,7 @@
                 }
 
                 FgThread.getHandler().postDelayed(() -> {
-                    synchronized (IDMAP_TOKEN) {
+                    synchronized (mIdmapToken) {
                         // Only stop the service if the service does not have an open connection.
                         if (mService == null || mOpenedCount.get() != 0) {
                             return;
@@ -91,7 +91,7 @@
                         stopIdmapService();
                         mService = null;
                     }
-                }, IDMAP_TOKEN, SERVICE_TIMEOUT_MS);
+                }, mIdmapToken, SERVICE_TIMEOUT_MS);
             }
         }
     }
@@ -104,14 +104,14 @@
     }
 
     String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
-            int userId) throws Exception {
-        try (Connection connection = connect()) {
+            int userId) throws TimeoutException, RemoteException {
+        try (Connection c = connect()) {
             return mService.createIdmap(targetPath, overlayPath, policies, enforce, userId);
         }
     }
 
-    boolean removeIdmap(String overlayPath, int userId) throws Exception {
-        try (Connection connection = connect()) {
+    boolean removeIdmap(String overlayPath, int userId) throws TimeoutException, RemoteException {
+        try (Connection c = connect()) {
             return mService.removeIdmap(overlayPath, userId);
         }
     }
@@ -119,76 +119,54 @@
     boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
              int userId)
             throws Exception {
-        try (Connection connection = connect()) {
+        try (Connection c = connect()) {
             return mService.verifyIdmap(targetPath, overlayPath, policies, enforce, userId);
         }
     }
 
-    String getIdmapPath(String overlayPath, int userId) throws Exception {
-        try (Connection connection = connect()) {
+    String getIdmapPath(String overlayPath, int userId) throws TimeoutException, RemoteException {
+        try (Connection c = connect()) {
             return mService.getIdmapPath(overlayPath, userId);
         }
     }
 
-    private static void startIdmapService() {
-        SystemProperties.set("ctl.start", IDMAP_DAEMON);
+    private IBinder getIdmapService() throws TimeoutException, RemoteException {
+        SystemService.start(IDMAP_DAEMON);
+
+        final long endMillis = SystemClock.elapsedRealtime() + SERVICE_CONNECT_TIMEOUT_MS;
+        while (SystemClock.elapsedRealtime() <= endMillis) {
+            final IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
+            if (binder != null) {
+                binder.linkToDeath(
+                        () -> Slog.w(TAG, String.format("service '%s' died", IDMAP_SERVICE)), 0);
+                return binder;
+            }
+
+            try {
+                Thread.sleep(SERVICE_CONNECT_INTERVAL_SLEEP_MS);
+            } catch (InterruptedException ignored) {
+            }
+        }
+
+        throw new TimeoutException(
+            String.format("Failed to connect to '%s' in %d milliseconds", IDMAP_SERVICE,
+                    SERVICE_CONNECT_TIMEOUT_MS));
     }
 
     private static void stopIdmapService() {
-        SystemProperties.set("ctl.stop", IDMAP_DAEMON);
+        SystemService.stop(IDMAP_DAEMON);
     }
 
-    private Connection connect() throws Exception {
-        synchronized (IDMAP_TOKEN) {
-            FgThread.getHandler().removeCallbacksAndMessages(IDMAP_TOKEN);
+    private Connection connect() throws TimeoutException, RemoteException {
+        synchronized (mIdmapToken) {
+            FgThread.getHandler().removeCallbacksAndMessages(mIdmapToken);
             if (mService != null) {
                 // Not enough time has passed to stop the idmap service. Reuse the existing
                 // interface.
                 return new Connection();
             }
 
-            // Start the idmap service if it is not currently running.
-            startIdmapService();
-
-            // Block until the service is found.
-            FutureTask<IBinder> bindIdmap = new FutureTask<>(() -> {
-                while (true) {
-                    try {
-                        IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
-                        if (binder != null) {
-                            return binder;
-                        }
-                    } catch (Exception e) {
-                        Slog.e(TAG, "service '" + IDMAP_SERVICE + "' not retrieved; "
-                                + e.getMessage());
-                    }
-                    Thread.sleep(100);
-                }
-            });
-
-            IBinder binder;
-            try {
-                FgThread.getHandler().postAtFrontOfQueue(bindIdmap);
-                binder = bindIdmap.get(SERVICE_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (Exception rethrow) {
-                Slog.e(TAG, "service '" + IDMAP_SERVICE + "' not found;");
-                throw rethrow;
-            }
-
-            try {
-                binder.linkToDeath(() -> {
-                    Slog.w(TAG, "service '" + IDMAP_SERVICE + "' died");
-                }, 0);
-            } catch (RemoteException rethrow) {
-                Slog.e(TAG, "service '" + IDMAP_SERVICE + "' failed to be bound");
-                throw rethrow;
-            }
-
-            mService = IIdmap2.Stub.asInterface(binder);
-            if (DEBUG) {
-                Slog.d(TAG, "service '" + IDMAP_SERVICE + "' connected");
-            }
-
+            mService = IIdmap2.Stub.asInterface(getIdmapService());
             return new Connection();
         }
     }
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index b9d6561..1e4dc7b 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -30,7 +30,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
 import android.os.Binder;
 import android.os.Environment;
@@ -137,7 +137,8 @@
     /**
      * Called by package manager service to scan apex package files when device boots up.
      *
-     * @param packageParser The package parser which supports caches.
+     * @param packageParser The package parser to support apex package parsing and caching parsed
+     *                      results.
      * @param executorService An executor to support parallel package parsing.
      */
     abstract void scanApexPackagesTraced(@NonNull PackageParser2 packageParser,
@@ -505,7 +506,13 @@
                         }
                         factoryPackagesSet.add(packageInfo.packageName);
                     }
-                } else if (throwable instanceof PackageParser.PackageParserException) {
+                } else if (throwable instanceof PackageParserException) {
+                    final PackageParserException e = (PackageParserException) throwable;
+                    // Skip parsing non-coreApp apex file if system is in minimal boot state.
+                    if (e.error == PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED) {
+                        Slog.w(TAG, "Scan apex failed, not a coreApp:" + ai.modulePath);
+                        continue;
+                    }
                     throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable);
                 } else {
                     throw new IllegalStateException("Unexpected exception occurred while parsing "
@@ -522,7 +529,8 @@
                     "APEX packages have not been scanned");
             boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
             boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
-            for (PackageInfo packageInfo: mAllPackagesCache) {
+            for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
+                final PackageInfo packageInfo = mAllPackagesCache.get(i);
                 if (!packageInfo.packageName.equals(packageName)) {
                     continue;
                 }
@@ -581,7 +589,8 @@
             if (!isApexSupported()) return false;
             Preconditions.checkState(mAllPackagesCache != null,
                     "APEX packages have not been scanned");
-            for (PackageInfo packageInfo : mAllPackagesCache) {
+            for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
+                final PackageInfo packageInfo = mAllPackagesCache.get(i);
                 if (packageInfo.packageName.equals(packageName)) {
                     return true;
                 }
@@ -839,7 +848,8 @@
                 IndentingPrintWriter ipw) {
             ipw.println();
             ipw.increaseIndent();
-            for (PackageInfo pi : packagesCache) {
+            for (int i = 0, size = packagesCache.size(); i < size; i++) {
+                final PackageInfo pi = packagesCache.get(i);
                 if (packageName != null && !packageName.equals(pi.packageName)) {
                     continue;
                 }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index ba7583f..dab4bfd 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -46,6 +46,7 @@
 
 import java.io.File;
 import java.nio.file.Paths;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -106,6 +107,8 @@
     private static final long mDowngradeUnusedAppsThresholdInMillis =
             getDowngradeUnusedAppsThresholdInMillis();
 
+    private static List<PackagesUpdatedListener> sPackagesUpdatedListeners = new ArrayList<>();
+
     public static void schedule(Context context) {
         if (isBackgroundDexoptDisabled()) {
             return;
@@ -244,6 +247,7 @@
             }
         }
         notifyPinService(updatedPackages);
+        notifyPackagesUpdated(updatedPackages);
         // Ran to completion, so we abandon our timeslice and do not reschedule.
         jobFinished(jobParams, /* reschedule */ false);
     }
@@ -391,6 +395,7 @@
         } finally {
             // Always let the pinner service know about changes.
             notifyPinService(updatedPackages);
+            notifyPackagesUpdated(updatedPackages);
         }
     }
 
@@ -642,6 +647,32 @@
         }
     }
 
+    public static interface PackagesUpdatedListener {
+        /** Callback when packages have been updated by the bg-dexopt service. */
+        public void onPackagesUpdated(ArraySet<String> updatedPackages);
+    }
+
+    public static void addPackagesUpdatedListener(PackagesUpdatedListener listener) {
+        synchronized (sPackagesUpdatedListeners) {
+            sPackagesUpdatedListeners.add(listener);
+        }
+    }
+
+    public static void removePackagesUpdatedListener(PackagesUpdatedListener listener) {
+        synchronized (sPackagesUpdatedListeners) {
+            sPackagesUpdatedListeners.remove(listener);
+        }
+    }
+
+    /** Notify all listeners (#addPackagesUpdatedListener) that packages have been updated. */
+    private void notifyPackagesUpdated(ArraySet<String> updatedPackages) {
+        synchronized (sPackagesUpdatedListeners) {
+            for (PackagesUpdatedListener listener : sPackagesUpdatedListeners) {
+                listener.onPackagesUpdated(updatedPackages);
+            }
+        }
+    }
+
     private static long getDowngradeUnusedAppsThresholdInMillis() {
         final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
         String sysPropValue = SystemProperties.get(sysPropKey);
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 4087675..28c8642d 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -294,6 +294,12 @@
                         .getAllCrossProfilePackages().contains(packageName));
     }
 
+    private boolean isCrossProfilePackageWhitelistedByDefault(String packageName) {
+        return mInjector.withCleanCallingIdentity(() ->
+                mInjector.getDevicePolicyManagerInternal()
+                        .getDefaultCrossProfilePackages().contains(packageName));
+    }
+
     private List<UserHandle> getTargetUserProfilesUnchecked(
             String packageName, @UserIdInt int userId) {
         return mInjector.withCleanCallingIdentity(() -> {
@@ -528,6 +534,9 @@
 
     @Override
     public boolean canConfigureInteractAcrossProfiles(String packageName) {
+        if (!canUserAttemptToConfigureInteractAcrossProfiles(packageName)) {
+            return false;
+        }
         if (!hasOtherProfileWithPackageInstalled(packageName, mInjector.getCallingUserId())) {
             return false;
         }
@@ -546,7 +555,35 @@
             return false;
         }
         return hasRequestedAppOpPermission(
-                AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName);
+                AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)
+                && !isPlatformSignedAppWithNonUserConfigurablePermission(packageName, profileIds);
+    }
+
+    private boolean isPlatformSignedAppWithNonUserConfigurablePermission(
+            String packageName, int[] profileIds) {
+        return !isCrossProfilePackageWhitelistedByDefault(packageName)
+                && isPlatformSignedAppWithAutomaticProfilesPermission(packageName, profileIds);
+    }
+
+    /**
+     * Only platform-signed apps can be granted INTERACT_ACROSS_PROFILES automatically without user
+     * consent.
+     *
+     * Returns true if the app is automatically granted the permission in at least one profile.
+     */
+    private boolean isPlatformSignedAppWithAutomaticProfilesPermission(
+            String packageName, int[] profileIds) {
+        for (int userId : profileIds) {
+            final int uid = mInjector.getPackageManagerInternal().getPackageUidInternal(
+                    packageName, /* flags= */ 0, userId);
+            if (uid == -1) {
+                continue;
+            }
+            if (isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, uid)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f37af3a..9fb468e 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -724,6 +724,30 @@
     }
 
     /**
+     * Deletes all snapshots of credential encrypted user data, where the snapshot id is not
+     * included in {@code retainSnapshotIds}.
+     *
+     * @param userId id of the user whose user data snapshots to delete.
+     * @param retainSnapshotIds ids of the snapshots that should not be deleted.
+     *
+     * @return {@code true} if the operation was successful, or {@code false} if a remote call
+     * shouldn't be continued. See {@link #checkBeforeRemote}.
+     *
+     * @throws InstallerException if failed to delete user data snapshot.
+     */
+    public boolean destroyCeSnapshotsNotSpecified(@UserIdInt int userId,
+            int[] retainSnapshotIds) throws InstallerException {
+        if (!checkBeforeRemote()) return false;
+
+        try {
+            mInstalld.destroyCeSnapshotsNotSpecified(null, userId, retainSnapshotIds);
+            return true;
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
+    /**
      * Migrates obb data from its legacy location {@code /data/media/obb} to
      * {@code /data/media/0/Android/obb}. This call is idempotent and a fast no-op if data has
      * already been migrated.
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index e625aef..e7d0c41 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -401,7 +401,8 @@
             return DEX_OPT_FAILED;
         }
         String classLoaderContext = null;
-        if (dexUseInfo.isUnknownClassLoaderContext() || dexUseInfo.isVariableClassLoaderContext()) {
+        if (dexUseInfo.isUnsupportedClassLoaderContext()
+                || dexUseInfo.isVariableClassLoaderContext()) {
             // If we have an unknown (not yet set), or a variable class loader chain. Just extract
             // the dex file.
             compilerFilter = "extract";
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 8ff7ea9..57908f3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -82,6 +82,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
+import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import libcore.io.IoUtils;
@@ -105,6 +106,7 @@
 import java.util.Objects;
 import java.util.Random;
 import java.util.function.IntPredicate;
+import java.util.function.Supplier;
 
 /** The service responsible for installing packages. */
 public class PackageInstallerService extends IPackageInstaller.Stub implements
@@ -194,7 +196,8 @@
         }
     };
 
-    public PackageInstallerService(Context context, PackageManagerService pm) {
+    public PackageInstallerService(Context context, PackageManagerService pm,
+            Supplier<PackageParser2> apexParserSupplier) {
         mContext = context;
         mPm = pm;
         mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class);
@@ -213,7 +216,7 @@
         mSessionsDir.mkdirs();
 
         mApexManager = ApexManager.getInstance();
-        mStagingManager = new StagingManager(this, context);
+        mStagingManager = new StagingManager(this, context, apexParserSupplier);
     }
 
     boolean okToSendBroadcasts()  {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 1248ec0..f07fa50 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1830,7 +1830,9 @@
             throws PackageManagerException {
         final List<File> addedFiles = getAddedApksLocked();
         if (addedFiles.isEmpty()) {
-            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    String.format("Session: %d. No packages staged in %s", sessionId,
+                          stageDir.getAbsolutePath()));
         }
 
         if (ArrayUtils.size(addedFiles) > 1) {
@@ -1921,7 +1923,9 @@
 
         final List<File> addedFiles = getAddedApksLocked();
         if (addedFiles.isEmpty() && removeSplitList.size() == 0) {
-            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
+            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
+                    String.format("Session: %d. No packages staged in %s", sessionId,
+                          stageDir.getAbsolutePath()));
         }
 
         // Verify that all staged packages are internally consistent
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f96ab1d..f693555 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -417,6 +417,7 @@
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -3571,7 +3572,11 @@
                 }
             }
 
-            mInstallerService = new PackageInstallerService(mContext, this);
+            // Prepare a supplier of package parser for the staging manager to parse apex file
+            // during the staging installation.
+            final Supplier<PackageParser2> apexParserSupplier = () -> new PackageParser2(
+                    mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback);
+            mInstallerService = new PackageInstallerService(mContext, this, apexParserSupplier);
             final Pair<ComponentName, String> instantAppResolverComponent =
                     getInstantAppResolverLPr();
             if (instantAppResolverComponent != null) {
@@ -9679,6 +9684,20 @@
     @Override
     public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
             String loaderIsa) {
+        if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName)
+                && Binder.getCallingUid() != Process.SYSTEM_UID) {
+            Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid="
+                    + Binder.getCallingUid());
+            // Do not record dex loads from processes pretending to be system server.
+            // Only the system server should be assigned the package "android", so reject calls
+            // that don't satisfy the constraint.
+            //
+            // notifyDexLoad is a PM API callable from the app process. So in theory, apps could
+            // craft calls to this API and pretend to be system server. Doing so poses no particular
+            // danger for dex load reporting or later dexopt, however it is a sensible check to do
+            // in order to verify the expectations.
+            return;
+        }
         int userId = UserHandle.getCallingUserId();
         ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
         if (ai == null) {
@@ -11305,7 +11324,7 @@
                     Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
                             parsedPackage.getPackageName() + " " +
                             AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
-                            + ", " 
+                            + ", "
                             + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
                 }
             }
@@ -17534,31 +17553,26 @@
                 + " Activities needs verification ...");
 
         int count = 0;
-        boolean handlesWebUris = false;
-        final boolean alreadyVerified;
+
         synchronized (mLock) {
             // If this is a new install and we see that we've already run verification for this
             // package, we have nothing to do: it means the state was restored from backup.
-            final IntentFilterVerificationInfo ivi =
-                    mSettings.getIntentFilterVerificationLPr(packageName);
-            alreadyVerified = (ivi != null);
-            if (!replacing && alreadyVerified) {
-                if (DEBUG_DOMAIN_VERIFICATION) {
-                    Slog.i(TAG, "Package " + packageName + " already verified: status="
-                            + ivi.getStatusString());
+            if (!replacing) {
+                IntentFilterVerificationInfo ivi =
+                        mSettings.getIntentFilterVerificationLPr(packageName);
+                if (ivi != null) {
+                    if (DEBUG_DOMAIN_VERIFICATION) {
+                        Slog.i(TAG, "Package " + packageName+ " already verified: status="
+                                + ivi.getStatusString());
+                    }
+                    return;
                 }
-                return;
             }
 
-            // If any filters need to be verified, then all need to be.  In addition, we need to
-            // know whether an updating app has any web navigation intent filters, to re-
-            // examine handling policy even if not re-verifying.
+            // If any filters need to be verified, then all need to be.
             boolean needToVerify = false;
             for (ParsedActivity a : activities) {
                 for (ParsedIntentInfo filter : a.getIntents()) {
-                    if (filter.handlesWebUris(true)) {
-                        handlesWebUris = true;
-                    }
                     if (filter.needsVerification()
                             && needsNetworkVerificationLPr(a.getPackageName())) {
                         if (DEBUG_DOMAIN_VERIFICATION) {
@@ -17566,23 +17580,19 @@
                                     "Intent filter needs verification, so processing all filters");
                         }
                         needToVerify = true;
-                        // It's safe to break out here because filter.needsVerification()
-                        // can only be true if filter.handlesWebUris(true) returns true, so
-                        // we've already noted that.
                         break;
                     }
                 }
             }
 
-            // Note whether this app publishes any web navigation handling support at all,
-            // and whether there are any web-nav filters that fit the profile for running
-            // a verification pass now.
             if (needToVerify) {
+                final boolean needsVerification = needsNetworkVerificationLPr(packageName);
                 final int verificationId = mIntentFilterVerificationToken++;
                 for (ParsedActivity a : activities) {
                     for (ParsedIntentInfo filter : a.getIntents()) {
-                        if (filter.handlesWebUris(true)
-                                && needsNetworkVerificationLPr(a.getPackageName())) {
+                        // Run verification against hosts mentioned in any web-nav intent filter,
+                        // even if the filter matches non-web schemes as well
+                        if (needsVerification && filter.handlesWebUris(false)) {
                             if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
                                     "Verification needed for IntentFilter:" + filter.toString());
                             mIntentFilterVerifier.addOneIntentFilterVerification(
@@ -17595,23 +17605,13 @@
         }
 
         if (count > 0) {
-            // count > 0 means that we're running a full verification pass
             if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
                     + " IntentFilter verification" + (count > 1 ? "s" : "")
                     +  " for userId:" + userId);
             mIntentFilterVerifier.startVerifications(userId);
-        } else if (alreadyVerified && handlesWebUris) {
-            // App used autoVerify in the past, no longer does, but still handles web
-            // navigation starts.
-            if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(TAG, "App changed web filters but no longer verifying - resetting policy");
-            }
-            synchronized (mLock) {
-                clearIntentFilterVerificationsLPw(packageName, userId);
-            }
         } else {
             if (DEBUG_DOMAIN_VERIFICATION) {
-                Slog.d(TAG, "No web filters or no prior verify policy for " + packageName);
+                Slog.d(TAG, "No filters or not all autoVerify for " + packageName);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 091535d..f6e4e1f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1291,7 +1291,6 @@
             return false;
         }
         ps.clearDomainVerificationStatusForUser(userId);
-        ps.setIntentFilterVerificationInfo(null);
         return true;
     }
 
@@ -4668,6 +4667,10 @@
                 pw.print(prefix); pw.print("  privateFlags="); printFlags(pw,
                         privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println();
             }
+            if (pkg.hasPreserveLegacyExternalStorage()) {
+                pw.print(prefix); pw.print("  hasPreserveLegacyExternalStorage=true");
+                pw.println();
+            }
             pw.print(prefix); pw.print("  forceQueryable="); pw.print(ps.pkg.isForceQueryable());
             if (ps.forceQueryableOverride) {
                 pw.print(" (override=true)");
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 60292b4..8f6bd02 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -35,11 +35,11 @@
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
 import android.content.rollback.IRollbackManager;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
@@ -68,8 +68,10 @@
 import com.android.internal.content.PackageHelper;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
+import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.rollback.WatchdogRollbackLogger;
 
 import java.io.File;
@@ -80,6 +82,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
+import java.util.function.Supplier;
 
 /**
  * This class handles staged install sessions, i.e. install sessions that require packages to
@@ -94,6 +97,7 @@
     private final PowerManager mPowerManager;
     private final Context mContext;
     private final PreRebootVerificationHandler mPreRebootVerificationHandler;
+    private final Supplier<PackageParser2> mPackageParserSupplier;
 
     @GuardedBy("mStagedSessions")
     private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
@@ -105,9 +109,11 @@
     private final List<String> mFailedPackageNames = new ArrayList<>();
     private String mNativeFailureReason;
 
-    StagingManager(PackageInstallerService pi, Context context) {
+    StagingManager(PackageInstallerService pi, Context context,
+            Supplier<PackageParser2> packageParserSupplier) {
         mPi = pi;
         mContext = context;
+        mPackageParserSupplier = packageParserSupplier;
 
         mApexManager = ApexManager.getInstance();
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -223,17 +229,21 @@
         final List<String> apexPackageNames = new ArrayList<>();
         for (ApexInfo apexInfo : apexInfoList.apexInfos) {
             final PackageInfo packageInfo;
-            int flags = PackageManager.GET_META_DATA;
-            PackageParser.Package pkg;
-            try {
+            final int flags = PackageManager.GET_META_DATA;
+            try (PackageParser2 packageParser = mPackageParserSupplier.get()) {
                 File apexFile = new File(apexInfo.modulePath);
-                PackageParser pp = new PackageParser();
-                pkg = pp.parsePackage(apexFile, flags, false);
+                final ParsedPackage parsedPackage = packageParser.parsePackage(
+                        apexFile, flags, false);
+                packageInfo = PackageInfoWithoutStateUtils.generate(parsedPackage, apexInfo, flags);
+                if (packageInfo == null) {
+                    throw new PackageManagerException(
+                            SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+                            "Unable to generate package info: " + apexInfo.modulePath);
+                }
             } catch (PackageParserException e) {
                 throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                         "Failed to parse APEX package " + apexInfo.modulePath, e);
             }
-            packageInfo = PackageParser.generatePackageInfo(pkg, apexInfo, flags);
             final PackageInfo activePackage = mApexManager.getPackageInfo(packageInfo.packageName,
                     ApexManager.MATCH_ACTIVE_PACKAGE);
             if (activePackage == null) {
@@ -254,8 +264,9 @@
     private int retrieveRollbackIdForCommitSession(int sessionId) throws PackageManagerException {
         RollbackManager rm = mContext.getSystemService(RollbackManager.class);
 
-        List<RollbackInfo> rollbacks = rm.getRecentlyCommittedRollbacks();
-        for (RollbackInfo rollback : rollbacks) {
+        final List<RollbackInfo> rollbacks = rm.getRecentlyCommittedRollbacks();
+        for (int i = 0, size = rollbacks.size(); i < size; i++) {
+            final RollbackInfo rollback = rollbacks.get(i);
             if (rollback.getCommittedSessionId() == sessionId) {
                 return rollback.getRollbackId();
             }
@@ -388,7 +399,8 @@
                     }
                 }
             }
-            for (PackageInstallerSession childSession : childrenSessions) {
+            for (int i = 0, size = childrenSessions.size(); i < size; i++) {
+                final PackageInstallerSession childSession = childrenSessions.get(i);
                 if (sessionContainsApex(childSession)) {
                     apexSessions.add(childSession);
                 }
@@ -402,15 +414,15 @@
         IRollbackManager rm = IRollbackManager.Stub.asInterface(
                 ServiceManager.getService(Context.ROLLBACK_SERVICE));
 
-        for (PackageInstallerSession apexSession : apexSessions) {
-            String packageName = apexSession.getPackageName();
+        for (int i = 0, sessionsSize = apexSessions.size(); i < sessionsSize; i++) {
+            final String packageName = apexSessions.get(i).getPackageName();
             // Perform any snapshots or restores for the APEX itself
             snapshotAndRestoreApexUserData(packageName, allUsers, rm);
 
             // Process the apks inside the APEX
-            List<String> apksInApex = mApexManager.getApksInApex(packageName);
-            for (String apk: apksInApex) {
-                snapshotAndRestoreApkInApexUserData(apk, allUsers, rm);
+            final List<String> apksInApex = mApexManager.getApksInApex(packageName);
+            for (int j = 0, apksSize = apksInApex.size(); j < apksSize; j++) {
+                snapshotAndRestoreApkInApexUserData(apksInApex.get(j), allUsers, rm);
             }
         }
     }
@@ -637,7 +649,8 @@
                     0 /* UserHandle.SYSTEM */);
             PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
             apkSession.open();
-            for (String apkFilePath : apkFilePaths) {
+            for (int i = 0, size = apkFilePaths.size(); i < size; i++) {
+                final String apkFilePath = apkFilePaths.get(i);
                 File apkFile = new File(apkFilePath);
                 ParcelFileDescriptor pfd = ParcelFileDescriptor.open(apkFile,
                         ParcelFileDescriptor.MODE_READ_ONLY);
@@ -705,9 +718,9 @@
                         "Unable to prepare multi-package session for staged session");
             }
 
-            for (PackageInstallerSession sessionToClone : childSessions) {
-                PackageInstallerSession apkChildSession =
-                        createAndWriteApkSession(sessionToClone, preReboot);
+            for (int i = 0, size = childSessions.size(); i < size; i++) {
+                final PackageInstallerSession apkChildSession = createAndWriteApkSession(
+                        childSessions.get(i), preReboot);
                 try {
                     apkParentSession.addChildSessionId(apkChildSession.sessionId);
                 } catch (IllegalStateException e) {
@@ -1206,10 +1219,9 @@
             // multi-package sessions, find all the child sessions that contain an APEX.
             if (hasApex) {
                 try {
-                    final List<PackageInfo> apexPackages =
-                            submitSessionToApexService(session);
-                    for (PackageInfo apexPackage : apexPackages) {
-                        validateApexSignature(apexPackage);
+                    final List<PackageInfo> apexPackages = submitSessionToApexService(session);
+                    for (int i = 0, size = apexPackages.size(); i < size; i++) {
+                        validateApexSignature(apexPackages.get(i));
                     }
                 } catch (PackageManagerException e) {
                     session.setStagedSessionFailed(e.error, e.getMessage());
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 117cc5e..ebdf856 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -17,13 +17,17 @@
 package com.android.server.pm.dex;
 
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
 
+import static java.util.function.Function.identity;
+
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackagePartitions;
 import android.os.FileUtils;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -45,6 +49,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -66,13 +71,12 @@
  */
 public class DexManager {
     private static final String TAG = "DexManager";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob";
     private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST =
             "pm.dexopt.priv-apps-oob-list";
 
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
     private final Context mContext;
 
     // Maps package name to code locations.
@@ -103,19 +107,6 @@
     private static int DEX_SEARCH_FOUND_SPLIT = 2;  // dex file is a split apk
     private static int DEX_SEARCH_FOUND_SECONDARY = 3;  // dex file is a secondary dex
 
-    /**
-     * We do not record packages that have no secondary dex files or that are not used by other
-     * apps. This is an optimization to reduce the amount of data that needs to be written to
-     * disk (apps will not usually be shared so this trims quite a bit the number we record).
-     *
-     * To make this behaviour transparent to the callers which need use information on packages,
-     * DexManager will return this DEFAULT instance from
-     * {@link DexManager#getPackageUseInfoOrDefault}. It has no data about secondary dex files and
-     * is marked as not being used by other apps. This reflects the intended behaviour when we don't
-     * find the package in the underlying data file.
-     */
-    private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
-
     public DexManager(Context context, IPackageManager pms, PackageDexOptimizer pdo,
             Installer installer, Object installLock) {
         mContext = context;
@@ -190,10 +181,14 @@
                 boolean primaryOrSplit = searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY ||
                         searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT;
 
-                if (primaryOrSplit && !isUsedByOtherApps) {
+                if (primaryOrSplit && !isUsedByOtherApps
+                        && !PLATFORM_PACKAGE_NAME.equals(searchResult.mOwningPackageName)) {
                     // If the dex file is the primary apk (or a split) and not isUsedByOtherApps
                     // do not record it. This case does not bring any new usable information
                     // and can be safely skipped.
+                    // Note this is just an optimization that makes things easier to read in the
+                    // package-dex-use file since we don't need to pollute it with redundant info.
+                    // However, we always record system server packages.
                     continue;
                 }
 
@@ -211,7 +206,7 @@
                     // async write to disk to make sure we don't loose the data in case of a reboot.
 
                     if (mPackageDexUsage.record(searchResult.mOwningPackageName,
-                            dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
+                            dexPath, loaderUserId, loaderIsa, primaryOrSplit,
                             loadingAppInfo.packageName, classLoaderContext)) {
                         mPackageDexUsage.maybeWriteAsync();
                     }
@@ -227,6 +222,23 @@
     }
 
     /**
+     * Check if the dexPath belongs to system server.
+     * System server can load code from different location, so we cast a wide-net here, and
+     * assume that if the paths is on any of the registered system partitions then it can be loaded
+     * by system server.
+     */
+    private boolean isSystemServerDexPathSupportedForOdex(String dexPath) {
+        ArrayList<PackagePartitions.SystemPartition> partitions =
+                PackagePartitions.getOrderedPartitions(identity());
+        for (int i = 0; i < partitions.size(); i++) {
+            if (partitions.get(i).containsPath(dexPath)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Read the dex usage from disk and populate the code cache locations.
      * @param existingPackages a map containing information about what packages
      *          are available to what users. Only packages in this list will be
@@ -364,7 +376,9 @@
 
         try {
             mPackageDexUsage.read();
-            mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+            List<String> packagesToKeepDataAbout = new ArrayList<>();
+            mPackageDexUsage.syncData(
+                    packageToUsersMap, packageToCodePaths, packagesToKeepDataAbout);
         } catch (Exception e) {
             mPackageDexUsage.clear();
             Slog.w(TAG, "Exception while loading package dex usage. "
@@ -391,8 +405,17 @@
      * to access the package use.
      */
     public PackageUseInfo getPackageUseInfoOrDefault(String packageName) {
+        // We do not record packages that have no secondary dex files or that are not used by other
+        // apps. This is an optimization to reduce the amount of data that needs to be written to
+        // disk (apps will not usually be shared so this trims quite a bit the number we record).
+        //
+        // To make this behaviour transparent to the callers which need use information on packages,
+        // DexManager will return this DEFAULT instance from
+        // {@link DexManager#getPackageUseInfoOrDefault}. It has no data about secondary dex files
+        // and is marked as not being used by other apps. This reflects the intended behaviour when
+        // we don't find the package in the underlying data file.
         PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName);
-        return useInfo == null ? DEFAULT_USE_INFO : useInfo;
+        return useInfo == null ? new PackageUseInfo(packageName) : useInfo;
     }
 
     /**
@@ -542,7 +565,7 @@
     // TODO(calin): questionable API in the presence of class loaders context. Needs amends as the
     // compilation happening here will use a pessimistic context.
     public RegisterDexModuleResult registerDexModule(ApplicationInfo info, String dexPath,
-            boolean isUsedByOtherApps, int userId) {
+            boolean isSharedModule, int userId) {
         // Find the owning package record.
         DexSearchResult searchResult = getDexPackage(info, dexPath, userId);
 
@@ -559,11 +582,14 @@
 
         // We found the package. Now record the usage for all declared ISAs.
         boolean update = false;
+        // If this is a shared module set the loading package to an arbitrary package name
+        // so that we can mark that module as usedByOthers.
+        String loadingPackage = isSharedModule ? ".shared.module" : searchResult.mOwningPackageName;
         for (String isa : getAppDexInstructionSets(info.primaryCpuAbi, info.secondaryCpuAbi)) {
             boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName,
-                    dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false,
+                    dexPath, userId, isa, /*primaryOrSplit*/ false,
                     searchResult.mOwningPackageName,
-                    PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT);
+                    PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT);
             update |= newUpdate;
         }
         if (update) {
@@ -603,12 +629,6 @@
      */
     private DexSearchResult getDexPackage(
             ApplicationInfo loadingAppInfo, String dexPath, int userId) {
-        // Ignore framework code.
-        // TODO(calin): is there a better way to detect it?
-        if (dexPath.startsWith("/system/framework/")) {
-            return new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND);
-        }
-
         // First, check if the package which loads the dex file actually owns it.
         // Most of the time this will be true and we can return early.
         PackageCodeLocations loadingPackageCodeLocations =
@@ -631,6 +651,28 @@
             }
         }
 
+        // We could not find the owning package amongst regular apps.
+        // If the loading package is system server, see if the dex file resides
+        // on any of the potentially system server owning location and if so,
+        // assuming system server ownership.
+        //
+        // Note: We don't have any way to detect which code paths are actually
+        // owned by system server. We can only assume that such paths are on
+        // system partitions.
+        if (PLATFORM_PACKAGE_NAME.equals(loadingAppInfo.packageName)) {
+            if (isSystemServerDexPathSupportedForOdex(dexPath)) {
+                // We record system server dex files as secondary dex files.
+                // The reason is that we only record the class loader context for secondary dex
+                // files and we expect that all primary apks are loaded with an empty class loader.
+                // System server dex files may be loaded in non-empty class loader so we need to
+                // keep track of their context.
+                return new DexSearchResult(PLATFORM_PACKAGE_NAME, DEX_SEARCH_FOUND_SECONDARY);
+            } else {
+                Slog.wtf(TAG, "System server loads dex files outside paths supported for odex: "
+                        + dexPath);
+            }
+        }
+
         if (DEBUG) {
             // TODO(calin): Consider checking for /data/data symlink.
             // /data/data/ symlinks /data/user/0/ and there's nothing stopping apps
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 08763e7..10760f5 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -39,10 +39,12 @@
 import java.io.Reader;
 import java.io.StringWriter;
 import java.io.Writer;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -51,25 +53,21 @@
  * Stat file which store usage information about dex files.
  */
 public class PackageDexUsage extends AbstractStatsBase<Void> {
-    private final static String TAG = "PackageDexUsage";
+    private static final String TAG = "PackageDexUsage";
 
-    // We support previous version to ensure that the usage list remains valid cross OTAs.
-    private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1 = 1;
-    // Version 2 added:
-    //  - the list of packages that load the dex files
-    //  - class loader contexts for secondary dex files
-    //  - usage for all code paths (including splits)
-    private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2 = 2;
+    // We are currently at version 2.
+    // Version 1 was introduced in Nougat and Version 2 in Oreo.
+    // We dropped version 1 support in R since all devices should have updated
+    // already.
+    private static final int PACKAGE_DEX_USAGE_VERSION = 2;
 
-    private final static int PACKAGE_DEX_USAGE_VERSION = PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
-
-    private final static String PACKAGE_DEX_USAGE_VERSION_HEADER =
+    private static final String PACKAGE_DEX_USAGE_VERSION_HEADER =
             "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__";
 
-    private final static String SPLIT_CHAR = ",";
-    private final static String CODE_PATH_LINE_CHAR = "+";
-    private final static String DEX_LINE_CHAR = "#";
-    private final static String LOADING_PACKAGE_CHAR = "@";
+    private static final String SPLIT_CHAR = ",";
+    private static final String CODE_PATH_LINE_CHAR = "+";
+    private static final String DEX_LINE_CHAR = "#";
+    private static final String LOADING_PACKAGE_CHAR = "@";
 
     // One of the things we record about dex files is the class loader context that was used to
     // load them. That should be stable but if it changes we don't keep track of variable contexts.
@@ -77,10 +75,6 @@
     // skip optimizations on that dex files.
     /*package*/ static final String VARIABLE_CLASS_LOADER_CONTEXT =
             "=VariableClassLoaderContext=";
-    // The markers used for unknown class loader contexts. This can happen if the dex file was
-    // recorded in a previous version and we didn't have a chance to update its usage.
-    /*package*/ static final String UNKNOWN_CLASS_LOADER_CONTEXT =
-            "=UnknownClassLoaderContext=";
 
     // The marker used for unsupported class loader contexts (no longer written, may occur in old
     // files so discarded on read). Note: this matches
@@ -126,7 +120,7 @@
      *         has been seen before.
      */
     /* package */ boolean record(String owningPackageName, String dexPath, int ownerUserId,
-            String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit,
+            String loaderIsa, boolean primaryOrSplit,
             String loadingPackageName, String classLoaderContext) {
         if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
             throw new IllegalArgumentException("loaderIsa " + loaderIsa + " is unsupported");
@@ -135,20 +129,22 @@
             throw new IllegalArgumentException("Null classLoaderContext");
         }
         if (classLoaderContext.equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)) {
+            Slog.e(TAG, "Unsupported context?");
             return false;
         }
 
+        boolean isUsedByOtherApps = !owningPackageName.equals(loadingPackageName);
+
         synchronized (mPackageUseInfoMap) {
             PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
             if (packageUseInfo == null) {
                 // This is the first time we see the package.
-                packageUseInfo = new PackageUseInfo();
+                packageUseInfo = new PackageUseInfo(owningPackageName);
                 if (primaryOrSplit) {
                     // If we have a primary or a split apk, set isUsedByOtherApps.
                     // We do not need to record the loaderIsa or the owner because we compile
                     // primaries for all users and all ISAs.
-                    packageUseInfo.mergeCodePathUsedByOtherApps(dexPath, isUsedByOtherApps,
-                            owningPackageName, loadingPackageName);
+                    packageUseInfo.mergePrimaryCodePaths(dexPath, loadingPackageName);
                 } else {
                     // For secondary dex files record the loaderISA and the owner. We'll need
                     // to know under which user to compile and for what ISA.
@@ -164,9 +160,8 @@
                 // We already have data on this package. Amend it.
                 if (primaryOrSplit) {
                     // We have a possible update on the primary apk usage. Merge
-                    // isUsedByOtherApps information and return if there was an update.
-                    return packageUseInfo.mergeCodePathUsedByOtherApps(
-                            dexPath, isUsedByOtherApps, owningPackageName, loadingPackageName);
+                    // dex path information and return if there was an update.
+                    return packageUseInfo.mergePrimaryCodePaths(dexPath, loadingPackageName);
                 } else {
                     DexUseInfo newData = new DexUseInfo(
                             isUsedByOtherApps, ownerUserId, classLoaderContext, loaderIsa);
@@ -281,7 +276,7 @@
 
             // Write the code paths used by other apps.
             for (Map.Entry<String, Set<String>> codeEntry :
-                    packageUseInfo.mCodePathsUsedByOtherApps.entrySet()) {
+                    packageUseInfo.mPrimaryCodePaths.entrySet()) {
                 String codePath = codeEntry.getKey();
                 Set<String> loadingPackages = codeEntry.getValue();
                 fpw.println(CODE_PATH_LINE_CHAR + codePath);
@@ -339,7 +334,9 @@
             version = Integer.parseInt(
                     versionLine.substring(PACKAGE_DEX_USAGE_VERSION_HEADER.length()));
             if (!isSupportedVersion(version)) {
-                throw new IllegalStateException("Unexpected version: " + version);
+                Slog.w(TAG, "Unexpected package-dex-use version: " + version
+                        + ". Not reading from it");
+                return;
             }
         }
 
@@ -377,9 +374,8 @@
                     throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
                 }
 
-                // In version 2 we added the loading packages and class loader context.
-                Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
-                String classLoaderContext = maybeReadClassLoaderContext(in, version);
+                Set<String> loadingPackages = readLoadingPackages(in, version);
+                String classLoaderContext = readClassLoaderContext(in, version);
 
                 if (UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(classLoaderContext)) {
                     // We used to record use of unsupported class loaders, but we no longer do.
@@ -410,34 +406,16 @@
                 }
                 currentPackageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
             } else if (line.startsWith(CODE_PATH_LINE_CHAR)) {
-                // This is a code path used by other apps line.
-                if (version < PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
-                    throw new IllegalArgumentException("Unexpected code path line when parsing " +
-                            "PackageDexUseData: " + line);
-                }
-
                 // Expects 2 lines:
                 //    +code_paths
                 //    @loading_packages
                 String codePath = line.substring(CODE_PATH_LINE_CHAR.length());
-                Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
-                currentPackageData.mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
+                Set<String> loadingPackages = readLoadingPackages(in, version);
+                currentPackageData.mPrimaryCodePaths.put(codePath, loadingPackages);
             } else {
                 // This is a package line.
-                if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
-                    currentPackage = line;
-                    currentPackageData = new PackageUseInfo();
-                } else {
-                    // Old version (<2)
-                    // We expect it to be: `packageName,isUsedByOtherApps`.
-                    String[] elems = line.split(SPLIT_CHAR);
-                    if (elems.length != 2) {
-                        throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
-                    }
-                    currentPackage = elems[0];
-                    currentPackageData = new PackageUseInfo();
-                    currentPackageData.mUsedByOtherAppsBeforeUpgrade = readBoolean(elems[1]);
-                }
+                currentPackage = line;
+                currentPackageData = new PackageUseInfo(currentPackage);
                 data.put(currentPackage, currentPackageData);
             }
         }
@@ -449,46 +427,31 @@
     }
 
     /**
-     * Reads the class loader context encoding from the buffer {@code in} if
-     * {@code version} is at least {PACKAGE_DEX_USAGE_VERSION}.
+     * Reads the class loader context encoding from the buffer {@code in}.
      */
-    private String maybeReadClassLoaderContext(BufferedReader in, int version) throws IOException {
-        String context = null;
-        if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
-            context = in.readLine();
-            if (context == null) {
-                throw new IllegalStateException("Could not find the classLoaderContext line.");
-            }
+    private String readClassLoaderContext(BufferedReader in, int version) throws IOException {
+        String context = in.readLine();
+        if (context == null) {
+            throw new IllegalStateException("Could not find the classLoaderContext line.");
         }
-        // The context might be empty if we didn't have the chance to update it after a version
-        // upgrade. In this case return the special marker so that we recognize this is an unknown
-        // context.
-        return context == null ? UNKNOWN_CLASS_LOADER_CONTEXT : context;
+        return context;
     }
 
     /**
-     * Reads the list of loading packages from the buffer {@code in} if
-     * {@code version} is at least {PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2}.
+     * Reads the list of loading packages from the buffer {@code in}.
      */
-    private Set<String> maybeReadLoadingPackages(BufferedReader in, int version)
+    private Set<String> readLoadingPackages(BufferedReader in, int version)
             throws IOException {
-        if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
-            String line = in.readLine();
-            if (line == null) {
-                throw new IllegalStateException("Could not find the loadingPackages line.");
-            }
-            // We expect that most of the times the list of loading packages will be empty.
-            if (line.length() == LOADING_PACKAGE_CHAR.length()) {
-                return Collections.emptySet();
-            } else {
-                Set<String> result = new HashSet<>();
-                Collections.addAll(result,
-                        line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR));
-                return result;
-            }
-        } else {
-            return Collections.emptySet();
+        String line = in.readLine();
+        if (line == null) {
+            throw new IllegalStateException("Could not find the loadingPackages line.");
         }
+        Set<String> result = new HashSet<>();
+        if (line.length() != LOADING_PACKAGE_CHAR.length()) {
+            Collections.addAll(result,
+                    line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR));
+        }
+        return result;
     }
 
     /**
@@ -501,21 +464,26 @@
     }
 
     private boolean isSupportedVersion(int version) {
-        return version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1
-                || version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
+        return version == PACKAGE_DEX_USAGE_VERSION;
     }
 
     /**
      * Syncs the existing data with the set of available packages by removing obsolete entries.
      */
     /*package*/ void syncData(Map<String, Set<Integer>> packageToUsersMap,
-            Map<String, Set<String>> packageToCodePaths) {
+            Map<String, Set<String>> packageToCodePaths,
+            List<String> packagesToKeepDataAbout) {
         synchronized (mPackageUseInfoMap) {
             Iterator<Map.Entry<String, PackageUseInfo>> pIt =
                     mPackageUseInfoMap.entrySet().iterator();
             while (pIt.hasNext()) {
                 Map.Entry<String, PackageUseInfo> pEntry = pIt.next();
                 String packageName = pEntry.getKey();
+                if (packagesToKeepDataAbout.contains(packageName)) {
+                    // This is a package for which we should keep the data even if it's not
+                    // in the list of user packages.
+                    continue;
+                }
                 PackageUseInfo packageUseInfo = pEntry.getValue();
                 Set<Integer> users = packageToUsersMap.get(packageName);
                 if (users == null) {
@@ -536,22 +504,31 @@
 
                     // Sync the code paths.
                     Set<String> codePaths = packageToCodePaths.get(packageName);
-                    Iterator<Map.Entry<String, Set<String>>> codeIt =
-                        packageUseInfo.mCodePathsUsedByOtherApps.entrySet().iterator();
-                    while (codeIt.hasNext()) {
-                        if (!codePaths.contains(codeIt.next().getKey())) {
-                            codeIt.remove();
+
+                    Iterator<Map.Entry<String, Set<String>>> recordedIt =
+                            packageUseInfo.mPrimaryCodePaths.entrySet().iterator();
+                    while (recordedIt.hasNext()) {
+                        Map.Entry<String, Set<String>> entry = recordedIt.next();
+                        String recordedCodePath = entry.getKey();
+                        if (!codePaths.contains(recordedCodePath)) {
+                            // Clean up a non existing code path.
+                            recordedIt.remove();
+                        } else {
+                            // Clean up a non existing loading package.
+                            Set<String> recordedLoadingPackages = entry.getValue();
+                            Iterator<String> recordedLoadingPackagesIt =
+                                    recordedLoadingPackages.iterator();
+                            while (recordedLoadingPackagesIt.hasNext()) {
+                                String recordedLoadingPackage = recordedLoadingPackagesIt.next();
+                                if (!packagesToKeepDataAbout.contains(recordedLoadingPackage)
+                                        && !packageToUsersMap.containsKey(recordedLoadingPackage)) {
+                                    recordedLoadingPackagesIt.remove();
+                                }
+                            }
                         }
                     }
 
-                    // In case the package was marked as used by other apps in a previous version
-                    // propagate the flag to all the code paths.
-                    // See mUsedByOtherAppsBeforeUpgrade docs on why it is important to do it.
-                    if (packageUseInfo.mUsedByOtherAppsBeforeUpgrade) {
-                        for (String codePath : codePaths) {
-                            packageUseInfo.mergeCodePathUsedByOtherApps(codePath, true, null, null);
-                        }
-                    } else if (!packageUseInfo.isAnyCodePathUsedByOtherApps()
+                    if (!packageUseInfo.isAnyCodePathUsedByOtherApps()
                         && packageUseInfo.mDexUseInfoMap.isEmpty()) {
                         // The package is not used by other apps and we removed all its dex files
                         // records. Remove the entire package record as well.
@@ -712,35 +689,26 @@
      * Stores data on how a package and its dex files are used.
      */
     public static class PackageUseInfo {
+        // The name of the package this info belongs to.
+        private final String mPackageName;
         // The app's code paths that are used by other apps.
         // The key is the code path and the value is the set of loading packages.
-        private final Map<String, Set<String>> mCodePathsUsedByOtherApps;
+        private final Map<String, Set<String>> mPrimaryCodePaths;
         // Map dex paths to their data (isUsedByOtherApps, owner id, loader isa).
         private final Map<String, DexUseInfo> mDexUseInfoMap;
 
-        // Keeps track of whether or not this package was used by other apps before
-        // we upgraded to VERSION 4 which records the info for each code path separately.
-        // This is unwanted complexity but without it we risk to profile guide compile
-        // something that supposed to be shared. For example:
-        //   1) we determine that chrome is used by another app
-        //   2) we take an OTA which upgrades the way we keep track of usage data
-        //   3) chrome doesn't get used until the background job executes
-        //   4) as part of the backgound job we now think that chrome is not used by others
-        //      and we speed-profile.
-        //   5) as a result the next time someone uses chrome it will extract from apk since
-        //      the compiled code will be private.
-        private boolean mUsedByOtherAppsBeforeUpgrade;
-
-        /*package*/ PackageUseInfo() {
-            mCodePathsUsedByOtherApps = new HashMap<>();
+        /*package*/ PackageUseInfo(String packageName) {
+            mPrimaryCodePaths = new HashMap<>();
             mDexUseInfoMap = new HashMap<>();
+            mPackageName = packageName;
         }
 
         // Creates a deep copy of the `other`.
         private PackageUseInfo(PackageUseInfo other) {
-            mCodePathsUsedByOtherApps = new HashMap<>();
-            for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) {
-                mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue()));
+            mPackageName = other.mPackageName;
+            mPrimaryCodePaths = new HashMap<>();
+            for (Map.Entry<String, Set<String>> e : other.mPrimaryCodePaths.entrySet()) {
+                mPrimaryCodePaths.put(e.getKey(), new HashSet<>(e.getValue()));
             }
 
             mDexUseInfoMap = new HashMap<>();
@@ -749,28 +717,29 @@
             }
         }
 
-        private boolean mergeCodePathUsedByOtherApps(String codePath, boolean isUsedByOtherApps,
-                String owningPackageName, String loadingPackage) {
-            if (!isUsedByOtherApps) {
-                // Nothing to update if the the code path is not used by other apps.
-                return false;
-            }
-
-            boolean newCodePath = false;
-            Set<String> loadingPackages = mCodePathsUsedByOtherApps.get(codePath);
+        private boolean mergePrimaryCodePaths(String codePath, String loadingPackage) {
+            Set<String> loadingPackages = mPrimaryCodePaths.get(codePath);
             if (loadingPackages == null) {
                 loadingPackages = new HashSet<>();
-                mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
-                newCodePath = true;
+                mPrimaryCodePaths.put(codePath, loadingPackages);
             }
-            boolean newLoadingPackage = loadingPackage != null
-                    && !loadingPackage.equals(owningPackageName)
-                    && loadingPackages.add(loadingPackage);
-            return newCodePath || newLoadingPackage;
+            return loadingPackages.add(loadingPackage);
         }
 
         public boolean isUsedByOtherApps(String codePath) {
-            return mCodePathsUsedByOtherApps.containsKey(codePath);
+            if (mPrimaryCodePaths.containsKey(codePath)) {
+                Set<String> loadingPackages = mPrimaryCodePaths.get(codePath);
+                if (loadingPackages.contains(mPackageName)) {
+                    // If the owning package is in the list then this code path
+                    // is used by others if there are other packages in the list.
+                    return loadingPackages.size() > 1;
+                } else {
+                    // The owning package is not in the loading packages. So if
+                    // the list is non-empty then the code path is used by others.
+                    return !loadingPackages.isEmpty();
+                }
+            }
+            return false;
         }
 
         public Map<String, DexUseInfo> getDexUseInfoMap() {
@@ -778,11 +747,11 @@
         }
 
         public Set<String> getLoadingPackages(String codePath) {
-            return mCodePathsUsedByOtherApps.getOrDefault(codePath, null);
+            return mPrimaryCodePaths.getOrDefault(codePath, null);
         }
 
         public boolean isAnyCodePathUsedByOtherApps() {
-            return !mCodePathsUsedByOtherApps.isEmpty();
+            return !mPrimaryCodePaths.isEmpty();
         }
 
         /**
@@ -790,16 +759,16 @@
          * Returns whether or not there was an update.
          */
         /*package*/ boolean clearCodePathUsedByOtherApps() {
-            // Update mUsedByOtherAppsBeforeUpgrade as well to be consistent with
-            // the new data. This is not saved to disk so we don't need to return it.
-            mUsedByOtherAppsBeforeUpgrade = true;
-
-            if (mCodePathsUsedByOtherApps.isEmpty()) {
-                return false;
-            } else {
-                mCodePathsUsedByOtherApps.clear();
-                return true;
+            boolean updated = false;
+            List<String> retainOnlyOwningPackage = new ArrayList<>(1);
+            retainOnlyOwningPackage.add(mPackageName);
+            for (Map.Entry<String, Set<String>> entry : mPrimaryCodePaths.entrySet()) {
+                // Remove or loading packages but the owning one.
+                if (entry.getValue().retainAll(retainOnlyOwningPackage)) {
+                    updated = true;
+                }
             }
+            return updated;
         }
     }
 
@@ -847,11 +816,9 @@
             boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
 
             String oldClassLoaderContext = mClassLoaderContext;
-            if (isUnknownOrUnsupportedContext(mClassLoaderContext)) {
-                // Can happen if we read a previous version.
+            if (isUnsupportedContext(mClassLoaderContext)) {
                 mClassLoaderContext = dexUseInfo.mClassLoaderContext;
-            } else if (!isUnknownOrUnsupportedContext(dexUseInfo.mClassLoaderContext)
-                        && !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
+            } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
                 // We detected a context change.
                 mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
             }
@@ -862,11 +829,8 @@
                     || !Objects.equals(oldClassLoaderContext, mClassLoaderContext);
         }
 
-        private static boolean isUnknownOrUnsupportedContext(String context) {
-            // TODO: Merge UNKNOWN_CLASS_LOADER_CONTEXT & UNSUPPORTED_CLASS_LOADER_CONTEXT cases
-            // into UNSUPPORTED_CLASS_LOADER_CONTEXT.
-            return UNKNOWN_CLASS_LOADER_CONTEXT.equals(context)
-                    || UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context);
+        private static boolean isUnsupportedContext(String context) {
+            return UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context);
         }
 
         public boolean isUsedByOtherApps() {
@@ -887,10 +851,8 @@
 
         public String getClassLoaderContext() { return mClassLoaderContext; }
 
-        public boolean isUnknownClassLoaderContext() {
-            // The class loader context may be unknown if we loaded the data from a previous version
-            // which didn't save the context.
-            return isUnknownOrUnsupportedContext(mClassLoaderContext);
+        public boolean isUnsupportedClassLoaderContext() {
+            return isUnsupportedContext(mClassLoaderContext);
         }
 
         public boolean isVariableClassLoaderContext() {
diff --git a/services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java b/services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java
new file mode 100644
index 0000000..807c82d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.pm.dex;
+
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import dalvik.system.BaseDexClassLoader;
+import dalvik.system.VMRuntime;
+
+import java.util.Map;
+
+/**
+ * Reports dex file use to the package manager on behalf of system server.
+ */
+public class SystemServerDexLoadReporter implements BaseDexClassLoader.Reporter {
+    private static final String TAG = "SystemServerDexLoadReporter";
+
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final IPackageManager mPackageManager;
+
+    private SystemServerDexLoadReporter(IPackageManager pm) {
+        mPackageManager = pm;
+    }
+
+    @Override
+    public void report(Map<String, String> classLoaderContextMap) {
+        if (DEBUG) {
+            Slog.i(TAG, "Reporting "  + classLoaderContextMap);
+        }
+        if (classLoaderContextMap.isEmpty()) {
+            Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap");
+            return;
+        }
+
+        try {
+            mPackageManager.notifyDexLoad(
+                    PLATFORM_PACKAGE_NAME,
+                    classLoaderContextMap,
+                    VMRuntime.getRuntime().vmInstructionSet());
+        } catch (RemoteException ignored) {
+            // We're in system server, it can't happen.
+        }
+    }
+
+    /**
+     * Configures system server dex file reporting.
+     * <p>The method will install a reporter in the BaseDexClassLoader and also
+     * force the reporting of any dex files already loaded by the system server.
+     */
+    public static void configureSystemServerDexReporter(IPackageManager pm) {
+        Slog.i(TAG, "Configuring system server dex reporter");
+
+        SystemServerDexLoadReporter reporter = new SystemServerDexLoadReporter(pm);
+        BaseDexClassLoader.setReporter(reporter);
+        ClassLoader currrentClassLoader = reporter.getClass().getClassLoader();
+        if (currrentClassLoader instanceof BaseDexClassLoader) {
+            ((BaseDexClassLoader) currrentClassLoader).reportClassLoaderChain();
+        } else {
+            Slog.wtf(TAG, "System server class loader is not a BaseDexClassLoader. type="
+                    + currrentClassLoader.getClass().getName());
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
new file mode 100644
index 0000000..ee3c406
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.pm.parsing.library;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+/**
+ * Updates a package to remove dependency on com.google.android.maps library.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class ComGoogleAndroidMapsUpdater extends PackageSharedLibraryUpdater {
+
+    private static final String LIBRARY_NAME = "com.google.android.maps";
+
+    @Override
+    public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+        parsedPackage.removeUsesLibrary(LIBRARY_NAME);
+        parsedPackage.removeUsesOptionalLibrary(LIBRARY_NAME);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index 1d018c4..1405a7d 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -45,6 +45,9 @@
     static {
         final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
 
+        // Remove com.google.android.maps library.
+        packageUpdaters.add(new ComGoogleAndroidMapsUpdater());
+
         // Automatically add the org.apache.http.legacy library to the app classpath if the app
         // targets < P.
         packageUpdaters.add(new OrgApacheHttpLegacyUpdater());
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index a726d39..e3faffa 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -882,7 +882,7 @@
 
     public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to default browser for user:" + userId);
-        grantPermissionsToSystemPackage(packageName, userId, ALWAYS_LOCATION_PERMISSIONS);
+        grantPermissionsToSystemPackage(packageName, userId, FOREGROUND_LOCATION_PERMISSIONS);
     }
 
     private String getDefaultSystemHandlerActivityPackage(String intentAction, int userId) {
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 161f304..27288d8 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -30,6 +30,7 @@
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -47,6 +48,7 @@
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.permission.PermissionControllerManager;
+import android.provider.Settings;
 import android.provider.Telephony;
 import android.telecom.TelecomManager;
 import android.util.ArrayMap;
@@ -70,7 +72,9 @@
 import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -180,8 +184,6 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         intentFilter.addDataScheme("package");
 
-
-        /* TODO ntmyren: enable receiver when test flakes are fixed
         getContext().registerReceiverAsUser(new BroadcastReceiver() {
             final List<Integer> mUserSetupUids = new ArrayList<>(200);
             final Map<UserHandle, PermissionControllerManager> mPermControllerManagers =
@@ -232,7 +234,6 @@
                 manager.updateUserSensitiveForApp(uid);
             }
         }, UserHandle.ALL, intentFilter, null, null);
-         */
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b41bc5c..cd6c5bf 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -513,10 +513,7 @@
 
     // The screen brightness setting override from the window manager
     // to allow the current foreground activity to override the brightness.
-    // Use -1 to disable.
-    private int mScreenBrightnessOverrideFromWindowManager = -1;
-
-    private float mScreenBrightnessOverrideFromWindowManagerFloat =
+    private float mScreenBrightnessOverrideFromWindowManager =
             PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
     // The window manager has determined the user to be inactive via other means.
@@ -2846,7 +2843,7 @@
                 screenBrightnessOverride = mScreenBrightnessSettingDefault;
             } else if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
                 autoBrightness = false;
-                screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManagerFloat;
+                screenBrightnessOverride = mScreenBrightnessOverrideFromWindowManager;
             } else {
                 autoBrightness = (mScreenBrightnessModeSetting ==
                         Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
@@ -2927,8 +2924,8 @@
         return !mIsVrModeEnabled && mScreenBrightnessBoostInProgress;
     }
 
-    private static boolean isValidBrightness(int value) {
-        return value >= 0 && value <= 255;
+    private static boolean isValidBrightness(float value) {
+        return value >= PowerManager.BRIGHTNESS_MIN && value <= PowerManager.BRIGHTNESS_MAX;
     }
 
     @VisibleForTesting
@@ -3583,13 +3580,11 @@
         }
     }
 
-    // TODO(brightnessfloat): change to float
-    private void setScreenBrightnessOverrideFromWindowManagerInternal(int brightness) {
+    private void setScreenBrightnessOverrideFromWindowManagerInternal(float brightness) {
         synchronized (mLock) {
-            if (mScreenBrightnessOverrideFromWindowManager != brightness) {
+            if (!BrightnessSynchronizer.floatEquals(mScreenBrightnessOverrideFromWindowManager,
+                    brightness)) {
                 mScreenBrightnessOverrideFromWindowManager = brightness;
-                mScreenBrightnessOverrideFromWindowManagerFloat =
-                        BrightnessSynchronizer.brightnessIntToFloat(mContext, brightness);
                 mDirty |= DIRTY_SETTINGS;
                 updatePowerStateLocked();
             }
@@ -3890,8 +3885,8 @@
                     + isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() + ")");
             pw.println("  mStayOnWhilePluggedInSetting=" + mStayOnWhilePluggedInSetting);
             pw.println("  mScreenBrightnessModeSetting=" + mScreenBrightnessModeSetting);
-            pw.println("  mScreenBrightnessOverrideFromWindowManagerFloat="
-                    + mScreenBrightnessOverrideFromWindowManagerFloat);
+            pw.println("  mScreenBrightnessOverrideFromWindowManager="
+                    + mScreenBrightnessOverrideFromWindowManager);
             pw.println("  mUserActivityTimeoutOverrideFromWindowManager="
                     + mUserActivityTimeoutOverrideFromWindowManager);
             pw.println("  mUserInactiveOverrideFromWindowManager="
@@ -4231,7 +4226,7 @@
             proto.write(
                     PowerServiceSettingsAndConfigurationDumpProto
                             .SCREEN_BRIGHTNESS_OVERRIDE_FROM_WINDOW_MANAGER,
-                    mScreenBrightnessOverrideFromWindowManagerFloat);
+                    mScreenBrightnessOverrideFromWindowManager);
             proto.write(
                     PowerServiceSettingsAndConfigurationDumpProto
                             .USER_ACTIVITY_TIMEOUT_OVERRIDE_FROM_WINDOW_MANAGER_MS,
@@ -5430,10 +5425,10 @@
     @VisibleForTesting
     final class LocalService extends PowerManagerInternal {
         @Override
-        public void setScreenBrightnessOverrideFromWindowManager(int screenBrightness) {
-            if (screenBrightness < PowerManager.BRIGHTNESS_DEFAULT
-                    || screenBrightness > PowerManager.BRIGHTNESS_ON) {
-                screenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+        public void setScreenBrightnessOverrideFromWindowManager(float screenBrightness) {
+            if (screenBrightness < PowerManager.BRIGHTNESS_MIN
+                    || screenBrightness > PowerManager.BRIGHTNESS_MAX) {
+                screenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             }
             setScreenBrightnessOverrideFromWindowManagerInternal(screenBrightness);
         }
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index 74958b6..cf1bfc3 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -21,10 +21,7 @@
       "options": [
         {"include-filter": "com.android.server.power"},
         {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
-        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
-        {
-          "exclude-filter": "com.android.server.power.PowerManagerServiceTest#testWakefulnessAwake_ShouldWakeUpWhenPluggedIn"
-        }
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     }
   ],
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 5f871ad4..83e99b0 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -505,6 +505,11 @@
             rollbackIds[i] = mRollbacks.get(i).info.getRollbackId();
         }
         ApexManager.getInstance().destroyCeSnapshotsNotSpecified(userId, rollbackIds);
+        try {
+            mInstaller.destroyCeSnapshotsNotSpecified(userId, rollbackIds);
+        } catch (Installer.InstallerException ie) {
+            Slog.e(TAG, "Failed to delete snapshots for user: " + userId, ie);
+        }
     }
 
     @WorkerThread
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index 841aca5..225bd82 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Environment;
 import android.os.IBinder;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -37,6 +38,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
@@ -131,12 +133,17 @@
         // duplicate the same loading logic here.
 
         // Load certificates trusted by the device manufacturer.
-        loadCertificatesFromDirectory("/product/etc/security/fsverity");
+        // NB: Directories need to be synced with system/security/fsverity_init/fsverity_init.cpp.
+        final String relativeDir = "etc/security/fsverity";
+        loadCertificatesFromDirectory(Environment.getRootDirectory().toPath()
+                .resolve(relativeDir));
+        loadCertificatesFromDirectory(Environment.getProductDirectory().toPath()
+                .resolve(relativeDir));
     }
 
-    private void loadCertificatesFromDirectory(String path) {
+    private void loadCertificatesFromDirectory(Path path) {
         try {
-            File[] files = new File(path).listFiles();
+            File[] files = path.toFile().listFiles();
             if (files == null) {
                 return;
             }
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 008933f..da1b7f3 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -114,6 +114,8 @@
         DEAD
     };
 
+    private Boolean mCaptureState;
+
     private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
     private final @NonNull Context mContext;
     private Map<Integer, Set<ModuleService>> mModules;
@@ -224,6 +226,11 @@
             mDelegate.setCaptureState(active);
         } catch (Exception e) {
             throw handleException(e);
+        } finally {
+            // It is safe to lock here - local operation.
+            synchronized (this) {
+                mCaptureState = active;
+            }
         }
     }
 
@@ -274,8 +281,11 @@
                 "This implementation is not inteded to be used directly with Binder.");
     }
 
-    @Override public void dump(PrintWriter pw) {
+    @Override
+    public void dump(PrintWriter pw) {
         synchronized (this) {
+            pw.printf("Capture state is %s\n", mCaptureState == null ? "uninitialized"
+                    : (mCaptureState ? "active" : "inactive"));
             if (mModules != null) {
                 for (int handle : mModules.keySet()) {
                     pw.println("=========================================");
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index d88dccb..26497d8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -131,7 +131,7 @@
      * @see com.android.internal.statusbar.IStatusBar#showToast(String, IBinder, CharSequence,
      * IBinder, int, ITransientNotificationCallback)
      */
-    void showToast(String packageName, IBinder token, CharSequence text,
+    void showToast(int uid, String packageName, IBinder token, CharSequence text,
             IBinder windowToken, int duration,
             @Nullable ITransientNotificationCallback textCallback);
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 78ef68c..d7a0c987 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -502,12 +502,12 @@
         }
 
         @Override
-        public void showToast(String packageName, IBinder token, CharSequence text,
+        public void showToast(int uid, String packageName, IBinder token, CharSequence text,
                 IBinder windowToken, int duration,
                 @Nullable ITransientNotificationCallback callback) {
             if (mBar != null) {
                 try {
-                    mBar.showToast(packageName, token, text, windowToken, duration, callback);
+                    mBar.showToast(uid, packageName, token, text, windowToken, duration, callback);
                 } catch (RemoteException ex) { }
             }
         }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index ddf166e..63952b0 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1904,7 +1904,7 @@
                     final WallpaperData fallback =
                             new WallpaperData(wallpaper.userId, getWallpaperDir(wallpaper.userId),
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
-                    ensureSaneWallpaperData(fallback, DEFAULT_DISPLAY);
+                    ensureSaneWallpaperData(fallback);
                     bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply);
                     mWaitingForUnlock = true;
                 }
@@ -2425,7 +2425,7 @@
         if (cropHint == null) {
             cropHint = new Rect(0, 0, 0, 0);
         } else {
-            if (cropHint.isEmpty()
+            if (cropHint.width() < 0 || cropHint.height() < 0
                     || cropHint.left < 0
                     || cropHint.top < 0) {
                 throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
@@ -3077,7 +3077,7 @@
                     wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
                             WALLPAPER_LOCK_ORIG, WALLPAPER_LOCK_CROP);
                     mLockWallpaperMap.put(userId, wallpaper);
-                    ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
+                    ensureSaneWallpaperData(wallpaper);
                 } else {
                     // sanity fallback: we're in bad shape, but establishing a known
                     // valid system+lock WallpaperData will keep us from dying.
@@ -3085,7 +3085,7 @@
                     wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
                             WALLPAPER, WALLPAPER_CROP);
                     mWallpaperMap.put(userId, wallpaper);
-                    ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
+                    ensureSaneWallpaperData(wallpaper);
                 }
             }
         }
@@ -3196,10 +3196,10 @@
         }
 
         ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
-        ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
+        ensureSaneWallpaperData(wallpaper);
         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
         if (lockWallpaper != null) {
-            ensureSaneWallpaperData(lockWallpaper, DEFAULT_DISPLAY);
+            ensureSaneWallpaperData(lockWallpaper);
         }
     }
 
@@ -3215,15 +3215,11 @@
         }
     }
 
-    private void ensureSaneWallpaperData(WallpaperData wallpaper, int displayId) {
-        final DisplayData size = getDisplayDataOrCreate(displayId);
-
-        if (displayId == DEFAULT_DISPLAY) {
-            // crop, if not previously specified
-            if (wallpaper.cropHint.width() <= 0
-                    || wallpaper.cropHint.height() <= 0) {
-                wallpaper.cropHint.set(0, 0, size.mWidth, size.mHeight);
-            }
+    private void ensureSaneWallpaperData(WallpaperData wallpaper) {
+        // Only overwrite cropHint if the rectangle is invalid.
+        if (wallpaper.cropHint.width() < 0
+                || wallpaper.cropHint.height() < 0) {
+            wallpaper.cropHint.set(0, 0, 0, 0);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a66741c..c47d215 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -42,7 +42,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.activityTypeToString;
 import static android.content.Intent.ACTION_MAIN;
 import static android.content.Intent.CATEGORY_HOME;
@@ -109,7 +108,6 @@
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_UNSET;
-import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
 
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -196,6 +194,7 @@
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
 import static com.android.server.wm.TaskPersister.DEBUG;
 import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -5811,18 +5810,8 @@
     }
 
     @VisibleForTesting
-    boolean shouldAnimate(int transit) {
-        if (task != null && !task.shouldAnimate()) {
-            return false;
-        }
-        final boolean isSplitScreenPrimary =
-                getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-        final boolean allowSplitScreenPrimaryAnimation = transit != TRANSIT_WALLPAPER_OPEN;
-
-        // We animate always if it's not split screen primary, and only some special cases in split
-        // screen primary because it causes issues with stack clipping when we run an un-minimize
-        // animation at the same time.
-        return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
+    boolean shouldAnimate() {
+        return task == null || task.shouldAnimate();
     }
 
     /**
@@ -5918,7 +5907,8 @@
 
     @Override
     void prepareSurfaces() {
-        final boolean show = isVisible() || isAnimating(PARENTS);
+        final boolean show = isVisible() || isAnimatingExcluding(PARENTS,
+                ANIMATION_TYPE_SCREEN_ROTATION);
 
         if (mSurfaceControl != null) {
             if (show && !mLastSurfaceShowing) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index f5eba96..78d2afc 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -20,7 +20,6 @@
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
@@ -685,9 +684,7 @@
             return;
         }
 
-        setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
-                false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
-                false /* creating */);
+        setWindowingMode(windowingMode, false /* creating */);
     }
 
     /**
@@ -709,27 +706,22 @@
      * @param preferredWindowingMode the preferred windowing mode. This may not be honored depending
      *         on the state of things. For example, WINDOWING_MODE_UNDEFINED will resolve to the
      *         previous non-transient mode if this stack is currently in a transient mode.
-     * @param animate Can be used to prevent animation.
-     * @param showRecents Controls whether recents is shown on the other side of a split while
-     *         entering split mode.
-     * @param enteringSplitScreenMode {@code true} if entering split mode.
-     * @param deferEnsuringVisibility Whether visibility updates are deferred. This is set when
-     *         many operations (which can effect visibility) are being performed in bulk.
      * @param creating {@code true} if this is being run during ActivityStack construction.
      */
-    void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents,
-            boolean enteringSplitScreenMode, boolean deferEnsuringVisibility, boolean creating) {
+    void setWindowingMode(int preferredWindowingMode, boolean creating) {
         mWmService.inSurfaceTransaction(() -> setWindowingModeInSurfaceTransaction(
-                preferredWindowingMode, animate, showRecents, enteringSplitScreenMode,
-                deferEnsuringVisibility, creating));
+                preferredWindowingMode, creating));
     }
 
-    private void setWindowingModeInSurfaceTransaction(int preferredWindowingMode, boolean animate,
-            boolean showRecents, boolean enteringSplitScreenMode, boolean deferEnsuringVisibility,
+    private void setWindowingModeInSurfaceTransaction(int preferredWindowingMode,
             boolean creating) {
+        final TaskDisplayArea taskDisplayArea = getDisplayArea();
+        if (taskDisplayArea == null) {
+            Slog.d(TAG, "taskDisplayArea is null, bail early");
+            return;
+        }
         final int currentMode = getWindowingMode();
         final int currentOverrideMode = getRequestedOverrideWindowingMode();
-        final TaskDisplayArea taskDisplayArea = getDisplayArea();
         final Task topTask = getTopMostTask();
         int windowingMode = preferredWindowingMode;
         if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
@@ -753,12 +745,9 @@
 
         final boolean alreadyInSplitScreenMode = taskDisplayArea.isSplitScreenModeActivated();
 
-        // Don't send non-resizeable notifications if the windowing mode changed was a side effect
-        // of us entering split-screen mode.
-        final boolean sendNonResizeableNotification = !enteringSplitScreenMode;
         // Take any required action due to us not supporting the preferred windowing mode.
         if (alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN
-                && sendNonResizeableNotification && isActivityTypeStandardOrUndefined()) {
+                && isActivityTypeStandardOrUndefined()) {
             final boolean preferredSplitScreen =
                     preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                     || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -794,7 +783,7 @@
         if (currentMode == WINDOWING_MODE_PINNED) {
             mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned();
         }
-        if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
+        if (likelyResolvedMode != WINDOWING_MODE_FULLSCREEN
                 && topActivity != null && !topActivity.noDisplay
                 && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) {
             // Inform the user that they are starting an app that may not work correctly in
@@ -806,7 +795,7 @@
 
         mAtmService.deferWindowLayout();
         try {
-            if (!animate && topActivity != null) {
+            if (topActivity != null) {
                 mStackSupervisor.mNoAnimActivities.add(topActivity);
             }
             super.setWindowingMode(windowingMode);
@@ -845,36 +834,11 @@
                         false /*preserveWindows*/, true /*deferResume*/);
             }
         } finally {
-            if (showRecents && !alreadyInSplitScreenMode && isOnHomeDisplay()
-                    && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                // Make sure recents stack exist when creating a dock stack as it normally needs to
-                // be on the other side of the docked stack and we make visibility decisions based
-                // on that.
-                // TODO: This is only here to help out with the case where recents stack doesn't
-                // exist yet. For that case the initial size of the split-screen stack will be the
-                // the one where the home stack is visible since recents isn't visible yet, but the
-                // divider will be off. I think we should just make the initial bounds that of home
-                // so that the divider matches and remove this logic.
-                // TODO: This is currently only called when entering split-screen while in another
-                // task, and from the tests
-                // TODO (b/78247419): Fix the rotation animation from fullscreen to minimized mode
-                final boolean isRecentsComponentHome =
-                        mAtmService.getRecentTasks().isRecentsComponentHomeActivity(mCurrentUser);
-                final ActivityStack recentStack = taskDisplayArea.getOrCreateStack(
-                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
-                        isRecentsComponentHome ? ACTIVITY_TYPE_HOME : ACTIVITY_TYPE_RECENTS,
-                        true /* onTop */);
-                recentStack.moveToFront("setWindowingMode");
-                // If task moved to docked stack - show recents if needed.
-                mWmService.showRecentApps();
-            }
             mAtmService.continueWindowLayout();
         }
 
-        if (!deferEnsuringVisibility) {
-            mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
-            mRootWindowContainer.resumeFocusedStacksTopActivities();
-        }
+        mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+        mRootWindowContainer.resumeFocusedStacksTopActivities();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 02601ff..0470ffa 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -80,8 +80,6 @@
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
 import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
-import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
-import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -140,7 +138,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.os.TransferPipe;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -382,71 +379,6 @@
 
     private boolean mInitialized;
 
-    private final MoveTaskToFullscreenHelper mMoveTaskToFullscreenHelper =
-            new MoveTaskToFullscreenHelper();
-    private class MoveTaskToFullscreenHelper {
-        private TaskDisplayArea mToDisplayArea;
-        private boolean mOnTop;
-        private Task mTopTask;
-        private boolean mSchedulePictureInPictureModeChange;
-
-        void process(ActivityStack fromStack, TaskDisplayArea toDisplayArea, boolean onTop,
-                boolean schedulePictureInPictureModeChange) {
-            mSchedulePictureInPictureModeChange = schedulePictureInPictureModeChange;
-            mToDisplayArea = toDisplayArea;
-            mOnTop = onTop;
-            mTopTask = fromStack.getTopMostTask();
-
-            final PooledConsumer c = PooledLambda.obtainConsumer(
-                    MoveTaskToFullscreenHelper::processLeafTask, this, PooledLambda.__(Task.class));
-            fromStack.forAllLeafTasks(c, false /* traverseTopToBottom */);
-            c.recycle();
-            mToDisplayArea = null;
-            mTopTask = null;
-        }
-
-        private void processLeafTask(Task task) {
-            // This is a one level task that we don't need to create stack for reparenting to.
-            if (task.isRootTask() && DisplayContent.alwaysCreateStack(WINDOWING_MODE_FULLSCREEN,
-                    task.getActivityType())) {
-                final ActivityStack stack = (ActivityStack) task;
-                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                if (mToDisplayArea.getDisplayId() != stack.getDisplayId()) {
-                    stack.reparent(mToDisplayArea, mOnTop);
-                } else if (mOnTop) {
-                    mToDisplayArea.positionStackAtTop(stack, false /* includingParents */);
-                } else {
-                    mToDisplayArea.positionStackAtBottom(stack);
-                }
-                return;
-            }
-
-            final ActivityStack toStack = mToDisplayArea.getOrCreateStack(null, mTmpOptions, task,
-                    task.getActivityType(), mOnTop);
-            if (task == toStack) {
-                // The task was reused as the root task.
-                return;
-            }
-
-            if (mOnTop) {
-                final boolean isTopTask = task == mTopTask;
-                // Defer resume until all the tasks have been moved to the fullscreen stack
-                task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, isTopTask /*animate*/,
-                        DEFER_RESUME, mSchedulePictureInPictureModeChange,
-                        "moveTasksToFullscreenStack - onTop");
-                MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext,
-                        task.effectiveUid, task.realActivity.flattenToString());
-            } else {
-                // Position the tasks in the fullscreen stack in order at the bottom of the
-                // stack. Also defer resume until all the tasks have been moved to the
-                // fullscreen stack.
-                task.reparent(toStack, ON_TOP, REPARENT_LEAVE_STACK_IN_PLACE,
-                        !ANIMATE, DEFER_RESUME, mSchedulePictureInPictureModeChange,
-                        "moveTasksToFullscreenStack - NOT_onTop");
-            }
-        }
-    }
-
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
@@ -1501,56 +1433,6 @@
         mResizingTasksDuringAnimation.clear();
     }
 
-    /**
-     * TODO: This should just change the windowing mode and resize vs. actually moving task around.
-     * Can do that once we are no longer using static stack ids.
-     */
-    private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
-            int toDisplayId, boolean onTop) {
-
-        mService.deferWindowLayout();
-        try {
-            final int windowingMode = fromStack.getWindowingMode();
-            final TaskDisplayArea toDisplayArea = mRootWindowContainer
-                    .getDisplayContent(toDisplayId).getDefaultTaskDisplayArea();
-
-            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                // We are moving all tasks from the docked stack to the fullscreen stack,
-                // which is dismissing the docked stack, so resize all other stacks to
-                // fullscreen here already so we don't end up with resize trashing.
-                for (int i = toDisplayArea.getStackCount() - 1; i >= 0; --i) {
-                    final ActivityStack otherStack = toDisplayArea.getStackAt(i);
-                    if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
-                        continue;
-                    }
-                    otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-                }
-            }
-
-            // If we are moving from the pinned stack, then the animation takes care of updating
-            // the picture-in-picture mode.
-            final boolean schedulePictureInPictureModeChange =
-                    windowingMode == WINDOWING_MODE_PINNED;
-
-            if (fromStack.hasChild()) {
-                mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                mMoveTaskToFullscreenHelper.process(
-                        fromStack, toDisplayArea, onTop, schedulePictureInPictureModeChange);
-            }
-
-            mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
-            mRootWindowContainer.resumeFocusedStacksTopActivities();
-        } finally {
-            mService.continueWindowLayout();
-        }
-    }
-
-    void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) {
-        // TODO(b/153089193): Support moving within the same task display area
-        mWindowManager.inSurfaceTransaction(() ->
-                moveTasksToFullscreenStackInSurfaceTransaction(fromStack, DEFAULT_DISPLAY, onTop));
-    }
-
     void setSplitScreenResizing(boolean resizing) {
         if (resizing == mDockedStackResizing) {
             return;
@@ -1560,26 +1442,46 @@
         mWindowManager.setDockedStackResizing(resizing);
     }
 
+    private void removePinnedStackInSurfaceTransaction(ActivityStack stack) {
+        /**
+         * Workaround: Force-stop all the activities in the pinned stack before we reparent them
+         * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
+         * that the client receives onStop() before it is reparented.  We do this by detaching
+         * the stack from the display so that it will be considered invisible when
+         * ensureActivitiesVisible() is called, and all of its activities will be marked
+         * invisible as well and added to the stopping list.  After which we process the
+         * stopping list by handling the idle.
+         */
+        stack.cancelAnimation();
+        stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
+        stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+        stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
+        activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
+                true /* processPausingActivities */, null /* configuration */);
+
+        // Reparent all the tasks to the bottom of the display
+        final DisplayContent toDisplay =
+                mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
+
+        mService.deferWindowLayout();
+        try {
+            stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+            if (toDisplay.getDisplayId() != stack.getDisplayId()) {
+                stack.reparent(toDisplay.getDefaultTaskDisplayArea(), false /* onTop */);
+            } else {
+                toDisplay.getDefaultTaskDisplayArea().positionStackAtBottom(stack);
+            }
+
+            mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            mRootWindowContainer.resumeFocusedStacksTopActivities();
+        } finally {
+            mService.continueWindowLayout();
+        }
+    }
+
     private void removeStackInSurfaceTransaction(ActivityStack stack) {
         if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
-            /**
-             * Workaround: Force-stop all the activities in the pinned stack before we reparent them
-             * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
-             * that the client receives onStop() before it is reparented.  We do this by detaching
-             * the stack from the display so that it will be considered invisible when
-             * ensureActivitiesVisible() is called, and all of its activities will be marked
-             * invisible as well and added to the stopping list.  After which we process the
-             * stopping list by handling the idle.
-             */
-            stack.cancelAnimation();
-            stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
-            stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
-            stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
-            activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
-                    true /* processPausingActivities */, null /* configuration */);
-
-            // Move all the tasks to the bottom of the fullscreen stack
-            moveTasksToFullscreenStackLocked(stack, !ON_TOP);
+            removePinnedStackInSurfaceTransaction(stack);
         } else {
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     ActivityStackSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0b19687..c253cd2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3324,33 +3324,12 @@
                     Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
                     return;
                 }
-                // Place the task in the right stack if it isn't there already based on
-                // the requested bounds.
-                // The stack transition logic is:
-                // - a null bounds on a freeform task moves that task to fullscreen
-                // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
-                //   that task to freeform
-                // - otherwise the task is not moved
-                ActivityStack stack = task.getStack();
                 if (!task.getWindowConfiguration().canResizeTask()) {
                     throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
                 }
-                if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
-                    stack = stack.getDisplayArea().getOrCreateStack(
-                            WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
-                } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
-                    stack = stack.getDisplayArea().getOrCreateStack(
-                            WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
-                }
 
                 // Reparent the task to the right stack if necessary
                 boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
-                if (stack != task.getStack()) {
-                    // Defer resume until the task is resized below
-                    task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
-                            DEFER_RESUME, "resizeTask");
-                    preserveWindow = false;
-                }
 
                 // After reparenting (which only resizes the task to the stack bounds), resize the
                 // task to the actual bounds provided
@@ -4022,28 +4001,6 @@
         }
     }
 
-    @Override
-    // TODO: API should just be about changing windowing modes...
-    public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "moveTasksToFullscreenStack()");
-        synchronized (mGlobalLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                final ActivityStack stack = mRootWindowContainer.getStack(fromStackId);
-                if (stack != null){
-                    if (!stack.isActivityTypeStandardOrUndefined()) {
-                        throw new IllegalArgumentException(
-                                "You can't move tasks from non-standard stacks.");
-                    }
-                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
     /**
      * Moves the top activity in the input stackId to the pinned stack.
      *
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index f86aeb2..78ee1de7 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -76,7 +76,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
 
 import android.annotation.DrawableRes;
@@ -1807,15 +1806,11 @@
     }
 
     int getAppStackClipMode() {
-        // When dismiss keyguard animation occurs, clip before the animation to prevent docked
-        // app from showing beyond the divider
-        if (mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
-                || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
-            return STACK_CLIP_BEFORE_ANIM;
-        }
         return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
                 || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
                 || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
+                || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
+                || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
                 ? STACK_CLIP_NONE
                 : STACK_CLIP_AFTER_ANIM;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 90fdf19..a512337 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -54,7 +54,6 @@
  * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
  * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
  * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
- *        Cannot have a sibling that is of type ANY.
  *
  * @param <T> type of the children of the DisplayArea.
  */
@@ -253,6 +252,12 @@
             super.prepareSurfaces();
             getBounds(mTmpDimBoundsRect);
 
+            // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer
+            // on the display level fades out.
+            if (forAllTasks(task -> !task.canAffectSystemUiFlags())) {
+                mDimmer.resetDimStates();
+            }
+
             if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
                 scheduleAnimation();
             }
@@ -268,7 +273,6 @@
         ANY;
 
         static void checkSiblings(Type bottom, Type top) {
-            checkState(!(bottom == ANY && top == ANY), "ANY cannot be a sibling of ANY");
             checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS),
                     bottom + " must be above BELOW_TASKS");
             checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS),
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 9821573..15b6f973 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -16,9 +16,14 @@
 
 package com.android.server.wm;
 
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+
 import android.content.res.Resources;
 import android.text.TextUtils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Policy that manages DisplayAreas.
  */
@@ -37,9 +42,9 @@
     protected final DisplayArea<? extends WindowContainer> mImeContainer;
 
     /**
-     * The Tasks container. Tasks etc. are automatically added to this container.
+     * The task display areas. Tasks etc. are automatically added to these containers.
      */
-    protected final DisplayArea<? extends ActivityStack> mTaskContainers;
+    protected final List<TaskDisplayArea> mTaskDisplayAreas;
 
     /**
      * Construct a new {@link DisplayAreaPolicy}
@@ -48,19 +53,19 @@
      * @param content the display content for which the policy applies
      * @param root the root display area under which the policy operates
      * @param imeContainer the ime container that the policy must attach
-     * @param taskDisplayArea the task container that the policy must attach
+     * @param taskDisplayAreas the task display areas that the policy must attach
      *
      * @see #attachDisplayAreas()
      */
     protected DisplayAreaPolicy(WindowManagerService wmService,
             DisplayContent content, DisplayArea.Root root,
             DisplayArea<? extends WindowContainer> imeContainer,
-            DisplayArea<? extends ActivityStack> taskDisplayArea) {
+            List<TaskDisplayArea> taskDisplayAreas) {
         mWmService = wmService;
         mContent = content;
         mRoot = root;
         mImeContainer = imeContainer;
-        mTaskContainers = taskDisplayArea;
+        mTaskDisplayAreas = taskDisplayAreas;
     }
 
     /**
@@ -80,15 +85,32 @@
      */
     public abstract void addWindow(WindowToken token);
 
+    /**
+     * @return the number of task display areas on the display.
+     */
+    public int getTaskDisplayAreaCount() {
+        return mTaskDisplayAreas.size();
+    }
+
+    /**
+     * @return the task display area at index.
+     */
+    public TaskDisplayArea getTaskDisplayAreaAt(int index) {
+        return mTaskDisplayAreas.get(index);
+    }
+
     /** Provider for platform-default display area policy. */
     static final class DefaultProvider implements DisplayAreaPolicy.Provider {
         @Override
         public DisplayAreaPolicy instantiate(WindowManagerService wmService,
                 DisplayContent content, DisplayArea.Root root,
-                DisplayArea<? extends WindowContainer> imeContainer,
-                TaskDisplayArea taskDisplayArea) {
+                DisplayArea<? extends WindowContainer> imeContainer) {
+            final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
+                    "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
+            final List<TaskDisplayArea> tdaList = new ArrayList<>();
+            tdaList.add(defaultTaskDisplayArea);
             return new DisplayAreaPolicyBuilder()
-                    .build(wmService, content, root, imeContainer, taskDisplayArea);
+                    .build(wmService, content, root, imeContainer, tdaList);
         }
     }
 
@@ -107,8 +129,7 @@
          */
         DisplayAreaPolicy instantiate(WindowManagerService wmService,
                 DisplayContent content, DisplayArea.Root root,
-                DisplayArea<? extends WindowContainer> imeContainer,
-                TaskDisplayArea taskDisplayArea);
+                DisplayArea<? extends WindowContainer> imeContainer);
 
         /**
          * Instantiate the device-specific {@link Provider}.
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index e8becfa..2c2f433 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -201,8 +201,8 @@
 
         Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root,
                 DisplayArea<? extends WindowContainer> imeContainer,
-                DisplayArea<? extends ActivityStack> taskDisplayArea, ArrayList<Feature> features) {
-            super(wmService, content, root, imeContainer, taskDisplayArea);
+                List<TaskDisplayArea> taskDisplayAreas, ArrayList<Feature> features) {
+            super(wmService, content, root, imeContainer, taskDisplayAreas);
             mFeatures = features;
             mAreas = new HashMap<>(features.size());
             for (int i = 0; i < mFeatures.size(); i++) {
@@ -267,7 +267,7 @@
                     areaForLayer[layer].mChildren.add(leafArea);
                     leafType = type;
                     if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
-                        leafArea.mExisting = mTaskContainers;
+                        addTaskDisplayAreasToLayer(areaForLayer[layer], layer);
                     } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
                         leafArea.mExisting = mImeContainer;
                     }
@@ -278,6 +278,17 @@
             root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas);
         }
 
+        /** Adds all task display areas to the specified layer */
+        private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea, int layer) {
+            final int count = mTaskDisplayAreas.size();
+            for (int i = 0; i < count; i++) {
+                PendingArea leafArea = new PendingArea(null, layer, parentPendingArea);
+                leafArea.mExisting = mTaskDisplayAreas.get(i);
+                leafArea.mMaxLayer = layer;
+                parentPendingArea.mChildren.add(leafArea);
+            }
+        }
+
         @Override
         public void addWindow(WindowToken token) {
             DisplayArea.Tokens area = findAreaForToken(token);
@@ -317,12 +328,16 @@
         return this;
     }
 
+    protected List<Feature> getFeatures() {
+        return mFeatures;
+    }
+
     Result build(WindowManagerService wmService,
             DisplayContent content, DisplayArea.Root root,
             DisplayArea<? extends WindowContainer> imeContainer,
-            DisplayArea<? extends ActivityStack> taskDisplayArea) {
+            List<TaskDisplayArea> taskDisplayAreas) {
 
-        return new Result(wmService, content, root, imeContainer, taskDisplayArea, new ArrayList<>(
+        return new Result(wmService, content, root, imeContainer, taskDisplayAreas, new ArrayList<>(
                 mFeatures));
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7931cd5..40243e8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -72,6 +72,7 @@
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
 
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
@@ -197,7 +198,6 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.window.ITaskOrganizer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -269,8 +269,6 @@
             new NonAppWindowContainers("mOverlayContainers", mWmService);
 
     /** The containers below are the only child containers {@link #mWindowContainers} can have. */
-    // Contains all window containers that are related to apps (Activities)
-    final TaskDisplayArea mTaskContainers = new TaskDisplayArea(this, mWmService);
 
     // Contains all IME window containers. Note that the z-ordering of the IME windows will depend
     // on the IME target. We mainly have this container grouping so we can keep track of all the IME
@@ -972,7 +970,7 @@
         super.addChild(mOverlayContainers, null);
 
         mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate(
-                mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
+                mWmService, this, mRootDisplayArea, mImeWindowsContainers);
         mWindowContainers.addChildren();
 
         // Sets the display content for the children.
@@ -1082,8 +1080,7 @@
             return null;
         }
         mShellRoots.put(windowType, root);
-        SurfaceControl out = new SurfaceControl();
-        out.copyFrom(rootLeash);
+        SurfaceControl out = new SurfaceControl(rootLeash);
         return out;
     }
 
@@ -2071,13 +2068,11 @@
     }
 
     protected int getTaskDisplayAreaCount() {
-        // TODO(multi-display-area): Report actual display area count
-        return 1;
+        return mDisplayAreaPolicy.getTaskDisplayAreaCount();
     }
 
     protected TaskDisplayArea getTaskDisplayAreaAt(int index) {
-        // TODO(multi-display-area): Report actual display area values
-        return mTaskContainers;
+        return mDisplayAreaPolicy.getTaskDisplayAreaAt(index);
     }
 
     ActivityStack getStack(int rootTaskId) {
@@ -2403,7 +2398,7 @@
      * or for cases when multi-instance is not supported yet (like Split-screen, PiP or Recents).
      */
     TaskDisplayArea getDefaultTaskDisplayArea() {
-        return mTaskContainers;
+        return mDisplayAreaPolicy.getTaskDisplayAreaAt(0);
     }
 
     @Override
@@ -3426,7 +3421,10 @@
 
     private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) {
         // Always update control target. This is needed to handle rotation.
-        updateImeControlTarget(target);
+        // We cannot set target as the control target, because mInputMethodTarget can only help
+        // decide the z-order of IME, but cannot control IME. Only the IME target reported from
+        // updateInputMethodTargetWindow can control IME.
+        updateImeControlTarget(mInputMethodControlTarget);
         if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) {
             return;
         }
@@ -3554,6 +3552,10 @@
                         onWallpaper, goingToShade, subtle));
             }
         }, true /* traverseTopToBottom */);
+        for (int i = mShellRoots.size() - 1; i >= 0; --i) {
+            mShellRoots.valueAt(i).startAnimation(policy.createHiddenByKeyguardExit(
+                    onWallpaper, goingToShade, subtle));
+        }
     }
 
     /** @return {@code true} if there is window to wait before enabling the screen. */
@@ -5433,6 +5435,10 @@
         return mDisplayPolicy.getSystemUiContext();
     }
 
+    Point getDisplayPosition() {
+        return mWmService.mDisplayManagerInternal.getDisplayPosition(getDisplayId());
+    }
+
     class RemoteInsetsControlTarget implements InsetsControlTarget {
         private final IDisplayWindowInsetsController mRemoteInsetsController;
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index caaa173..e9d3d56 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -696,6 +696,10 @@
         mForceShowSystemBarsFromExternal = forceShowSystemBars;
     }
 
+    boolean getForceShowSystemBars() {
+        return mForceShowSystemBarsFromExternal;
+    }
+
     public boolean hasNavigationBar() {
         return mHasNavigationBar;
     }
@@ -1494,9 +1498,7 @@
         final int behavior = mLastBehavior;
         boolean navVisible = ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL
                 ? (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
-                : mNavigationBar != null && mNavigationBar.getControllableInsetProvider() != null
-                        && mNavigationBar.getControllableInsetProvider().isClientVisible()
-                        && !mDisplayContent.getInsetsPolicy().isTransient(ITYPE_NAVIGATION_BAR);
+                : isNavigationBarRequestedVisible();
         boolean navTranslucent = (sysui
                 & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
         boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0
@@ -1533,6 +1535,14 @@
         mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation;
     }
 
+    boolean isNavigationBarRequestedVisible() {
+        final InsetsSourceProvider provider =
+                mDisplayContent.getInsetsStateController().peekSourceProvider(ITYPE_NAVIGATION_BAR);
+        return provider == null
+                ? InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR)
+                : provider.isClientVisible();
+    }
+
     void updateHideNavInputEventReceiver(boolean navVisible, boolean navAllowedHidden) {
         // When the navigation bar isn't visible, we put up a fake input window to catch all
         // touch events. This way we can detect when the user presses anywhere to bring back the
@@ -3879,10 +3889,10 @@
                 WindowManager.LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.MATCH_PARENT);
         lp.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
-        lp.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN
-                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+        lp.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+        lp.setFitInsetsTypes(0);
         lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
         if (ActivityManager.isHighEndGfx()) {
             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index e4e5716..61a199a 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -99,7 +99,7 @@
     }
 
     private void updateHideNavInputEventReceiver() {
-        mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR),
+        mPolicy.updateHideNavInputEventReceiver(mPolicy.isNavigationBarRequestedVisible(),
                 mFocusedWin != null
                         && mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH);
     }
@@ -304,7 +304,10 @@
         // We need to force system bars when the docked stack is visible, when the freeform stack
         // is visible but also when we are resizing for the transitions when docked stack
         // visibility changes.
-        return isDockedStackVisible || isFreeformStackVisible || isResizing;
+        return isDockedStackVisible
+                || isFreeformStackVisible
+                || isResizing
+                || mPolicy.getForceShowSystemBars();
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e1ef76f..80b8b58 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -178,7 +178,7 @@
 
     private Object mLastWindowFreezeSource = null;
     private Session mHoldScreen = null;
-    private float mScreenBrightness = -1;
+    private float mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
     private long mUserActivityTimeout = -1;
     private boolean mUpdateRotation = false;
     // Following variables are for debugging screen wakelock only.
@@ -826,7 +826,7 @@
         }
 
         mHoldScreen = null;
-        mScreenBrightness = -1;
+        mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mUserActivityTimeout = -1;
         mObscureApplicationContentOnSecondaryDisplays = false;
         mSustainedPerformanceModeCurrent = false;
@@ -936,12 +936,14 @@
 
         mWmService.setHoldScreenLocked(mHoldScreen);
         if (!mWmService.mDisplayFrozen) {
-            final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f
-                    ? -1 : toBrightnessOverride(mScreenBrightness);
-
+            final float brightnessOverride = mScreenBrightnessOverride < PowerManager.BRIGHTNESS_MIN
+                    || mScreenBrightnessOverride > PowerManager.BRIGHTNESS_MAX
+                    ? PowerManager.BRIGHTNESS_INVALID_FLOAT : mScreenBrightnessOverride;
+            int brightnessFloatAsIntBits = Float.floatToIntBits(brightnessOverride);
             // Post these on a handler such that we don't call into power manager service while
             // holding the window manager lock to avoid lock contention with power manager lock.
-            mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightness, 0).sendToTarget();
+            mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightnessFloatAsIntBits,
+                    0).sendToTarget();
             mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
         }
 
@@ -1125,8 +1127,9 @@
                                 + "has FLAG_KEEP_SCREEN_ON!!! called by%s",
                         w, Debug.getCallers(10));
             }
-            if (!syswin && w.mAttrs.screenBrightness >= 0 && mScreenBrightness < 0) {
-                mScreenBrightness = w.mAttrs.screenBrightness;
+            if (!syswin && w.mAttrs.screenBrightness >= 0
+                    && Float.isNaN(mScreenBrightnessOverride)) {
+                mScreenBrightnessOverride = w.mAttrs.screenBrightness;
             }
 
             final int type = attrs.type;
@@ -1190,10 +1193,6 @@
         return doRequest;
     }
 
-    private static int toBrightnessOverride(float value) {
-        return (int)(value * PowerManager.BRIGHTNESS_ON);
-    }
-
     private final class MyHandler extends Handler {
 
         public MyHandler(Looper looper) {
@@ -1205,7 +1204,7 @@
             switch (msg.what) {
                 case SET_SCREEN_BRIGHTNESS_OVERRIDE:
                     mWmService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
-                            msg.arg1);
+                            Float.intBitsToFloat(msg.arg1));
                     break;
                 case SET_USER_ACTIVITY_TIMEOUT:
                     mWmService.mPowerManagerInternal.
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 5f3732a..7d8a56e 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -51,6 +51,7 @@
 import android.view.IWindowSession;
 import android.view.IWindowSessionCallback;
 import android.view.InputChannel;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -158,10 +159,10 @@
             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
             Rect outStableInsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState) {
+            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                 outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
-                outInsetsState);
+                outInsetsState, outActiveControls);
     }
 
     @Override
@@ -171,7 +172,7 @@
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                 new Rect() /* outFrame */, outContentInsets, outStableInsets,
                 new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
-                outInsetsState);
+                outInsetsState, null);
     }
 
     @Override
@@ -191,7 +192,8 @@
             Rect outStableInsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-            Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
+            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
+            SurfaceControl outBLASTSurfaceControl) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -199,8 +201,8 @@
                 requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                 outFrame, outContentInsets, outVisibleInsets,
                 outStableInsets, outBackdropFrame, cutout,
-                mergedConfiguration, outSurfaceControl, outInsetsState, outSurfaceSize,
-                outBLASTSurfaceControl);
+                mergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls,
+                outSurfaceSize, outBLASTSurfaceControl);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 9732637..701feff 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -16,12 +16,20 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
+
 import android.annotation.NonNull;
+import android.graphics.Point;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.view.DisplayInfo;
 import android.view.IWindow;
 import android.view.SurfaceControl;
+import android.view.animation.Animation;
 
 /**
  * Represents a piece of the hierarchy under which a client Shell can manage sub-windows.
@@ -70,5 +78,29 @@
     IWindow getClient() {
         return mClient;
     }
+
+    void startAnimation(Animation anim) {
+        // Only do this for the divider
+        if (mToken.windowType != TYPE_DOCK_DIVIDER) {
+            return;
+        }
+
+        DisplayInfo displayInfo = mToken.getFixedRotationTransformDisplayInfo();
+        if (displayInfo == null) {
+            displayInfo = mDisplayContent.getDisplayInfo();
+        }
+
+        // Mostly copied from WindowState to enable keyguard transition animation
+        anim.initialize(displayInfo.logicalWidth, displayInfo.logicalHeight,
+                displayInfo.appWidth, displayInfo.appHeight);
+        anim.restrictDuration(MAX_ANIMATION_DURATION);
+        anim.scaleCurrentDuration(mDisplayContent.mWmService.getWindowAnimationScaleLocked());
+        final AnimationAdapter adapter = new LocalAnimationAdapter(
+                new WindowAnimationSpec(anim, new Point(0, 0), false /* canSkipFirstFrame */,
+                        0 /* windowCornerRadius */),
+                mDisplayContent.mWmService.mSurfaceAnimationRunner);
+        mToken.startAnimation(mToken.getPendingTransaction(), adapter, false /* hidden */,
+                ANIMATION_TYPE_WINDOW_ANIMATION, null /* animationFinishedCallback */);
+    }
 }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 6358e47..18e32c0 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -202,6 +202,11 @@
         return mAnimation != null;
     }
 
+    @AnimationType
+    int getAnimationType() {
+        return mAnimationType;
+    }
+
     /**
      * @return The current animation spec if we are running an animation, or {@code null} otherwise.
      */
@@ -453,32 +458,38 @@
      * Animation for screen rotation.
      * @hide
      */
-    static final int ANIMATION_TYPE_SCREEN_ROTATION = 2;
+    static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1;
 
     /**
      * Animation for dimming.
      * @hide
      */
-    static final int ANIMATION_TYPE_DIMMER = 3;
+    static final int ANIMATION_TYPE_DIMMER = 1 << 2;
 
     /**
      * Animation for recent apps.
      * @hide
      */
-    static final int ANIMATION_TYPE_RECENTS = 4;
+    static final int ANIMATION_TYPE_RECENTS = 1 << 3;
 
     /**
      * Animation for a {@link WindowState} without animating the activity.
      * @hide
      */
-    static final int ANIMATION_TYPE_WINDOW_ANIMATION = 5;
+    static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4;
 
     /**
      * Animation to control insets. This is actually not an animation, but is used to give the
      * client a leash over the system window causing insets.
      * @hide
      */
-    static final int ANIMATION_TYPE_INSETS_CONTROL = 6;
+    static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
+
+    /**
+     * Bitmask to include all animation types. This is NOT an {@link AnimationType}
+     * @hide
+     */
+    static final int ANIMATION_TYPE_ALL = -1;
 
     /**
      * The type of the animation.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c891c11..d31939d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2448,7 +2448,8 @@
     Rect updateOverrideConfigurationFromLaunchBounds() {
         // If the task is controlled by another organized task, do not set override
         // configurations and let its parent (organized task) to control it;
-        final Rect bounds = isOrganized() && !isRootTask() ? null : getLaunchBounds();
+        final Task rootTask = getRootTask();
+        final Rect bounds = rootTask != this && rootTask.isOrganized() ? null : getLaunchBounds();
         setBounds(bounds);
         if (bounds != null && !bounds.isEmpty()) {
             // TODO: Review if we actually want to do this - we are setting the launch bounds
@@ -3347,6 +3348,21 @@
 
     @Override
     Dimmer getDimmer() {
+        // If the window is in multi-window mode, we want to dim at the Task level to ensure the dim
+        // bounds match the area the app lives in
+        if (inMultiWindowMode()) {
+            return mDimmer;
+        }
+
+        // If we're not at the root task level, we want to keep traversing through the parents to
+        // find the root.
+        // Once at the root task level, we want to check {@link #isTranslucent(ActivityRecord)}.
+        // If true, we want to get the Dimmer from the level above since we don't want to animate
+        // the dim with the Task.
+        if (!isRootTask() || isTranslucent(null)) {
+            return super.getDimmer();
+        }
+
         return mDimmer;
     }
 
@@ -4105,20 +4121,18 @@
      * Any time any of these conditions are updated, the updating code should call
      * sendTaskAppeared.
      */
-    private boolean taskAppearedReady() {
+    boolean taskAppearedReady() {
         return mSurfaceControl != null && mTaskOrganizer != null && getHasBeenVisible();
     }
 
     private void sendTaskAppeared() {
-        if (taskAppearedReady() && !mTaskAppearedSent) {
-            mTaskAppearedSent = true;
+        if (mTaskOrganizer != null) {
             mAtmService.mTaskOrganizerController.onTaskAppeared(mTaskOrganizer, this);
         }
     }
 
     private void sendTaskVanished() {
-        if (mTaskOrganizer != null && mTaskAppearedSent) {
-            mTaskAppearedSent = false;
+        if (mTaskOrganizer != null) {
             mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this);
         }
    }
@@ -4131,17 +4145,6 @@
         // Let the old organizer know it has lost control.
         sendTaskVanished();
         mTaskOrganizer = organizer;
-
-        // If the task is not yet visible when it is added to the task organizer, then we should
-        // hide it to allow the task organizer to show it when it is properly reparented. We skip
-        // this for tasks created by the organizer because they can synchronously update the leash
-        // before new children are added to the task.
-        if (!mCreatedByOrganizer && organizer != null
-                && (!getHasBeenVisible() || !hasVisibleChildren())) {
-            getPendingTransaction().hide(getSurfaceControl());
-            commitPendingTransaction();
-        }
-
         sendTaskAppeared();
         onTaskOrganizerChanged();
         return true;
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 8e45dc3..11cfad2 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -31,7 +31,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.window.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER;
 
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
@@ -141,8 +140,9 @@
      */
     private boolean mRemoved;
 
-    TaskDisplayArea(DisplayContent displayContent, WindowManagerService service) {
-        super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
+    TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name,
+            int displayAreaFeature) {
+        super(service, Type.ANY, name, displayAreaFeature);
         mDisplayContent = displayContent;
         mRootWindowContainer = service.mRoot;
         mAtmService = service.mAtmService;
@@ -762,7 +762,11 @@
      */
     ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
             Intent intent, Task candidateTask) {
-        if (!alwaysCreateStack(windowingMode, activityType)) {
+        // Need to pass in a determined windowing mode to see if a new stack should be created,
+        // so use its parent's windowing mode if it is undefined.
+        if (!alwaysCreateStack(
+                windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : getWindowingMode(),
+                activityType)) {
             ActivityStack stack = getStack(windowingMode, activityType);
             if (stack != null) {
                 return stack;
@@ -932,9 +936,7 @@
             }
         } else {
             addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
-            stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
-                    false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
-                    true /* creating */);
+            stack.setWindowingMode(windowingMode, true /* creating */);
         }
         return stack;
     }
@@ -1144,7 +1146,7 @@
             for (int i = getStackCount() - 1; i >= 0; --i) {
                 final ActivityStack stack = getStackAt(i);
                 // Collect the root tasks that are currently being organized.
-                if (stack.isOrganized()) {
+                if (stack.mCreatedByOrganizer) {
                     for (int k = stack.getChildCount() - 1; k >= 0; --k) {
                         final ActivityStack childStack = (ActivityStack) stack.getChildAt(k);
                         if (childStack.getActivityType() == activityType) {
@@ -1610,6 +1612,11 @@
         }
     }
 
+    @Override
+    boolean canCreateRemoteAnimationTarget() {
+        return true;
+    }
+
     /**
      * Callback for when the order of the stacks in the display changes.
      */
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 75f2e1f..22702dd 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -25,7 +25,6 @@
 import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
 
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.WindowConfiguration;
 import android.content.Intent;
@@ -39,7 +38,6 @@
 import android.window.ITaskOrganizerController;
 import android.window.WindowContainerToken;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 
 import java.io.PrintWriter;
@@ -48,7 +46,6 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.WeakHashMap;
-import java.util.function.Consumer;
 
 /**
  * Stores the TaskOrganizers associated with a given windowing mode and
@@ -84,95 +81,17 @@
                 }
             }
         }
-    }
-
-    /**
-     * A wrapper class around ITaskOrganizer to ensure that the calls are made in the right
-     * lifecycle order since we may be updating the visibility of task surface controls in a pending
-     * transaction before they are presented to the task org.
-     */
-    private class TaskOrganizerCallbacks {
-        final WindowManagerService mService;
-        final ITaskOrganizer mTaskOrganizer;
-        final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
-
-        TaskOrganizerCallbacks(WindowManagerService wm, ITaskOrganizer taskOrg,
-                Consumer<Runnable> deferTaskOrgCallbacksConsumer) {
-            mService = wm;
-            mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer;
-            mTaskOrganizer = taskOrg;
-        }
-
-        IBinder getBinder() {
-            return mTaskOrganizer.asBinder();
-        }
-
-        void onTaskAppeared(Task task) {
-            final RunningTaskInfo taskInfo = task.getTaskInfo();
-            mDeferTaskOrgCallbacksConsumer.accept(() -> {
-                try {
-                    mTaskOrganizer.onTaskAppeared(taskInfo);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Exception sending onTaskAppeared callback", e);
-                }
-            });
-        }
-
-
-        void onTaskVanished(Task task) {
-            final RunningTaskInfo taskInfo = task.getTaskInfo();
-            mDeferTaskOrgCallbacksConsumer.accept(() -> {
-                try {
-                    mTaskOrganizer.onTaskVanished(taskInfo);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Exception sending onTaskVanished callback", e);
-                }
-            });
-        }
-
-        void onTaskInfoChanged(Task task, ActivityManager.RunningTaskInfo taskInfo) {
-            mDeferTaskOrgCallbacksConsumer.accept(() -> {
-                if (!task.isOrganized()) {
-                    // This is safe to ignore if the task is no longer organized
-                    return;
-                }
-                try {
-                    mTaskOrganizer.onTaskInfoChanged(taskInfo);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e);
-                }
-            });
-        }
-
-        void onBackPressedOnTaskRoot(Task task) {
-            mDeferTaskOrgCallbacksConsumer.accept(() -> {
-                if (!task.isOrganized()) {
-                    // This is safe to ignore if the task is no longer organized
-                    return;
-                }
-                try {
-                   mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo());
-                } catch (Exception e) {
-                    Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e);
-                }
-            });
-        }
-    }
+    };
 
     private class TaskOrganizerState {
-        private final TaskOrganizerCallbacks mOrganizer;
+        private final ITaskOrganizer mOrganizer;
         private final DeathRecipient mDeathRecipient;
         private final ArrayList<Task> mOrganizedTasks = new ArrayList<>();
         private final int mUid;
         private boolean mInterceptBackPressedOnTaskRoot;
 
         TaskOrganizerState(ITaskOrganizer organizer, int uid) {
-            final Consumer<Runnable> deferTaskOrgCallbacksConsumer =
-                    mDeferTaskOrgCallbacksConsumer != null
-                            ? mDeferTaskOrgCallbacksConsumer
-                            : mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable;
-            mOrganizer = new TaskOrganizerCallbacks(mService.mWindowManager, organizer,
-                    deferTaskOrgCallbacksConsumer);
+            mOrganizer = organizer;
             mDeathRecipient = new DeathRecipient(organizer);
             try {
                 organizer.asBinder().linkToDeath(mDeathRecipient, 0);
@@ -187,19 +106,37 @@
         }
 
         void addTask(Task t) {
-            mOrganizedTasks.add(t);
-            mOrganizer.onTaskAppeared(t);
+            if (t.mTaskAppearedSent) return;
+
+            if (!mOrganizedTasks.contains(t)) {
+                mOrganizedTasks.add(t);
+            }
+            if (t.taskAppearedReady()) {
+                try {
+                    t.mTaskAppearedSent = true;
+                    mOrganizer.onTaskAppeared(t.getTaskInfo());
+                } catch (Exception e) {
+                    Slog.e(TAG, "Exception sending taskAppeared callback" + e);
+                }
+            }
         }
 
         void removeTask(Task t) {
+            if (t.mTaskAppearedSent) {
+                try {
+                    t.mTaskAppearedSent = false;
+                    mOrganizer.onTaskVanished(t.getTaskInfo());
+                } catch (Exception e) {
+                    Slog.e(TAG, "Exception sending taskVanished callback" + e);
+                }
+            }
             mOrganizedTasks.remove(t);
-            mOrganizer.onTaskVanished(t);
         }
 
         void dispose() {
             releaseTasks();
             for (int i = mTaskOrganizersForWindowingMode.size() - 1; i >= 0; --i) {
-                mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.getBinder());
+                mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.asBinder());
             }
         }
 
@@ -212,7 +149,7 @@
         }
 
         void unlinkDeath() {
-            mOrganizer.getBinder().unlinkToDeath(mDeathRecipient, 0);
+            mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0);
         }
     }
 
@@ -222,10 +159,9 @@
     private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
     private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
 
-    private final ActivityTaskManagerService mService;
+    final ActivityTaskManagerService mService;
 
-    private RunningTaskInfo mTmpTaskInfo;
-    private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer;
+    RunningTaskInfo mTmpTaskInfo;
 
     TaskOrganizerController(ActivityTaskManagerService atm) {
         mService = atm;
@@ -237,15 +173,6 @@
     }
 
     /**
-     * Specifies the consumer to run to defer the task org callbacks. Can be overridden while
-     * testing to allow the callbacks to be sent synchronously.
-     */
-    @VisibleForTesting
-    public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) {
-        mDeferTaskOrgCallbacksConsumer = consumer;
-    }
-
-    /**
      * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
      * If there was already a TaskOrganizer for this windowing mode it will be evicted
      * but will continue to organize it's existing tasks.
@@ -336,7 +263,7 @@
         if (state == null) {
             return null;
         }
-        return state.mOrganizer.mTaskOrganizer;
+        return state.mOrganizer;
     }
 
     void onTaskAppeared(ITaskOrganizer organizer, Task task) {
@@ -441,10 +368,11 @@
         // change.
         mTmpTaskInfo = null;
 
-        if (task.isOrganized()) {
-            final TaskOrganizerState state = mTaskOrganizerStates.get(
-                    task.mTaskOrganizer.asBinder());
-            state.mOrganizer.onTaskInfoChanged(task, newInfo);
+        if (task.mTaskOrganizer != null) {
+            try {
+                task.mTaskOrganizer.onTaskInfoChanged(newInfo);
+            } catch (RemoteException e) {
+            }
         }
     }
 
@@ -603,7 +531,11 @@
             return false;
         }
 
-        state.mOrganizer.onBackPressedOnTaskRoot(task);
+        try {
+            state.mOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo());
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception sending interceptBackPressedOnTaskRoot callback" + e);
+        }
         return true;
     }
 
@@ -620,7 +552,7 @@
                 final TaskOrganizerState state =  mTaskOrganizerStates.get(taskOrgs.get(j));
                 final ArrayList<Task> tasks = state.mOrganizedTasks;
                 pw.print(innerPrefix + "    ");
-                pw.println(state.mOrganizer.mTaskOrganizer + " uid=" + state.mUid + ":");
+                pw.println(state.mOrganizer + " uid=" + state.mUid + ":");
                 for (int k = 0; k < tasks.size(); k++) {
                     pw.println(innerPrefix + "      " + tasks.get(k));
                 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index eb005e0..14e5c6c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -68,6 +68,7 @@
 import android.util.Slog;
 import android.view.DisplayCutout;
 import android.view.IWindowSession;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -164,6 +165,7 @@
         final Rect tmpContentInsets = new Rect();
         final Rect tmpStableInsets = new Rect();
         final InsetsState mTmpInsetsState = new InsetsState();
+        final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
         final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
         final TaskDescription taskDescription = new TaskDescription();
         taskDescription.setBackgroundColor(WHITE);
@@ -231,8 +233,8 @@
         }
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
-                    View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
-                    tmpCutout, null, mTmpInsetsState);
+                    View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrame, tmpRect,
+                    tmpRect, tmpCutout, null, mTmpInsetsState, mTempControls);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
@@ -249,7 +251,7 @@
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                     tmpFrame, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect,
                     tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
-                    sTmpSurfaceSize, sTmpSurfaceControl);
+                    mTempControls, sTmpSurfaceSize, sTmpSurfaceControl);
         } catch (RemoteException e) {
             // Local call.
         }
@@ -377,6 +379,7 @@
             frame = null;
             mTmpDstFrame.set(mFrame);
         }
+        mTmpDstFrame.offsetTo(0, 0);
 
         // Scale the mismatch dimensions to fill the task bounds
         mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 037b9c8..f6e952c 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -36,6 +36,7 @@
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@@ -790,7 +791,7 @@
      * By default this predicate only checks if this container itself is actually running an
      * animation, but you can extend the check target over its relatives, or relax the condition
      * so that this can return {@code true} if an animation starts soon by giving a combination
-     * of {@link #AnimationFlags}.
+     * of {@link AnimationFlags}.
      *
      * Note that you can give a combination of bitmask flags to specify targets and condition for
      * checking animating status.
@@ -800,12 +801,18 @@
      *
      * Note that TRANSITION propagates to parents and children as well.
      *
-     * {@see AnimationFlags#TRANSITION}
-     * {@see AnimationFlags#PARENTS}
-     * {@see AnimationFlags#CHILDREN}
+     * @param flags The combination of bitmask flags to specify targets and condition for
+     *              checking animating status.
+     * @param typesToCheck The combination of bitmask {@link AnimationType} to compare when
+     *                     determining if animating.
+     *
+     * @see AnimationFlags#TRANSITION
+     * @see AnimationFlags#PARENTS
+     * @see AnimationFlags#CHILDREN
      */
-    boolean isAnimating(int flags) {
-        if (mSurfaceAnimator.isAnimating()) {
+    boolean isAnimating(int flags, int typesToCheck) {
+        int animationType = mSurfaceAnimator.getAnimationType();
+        if (mSurfaceAnimator.isAnimating() && (animationType & typesToCheck) > 0) {
             return true;
         }
         if ((flags & TRANSITION) != 0 && isWaitingForTransitionStart()) {
@@ -813,14 +820,14 @@
         }
         if ((flags & PARENTS) != 0) {
             final WindowContainer parent = getParent();
-            if (parent != null && parent.isAnimating(flags & ~CHILDREN)) {
+            if (parent != null && parent.isAnimating(flags & ~CHILDREN, typesToCheck)) {
                 return true;
             }
         }
         if ((flags & CHILDREN) != 0) {
             for (int i = 0; i < mChildren.size(); ++i) {
                 final WindowContainer wc = mChildren.get(i);
-                if (wc.isAnimating(flags & ~PARENTS)) {
+                if (wc.isAnimating(flags & ~PARENTS, typesToCheck)) {
                     return true;
                 }
             }
@@ -829,6 +836,26 @@
     }
 
     /**
+     * Similar to {@link #isAnimating(int, int)} except provide a bitmask of
+     * {@link AnimationType} to exclude, rather than include
+     * @param flags The combination of bitmask flags to specify targets and condition for
+     *              checking animating status.
+     * @param typesToExclude The combination of bitmask {@link AnimationType} to exclude when
+     *                     checking if animating.
+     */
+    boolean isAnimatingExcluding(int flags, int typesToExclude) {
+        return isAnimating(flags, ANIMATION_TYPE_ALL & ~typesToExclude);
+    }
+
+    /**
+     * @see #isAnimating(int, int)
+     * TODO (b/152333373): Migrate calls to use isAnimating with specified animation type
+     */
+    boolean isAnimating(int flags) {
+        return isAnimating(flags, ANIMATION_TYPE_ALL);
+    }
+
+    /**
      * @return {@code true} when the container is waiting the app transition start, {@code false}
      *         otherwise.
      */
@@ -2180,11 +2207,6 @@
 
     private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
                                     boolean isVoiceInteraction) {
-        if (isOrganized()) {
-            // Defer to the task organizer to run animations
-            return null;
-        }
-
         final DisplayContent displayContent = getDisplayContent();
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         final int width = displayInfo.appWidth;
@@ -2273,6 +2295,10 @@
     public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
         mLastLayer = -1;
         reassignLayer(t);
+
+        // Leash is now responsible for position, so set our position to 0.
+        t.setPosition(mSurfaceControl, 0, 0);
+        mLastSurfacePosition.set(0, 0);
     }
 
     @Override
@@ -2280,6 +2306,7 @@
         mLastLayer = -1;
         mSurfaceFreezer.unfreeze(t);
         reassignLayer(t);
+        updateSurfacePosition(t);
     }
 
     /**
@@ -2343,11 +2370,15 @@
         }
     }
 
-    void updateSurfacePosition() {
+    final void updateSurfacePosition() {
+        updateSurfacePosition(getPendingTransaction());
+    }
+
+    void updateSurfacePosition(Transaction t) {
         // Avoid fighting with the organizer over Surface position.
         if (isOrganized()) return;
 
-        if (mSurfaceControl == null) {
+        if (mSurfaceControl == null || mSurfaceAnimator.hasLeash()) {
             return;
         }
 
@@ -2356,7 +2387,7 @@
             return;
         }
 
-        getPendingTransaction().setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
+        t.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
         mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
     }
 
@@ -2479,9 +2510,7 @@
             // We need to copy the SurfaceControl instead of returning the original
             // because the Parcel FLAGS PARCELABLE_WRITE_RETURN_VALUE cause SurfaceControls
             // to release themselves.
-            SurfaceControl sc = new SurfaceControl();
-            sc.copyFrom(wc.getSurfaceControl());
-            return sc;
+            return new SurfaceControl(wc.getSurfaceControl());
         }
 
         WindowContainerToken toWindowContainerToken() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3cbb85b..8d3a4f2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -227,6 +227,7 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputWindowHandle;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.KeyEvent;
 import android.view.MagnificationSpec;
@@ -1356,7 +1357,7 @@
             LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
             Rect outContentInsets, Rect outStableInsets,
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState) {
+            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
         int[] appOp = new int[1];
         final boolean isRoundedCornerOverlay = (attrs.privateFlags
                 & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
@@ -1643,8 +1644,7 @@
                     outStableInsets, outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
             }
-            outInsetsState.set(win.getInsetsState(),
-                    win.mClient instanceof IWindow.Stub /* copySource */);
+            outInsetsState.set(win.getInsetsState(), win.isClientLocal());
 
             if (mInTouchMode) {
                 res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -1678,6 +1678,8 @@
             }
             displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
 
+            getInsetsSourceControls(win, outActiveControls);
+
             ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
                     + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
 
@@ -2080,7 +2082,8 @@
             Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
-            Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) {
+            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
+            SurfaceControl outBLASTSurfaceControl) {
         int result = 0;
         boolean configChanged;
         final int pid = Binder.getCallingPid();
@@ -2375,8 +2378,8 @@
                     outStableInsets);
             outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
             outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
-            outInsetsState.set(win.getInsetsState(),
-                    win.mClient instanceof IWindow.Stub /* copySource */);
+            outInsetsState.set(win.getInsetsState(), win.isClientLocal());
+            getInsetsSourceControls(win, outActiveControls);
             if (DEBUG) {
                 Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
                         + ", requestedWidth=" + requestedWidth
@@ -2413,6 +2416,21 @@
         return result;
     }
 
+    private void getInsetsSourceControls(WindowState win, InsetsSourceControl[] outControls) {
+        if (outControls != null) {
+            final InsetsSourceControl[] controls =
+                    win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win);
+            Arrays.fill(outControls, null);
+            if (controls != null) {
+                final int length = Math.min(controls.length, outControls.length);
+                for (int i = 0; i < length; i++) {
+                    outControls[i] = win.isClientLocal()
+                            ? new InsetsSourceControl(controls[i]) : controls[i];
+                }
+            }
+        }
+    }
+
     private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator,
             boolean focusMayChange) {
         // Try starting an animation; if there isn't one, we
@@ -5317,7 +5335,8 @@
                 throw new IllegalArgumentException(
                         "Requested window " + client + " does not exist");
             }
-            ProtoLog.w(WM_ERROR, "Failed looking up window callers=%s", Debug.getCallers(3));
+            ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,
+                    Debug.getCallers(3));
             return null;
         }
         if (session != null && win.mSession != session) {
@@ -5325,7 +5344,8 @@
                 throw new IllegalArgumentException("Requested window " + client + " is in session "
                         + win.mSession + ", not " + session);
             }
-            ProtoLog.w(WM_ERROR, "Failed looking up window callers=%s", Debug.getCallers(3));
+            ProtoLog.w(WM_ERROR, "Failed looking up window session=%s callers=%s", session,
+                    Debug.getCallers(3));
             return null;
         }
 
@@ -6049,11 +6069,21 @@
         pw.print("  mHasPermanentDpad="); pw.println(mHasPermanentDpad);
         mRoot.dumpTopFocusedDisplayId(pw);
         mRoot.forAllDisplays(dc -> {
+            final int displayId = dc.getDisplayId();
             final WindowState inputMethodTarget = dc.mInputMethodTarget;
             if (inputMethodTarget != null) {
-                pw.print("  mInputMethodTarget in display# "); pw.print(dc.getDisplayId());
+                pw.print("  mInputMethodTarget in display# "); pw.print(displayId);
                 pw.print(' '); pw.println(inputMethodTarget);
             }
+            if (mAccessibilityController != null) {
+                final Region magnificationRegion = new Region();
+                mAccessibilityController.getMagnificationRegionLocked(displayId,
+                        magnificationRegion);
+                pw.print("  mMagnificationRegion in display# ");
+                pw.print(displayId);
+                pw.print(' ');
+                pw.println(magnificationRegion);
+            }
         });
         pw.print("  mInTouchMode="); pw.println(mInTouchMode);
         pw.print("  mLastDisplayFreezeDuration=");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8e7585a..5a76bac 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3439,13 +3439,23 @@
         getMergedConfiguration(mLastReportedConfiguration);
         mLastConfigReportedToClient = true;
 
+        final boolean reportOrientation = mReportOrientationChanged;
+        // Always reset these states first, so if {@link IWindow#resized} fails, this
+        // window won't be added to {@link WindowManagerService#mResizingWindows} and set
+        // {@link #mOrientationChanging} to true again by {@link #updateResizingWindowIfNeeded}
+        // that may cause WINDOW_FREEZE_TIMEOUT because resizing the client keeps failing.
+        mReportOrientationChanged = false;
+        mDragResizingChangeReported = true;
+        mWinAnimator.mSurfaceResized = false;
+        mWindowFrames.resetInsetsChanged();
+
         final Rect frame = mWindowFrames.mCompatFrame;
         final Rect contentInsets = mWindowFrames.mLastContentInsets;
         final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
         final Rect stableInsets = mWindowFrames.mLastStableInsets;
         final MergedConfiguration mergedConfiguration = mLastReportedConfiguration;
         final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
-        final boolean forceRelayout = mReportOrientationChanged || isDragResizeChanged();
+        final boolean forceRelayout = reportOrientation || isDragResizeChanged();
         final int displayId = getDisplayId();
         final DisplayCutout displayCutout = getWmDisplayCutout().getDisplayCutout();
 
@@ -3454,29 +3464,25 @@
                     mergedConfiguration, getBackdropFrame(frame), forceRelayout,
                     getDisplayContent().getDisplayPolicy().areSystemBarsForcedShownLw(this),
                     displayId, new DisplayCutout.ParcelableWrapper(displayCutout));
-            mDragResizingChangeReported = true;
 
             if (mWmService.mAccessibilityController != null) {
                 mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(displayId);
             }
             updateLocationInParentDisplayIfNeeded();
-
-            mWindowFrames.resetInsetsChanged();
-            mWinAnimator.mSurfaceResized = false;
-            mReportOrientationChanged = false;
         } catch (RemoteException e) {
+            // Cancel orientation change of this window to avoid blocking unfreeze display.
             setOrientationChanging(false);
             mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
                     - mWmService.mDisplayFreezeTime);
-            // We are assuming the hosting process is dead or in a zombie state.
-            Slog.w(TAG, "Failed to report 'resized' to the client of " + this
-                    + ", removing this window.");
-            mWmService.mPendingRemove.add(this);
-            mWmService.mWindowPlacerLocked.requestTraversal();
+            Slog.w(TAG, "Failed to report 'resized' to " + this + " due to " + e);
         }
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
+    boolean isClientLocal() {
+        return mClient instanceof IWindow.Stub;
+    }
+
     void updateLocationInParentDisplayIfNeeded() {
         final int embeddedDisplayContentsSize = mEmbeddedDisplayContents.size();
         // If there is any embedded display which is re-parented to this window, we need to
@@ -4982,7 +4988,7 @@
     }
 
     @Override
-    boolean isAnimating(int flags) {
+    boolean isAnimating(int flags, int typesToCheck) {
 
         // If we are an inset provider, all our animations are driven by the inset client, so we
         // aren't really animating.
@@ -4990,7 +4996,7 @@
         if (mControllableInsetProvider != null) {
             return false;
         }
-        return super.isAnimating(flags);
+        return super.isAnimating(flags, typesToCheck);
     }
 
     void startAnimation(Animation anim) {
@@ -5231,23 +5237,14 @@
     @Override
     public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
         super.onAnimationLeashCreated(t, leash);
-
-        // Leash is now responsible for position, so set our position to 0.
-        t.setPosition(mSurfaceControl, 0, 0);
-        mLastSurfacePosition.set(0, 0);
     }
 
     @Override
     public void onAnimationLeashLost(Transaction t) {
         super.onAnimationLeashLost(t);
-        updateSurfacePosition(t);
     }
 
     @Override
-    void updateSurfacePosition() {
-        updateSurfacePosition(getPendingTransaction());
-    }
-
     @VisibleForTesting
     void updateSurfacePosition(Transaction t) {
         if (mSurfaceControl == null) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index f7ea953..55e6ab7 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -126,7 +126,7 @@
         if (useBLAST) {
             mBLASTSurfaceControl = win.makeSurface()
                 .setParent(mSurfaceControl)
-                .setName("BLAST Adapter Layer")
+                .setName(name + "(BLAST)")
                 .setHidden(false)
                 .setBLASTLayer()
                 .build();
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 7457a1d..23091a0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -535,8 +535,8 @@
     }
 
     @Override
-    void updateSurfacePosition() {
-        super.updateSurfacePosition();
+    void updateSurfacePosition(SurfaceControl.Transaction t) {
+        super.updateSurfacePosition(t);
         if (isFixedRotationTransforming()) {
             // The window is layouted in a simulated rotated display but the real display hasn't
             // rotated, so here transforms its surface to fit in the real display.
diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp
index 636d110..a942108 100644
--- a/services/core/xsd/vts/Android.bp
+++ b/services/core/xsd/vts/Android.bp
@@ -36,7 +36,7 @@
     ],
     test_suites: [
         "general-tests",
-        "vts-core"
+        "vts"
     ],
     test_config: "vts_defaultPermissions_validate_test.xml",
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d1c47d9..67e83ba 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -12459,6 +12459,11 @@
             return DevicePolicyManagerService.this.getAllCrossProfilePackages();
         }
 
+        @Override
+        public List<String> getDefaultCrossProfilePackages() {
+            return DevicePolicyManagerService.this.getDefaultCrossProfilePackages();
+        }
+
         /**
          * Sends the {@code intent} to the packages with cross profile capabilities.
          *
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 02bb0bc..b13d330 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -50,7 +50,6 @@
         "libbinder",
         "libcrypto",
         "libcutils",
-        "libdataloader",
         "libincfs",
         "liblog",
         "libz",
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 97de1800..2dbbc5a 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -155,11 +155,6 @@
     return ok();
 }
 
-binder::Status BinderIncrementalService::setStorageParams(int32_t storage, bool enableReadLogs, int32_t* _aidl_return) {
-    *_aidl_return = mImpl.setStorageParams(storage, enableReadLogs);
-    return ok();
-}
-
 binder::Status BinderIncrementalService::makeDirectory(int32_t storageId, const std::string& path,
                                                        int32_t* _aidl_return) {
     *_aidl_return = mImpl.makeDir(storageId, path);
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index d0357d9..28613e1 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -71,7 +71,6 @@
     binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
                                            const std::string& libDirRelativePath,
                                            const std::string& abi, bool* _aidl_return) final;
-    binder::Status setStorageParams(int32_t storage, bool enableReadLogs, int32_t* _aidl_return) final;
 
 private:
     android::incremental::IncrementalService mImpl;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 0da1673..d1153e6 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "IncrementalService"
 
 #include "IncrementalService.h"
-#include "IncrementalServiceValidation.h"
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -74,7 +73,7 @@
 };
 
 static const Constants& constants() {
-    static Constants c;
+    static constexpr Constants c;
     return c;
 }
 
@@ -160,6 +159,9 @@
 }
 } // namespace
 
+const bool IncrementalService::sEnablePerfLogging =
+        android::base::GetBoolProperty("incremental.perflogging", false);
+
 IncrementalService::IncFsMount::~IncFsMount() {
     incrementalService.mDataLoaderManager->destroyDataLoader(mountId);
     LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\'';
@@ -579,28 +581,33 @@
 int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLogs) {
     const auto ifs = getIfs(storageId);
     if (!ifs) {
+        LOG(ERROR) << "setStorageParams failed, invalid storageId: " << storageId;
         return -EINVAL;
     }
 
-    ifs->dataLoaderFilesystemParams.readLogsEnabled = enableReadLogs;
     if (enableReadLogs) {
-        // We never unregister the callbacks, but given a restricted number of data loaders and even fewer asking for read log access, should be ok.
-        registerAppOpsCallback(ifs->dataLoaderParams.packageName);
-    }
-
-    return applyStorageParams(*ifs);
-}
-
-int IncrementalService::applyStorageParams(IncFsMount& ifs) {
-    const bool enableReadLogs = ifs.dataLoaderFilesystemParams.readLogsEnabled;
-    if (enableReadLogs) {
-        if (auto status = CheckPermissionForDataDelivery(kDataUsageStats, kOpUsage);
+        if (auto status =
+                    mAppOpsManager->checkPermission(kDataUsageStats, kOpUsage,
+                                                    ifs->dataLoaderParams.packageName.c_str());
             !status.isOk()) {
-            LOG(ERROR) << "CheckPermissionForDataDelivery failed: " << status.toString8();
+            LOG(ERROR) << "checkPermission failed: " << status.toString8();
             return fromBinderStatus(status);
         }
     }
 
+    if (auto status = applyStorageParams(*ifs, enableReadLogs); !status.isOk()) {
+        LOG(ERROR) << "applyStorageParams failed: " << status.toString8();
+        return fromBinderStatus(status);
+    }
+
+    if (enableReadLogs) {
+        registerAppOpsCallback(ifs->dataLoaderParams.packageName);
+    }
+
+    return 0;
+}
+
+binder::Status IncrementalService::applyStorageParams(IncFsMount& ifs, bool enableReadLogs) {
     using unique_fd = ::android::base::unique_fd;
     ::android::os::incremental::IncrementalFileSystemControlParcel control;
     control.cmd.reset(unique_fd(dup(ifs.control.cmd())));
@@ -611,13 +618,7 @@
     }
 
     std::lock_guard l(mMountOperationLock);
-    const auto status = mVold->setIncFsMountOptions(control, enableReadLogs);
-    if (!status.isOk()) {
-        LOG(ERROR) << "Calling Vold::setIncFsMountOptions() failed: " << status.toString8();
-        return fromBinderStatus(status);
-    }
-
-    return 0;
+    return mVold->setIncFsMountOptions(control, enableReadLogs);
 }
 
 void IncrementalService::deleteStorage(StorageId storageId) {
@@ -722,7 +723,10 @@
     if (storageInfo == ifs->storages.end()) {
         return -EINVAL;
     }
-    std::string normSource = normalizePathToStorage(ifs, storage, source);
+    std::string normSource = normalizePathToStorageLocked(storageInfo, source);
+    if (normSource.empty()) {
+        return -EINVAL;
+    }
     l.unlock();
     std::unique_lock l2(mLock, std::defer_lock);
     return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource),
@@ -771,22 +775,28 @@
     return 0;
 }
 
-std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr ifs,
+std::string IncrementalService::normalizePathToStorageLocked(
+        IncFsMount::StorageMap::iterator storageIt, std::string_view path) {
+    std::string normPath;
+    if (path::isAbsolute(path)) {
+        normPath = path::normalize(path);
+        if (!path::startsWith(normPath, storageIt->second.name)) {
+            return {};
+        }
+    } else {
+        normPath = path::normalize(path::join(storageIt->second.name, path));
+    }
+    return normPath;
+}
+
+std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr& ifs,
                                                        StorageId storage, std::string_view path) {
+    std::unique_lock l(ifs->lock);
     const auto storageInfo = ifs->storages.find(storage);
     if (storageInfo == ifs->storages.end()) {
         return {};
     }
-    std::string normPath;
-    if (path::isAbsolute(path)) {
-        normPath = path::normalize(path);
-    } else {
-        normPath = path::normalize(path::join(storageInfo->second.name, path));
-    }
-    if (!path::startsWith(normPath, storageInfo->second.name)) {
-        return {};
-    }
-    return normPath;
+    return normalizePathToStorageLocked(storageInfo, path);
 }
 
 int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
@@ -794,7 +804,8 @@
     if (auto ifs = getIfs(storage)) {
         std::string normPath = normalizePathToStorage(ifs, storage, path);
         if (normPath.empty()) {
-            LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path;
+            LOG(ERROR) << "Internal error: storageId " << storage
+                       << " failed to normalize: " << path;
             return -EINVAL;
         }
         auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
@@ -802,10 +813,6 @@
             LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
             return err;
         }
-        std::vector<uint8_t> metadataBytes;
-        if (params.metadata.data && params.metadata.size > 0) {
-            metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size);
-        }
         return 0;
     }
     return -EINVAL;
@@ -845,8 +852,9 @@
 
 int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath,
                              StorageId destStorageId, std::string_view newPath) {
-    if (auto ifsSrc = getIfs(sourceStorageId), ifsDest = getIfs(destStorageId);
-        ifsSrc && ifsSrc == ifsDest) {
+    auto ifsSrc = getIfs(sourceStorageId);
+    auto ifsDest = sourceStorageId == destStorageId ? ifsSrc : getIfs(destStorageId);
+    if (ifsSrc && ifsSrc == ifsDest) {
         std::string normOldPath = normalizePathToStorage(ifsSrc, sourceStorageId, oldPath);
         std::string normNewPath = normalizePathToStorage(ifsDest, destStorageId, newPath);
         if (normOldPath.empty() || normNewPath.empty()) {
@@ -1146,6 +1154,7 @@
     fsControlParcel.incremental->pendingReads.reset(
             base::unique_fd(::dup(ifs.control.pendingReads())));
     fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs())));
+    fsControlParcel.service = new IncrementalServiceConnector(*this, ifs.mountId);
     sp<IncrementalDataLoaderListener> listener =
             new IncrementalDataLoaderListener(*this,
                                               externalListener ? *externalListener
@@ -1159,11 +1168,25 @@
     return true;
 }
 
-// Extract lib filse from zip, create new files in incfs and write data to them
+template <class Duration>
+static long elapsedMcs(Duration start, Duration end) {
+    return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
+}
+
+// Extract lib files from zip, create new files in incfs and write data to them
 bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
                                                  std::string_view libDirRelativePath,
                                                  std::string_view abi) {
+    namespace sc = std::chrono;
+    using Clock = sc::steady_clock;
+    auto start = Clock::now();
+
     const auto ifs = getIfs(storage);
+    if (!ifs) {
+        LOG(ERROR) << "Invalid storage " << storage;
+        return false;
+    }
+
     // First prepare target directories if they don't exist yet
     if (auto res = makeDirs(storage, libDirRelativePath, 0755)) {
         LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath
@@ -1171,148 +1194,196 @@
         return false;
     }
 
-    std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(apkFullPath.data()));
+    auto mkDirsTs = Clock::now();
+
+    std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(path::c_str(apkFullPath)));
     if (!zipFile) {
         LOG(ERROR) << "Failed to open zip file at " << apkFullPath;
         return false;
     }
     void* cookie = nullptr;
     const auto libFilePrefix = path::join(constants().libDir, abi);
-    if (!zipFile.get()->startIteration(&cookie, libFilePrefix.c_str() /* prefix */,
-                                       constants().libSuffix.data() /* suffix */)) {
+    if (!zipFile->startIteration(&cookie, libFilePrefix.c_str() /* prefix */,
+                                 constants().libSuffix.data() /* suffix */)) {
         LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
         return false;
     }
+    auto endIteration = [&zipFile](void* cookie) { zipFile->endIteration(cookie); };
+    auto iterationCleaner = std::unique_ptr<void, decltype(endIteration)>(cookie, endIteration);
+
+    auto openZipTs = Clock::now();
+
+    std::vector<IncFsDataBlock> instructions;
     ZipEntryRO entry = nullptr;
-    bool success = true;
-    while ((entry = zipFile.get()->nextEntry(cookie)) != nullptr) {
+    while ((entry = zipFile->nextEntry(cookie)) != nullptr) {
+        auto startFileTs = Clock::now();
+
         char fileName[PATH_MAX];
-        if (zipFile.get()->getEntryFileName(entry, fileName, sizeof(fileName))) {
+        if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
             continue;
         }
         const auto libName = path::basename(fileName);
         const auto targetLibPath = path::join(libDirRelativePath, libName);
         const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath);
         // If the extract file already exists, skip
-        struct stat st;
-        if (stat(targetLibPathAbsolute.c_str(), &st) == 0) {
-            LOG(INFO) << "Native lib file already exists: " << targetLibPath
-                      << "; skipping extraction";
+        if (access(targetLibPathAbsolute.c_str(), F_OK) == 0) {
+            if (sEnablePerfLogging) {
+                LOG(INFO) << "incfs: Native lib file already exists: " << targetLibPath
+                          << "; skipping extraction, spent "
+                          << elapsedMcs(startFileTs, Clock::now()) << "mcs";
+            }
             continue;
         }
 
-        uint32_t uncompressedLen;
-        if (!zipFile.get()->getEntryInfo(entry, nullptr, &uncompressedLen, nullptr, nullptr,
-                                         nullptr, nullptr)) {
+        uint32_t uncompressedLen, compressedLen;
+        if (!zipFile->getEntryInfo(entry, nullptr, &uncompressedLen, &compressedLen, nullptr,
+                                   nullptr, nullptr)) {
             LOG(ERROR) << "Failed to read native lib entry: " << fileName;
-            success = false;
-            break;
+            return false;
         }
 
         // Create new lib file without signature info
-        incfs::NewFileParams libFileParams{};
-        libFileParams.size = uncompressedLen;
-        libFileParams.signature = {};
-        // Metadata of the new lib file is its relative path
-        IncFsSpan libFileMetadata;
-        libFileMetadata.data = targetLibPath.c_str();
-        libFileMetadata.size = targetLibPath.size();
-        libFileParams.metadata = libFileMetadata;
+        incfs::NewFileParams libFileParams = {
+                .size = uncompressedLen,
+                .signature = {},
+                // Metadata of the new lib file is its relative path
+                .metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()},
+        };
         incfs::FileId libFileId = idFromMetadata(targetLibPath);
-        if (auto res = makeFile(storage, targetLibPath, 0777, libFileId, libFileParams)) {
+        if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0777, libFileId,
+                                        libFileParams)) {
             LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res;
-            success = false;
             // If one lib file fails to be created, abort others as well
-            break;
+            return false;
         }
+
+        auto makeFileTs = Clock::now();
+
         // If it is a zero-byte file, skip data writing
         if (uncompressedLen == 0) {
+            if (sEnablePerfLogging) {
+                LOG(INFO) << "incfs: Extracted " << libName << "(" << compressedLen << " -> "
+                          << uncompressedLen << " bytes): " << elapsedMcs(startFileTs, makeFileTs)
+                          << "mcs, make: " << elapsedMcs(startFileTs, makeFileTs);
+            }
             continue;
         }
 
         // Write extracted data to new file
-        std::vector<uint8_t> libData(uncompressedLen);
-        if (!zipFile.get()->uncompressEntry(entry, &libData[0], uncompressedLen)) {
+        // NOTE: don't zero-initialize memory, it may take a while
+        auto libData = std::unique_ptr<uint8_t[]>(new uint8_t[uncompressedLen]);
+        if (!zipFile->uncompressEntry(entry, libData.get(), uncompressedLen)) {
             LOG(ERROR) << "Failed to extract native lib zip entry: " << fileName;
-            success = false;
-            break;
+            return false;
         }
+
+        auto extractFileTs = Clock::now();
+
         const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId);
         if (!writeFd.ok()) {
             LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd;
-            success = false;
-            break;
+            return false;
         }
-        const int numBlocks = uncompressedLen / constants().blockSize + 1;
-        std::vector<IncFsDataBlock> instructions;
-        auto remainingData = std::span(libData);
-        for (int i = 0; i < numBlocks - 1; i++) {
+
+        auto openFileTs = Clock::now();
+
+        const int numBlocks = (uncompressedLen + constants().blockSize - 1) / constants().blockSize;
+        instructions.clear();
+        instructions.reserve(numBlocks);
+        auto remainingData = std::span(libData.get(), uncompressedLen);
+        for (int i = 0; i < numBlocks; i++) {
+            const auto blockSize = std::min<uint16_t>(constants().blockSize, remainingData.size());
             auto inst = IncFsDataBlock{
-                    .fileFd = writeFd,
+                    .fileFd = writeFd.get(),
                     .pageIndex = static_cast<IncFsBlockIndex>(i),
                     .compression = INCFS_COMPRESSION_KIND_NONE,
                     .kind = INCFS_BLOCK_KIND_DATA,
-                    .dataSize = static_cast<uint16_t>(constants().blockSize),
+                    .dataSize = blockSize,
                     .data = reinterpret_cast<const char*>(remainingData.data()),
             };
             instructions.push_back(inst);
-            remainingData = remainingData.subspan(constants().blockSize);
+            remainingData = remainingData.subspan(blockSize);
         }
-        // Last block
-        auto inst = IncFsDataBlock{
-                .fileFd = writeFd,
-                .pageIndex = static_cast<IncFsBlockIndex>(numBlocks - 1),
-                .compression = INCFS_COMPRESSION_KIND_NONE,
-                .kind = INCFS_BLOCK_KIND_DATA,
-                .dataSize = static_cast<uint16_t>(remainingData.size()),
-                .data = reinterpret_cast<const char*>(remainingData.data()),
-        };
-        instructions.push_back(inst);
+        auto prepareInstsTs = Clock::now();
+
         size_t res = mIncFs->writeBlocks(instructions);
         if (res != instructions.size()) {
             LOG(ERROR) << "Failed to write data into: " << targetLibPath;
-            success = false;
+            return false;
         }
-        instructions.clear();
+
+        if (sEnablePerfLogging) {
+            auto endFileTs = Clock::now();
+            LOG(INFO) << "incfs: Extracted " << libName << "(" << compressedLen << " -> "
+                      << uncompressedLen << " bytes): " << elapsedMcs(startFileTs, endFileTs)
+                      << "mcs, make: " << elapsedMcs(startFileTs, makeFileTs)
+                      << " extract: " << elapsedMcs(makeFileTs, extractFileTs)
+                      << " open: " << elapsedMcs(extractFileTs, openFileTs)
+                      << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
+                      << " write:" << elapsedMcs(prepareInstsTs, endFileTs);
+        }
     }
-    zipFile.get()->endIteration(cookie);
-    return success;
+
+    if (sEnablePerfLogging) {
+        auto end = Clock::now();
+        LOG(INFO) << "incfs: configureNativeBinaries complete in " << elapsedMcs(start, end)
+                  << "mcs, make dirs: " << elapsedMcs(start, mkDirsTs)
+                  << " open zip: " << elapsedMcs(mkDirsTs, openZipTs)
+                  << " extract all: " << elapsedMcs(openZipTs, end);
+    }
+
+    return true;
 }
 
 void IncrementalService::registerAppOpsCallback(const std::string& packageName) {
-    if (packageName.empty()) {
+    sp<IAppOpsCallback> listener;
+    {
+        std::unique_lock lock{mCallbacksLock};
+        auto& cb = mCallbackRegistered[packageName];
+        if (cb) {
+            return;
+        }
+        cb = new AppOpsListener(*this, packageName);
+        listener = cb;
+    }
+
+    mAppOpsManager->startWatchingMode(AppOpsManager::OP_GET_USAGE_STATS, String16(packageName.c_str()), listener);
+}
+
+bool IncrementalService::unregisterAppOpsCallback(const std::string& packageName) {
+    sp<IAppOpsCallback> listener;
+    {
+        std::unique_lock lock{mCallbacksLock};
+        auto found = mCallbackRegistered.find(packageName);
+        if (found == mCallbackRegistered.end()) {
+            return false;
+        }
+        listener = found->second;
+        mCallbackRegistered.erase(found);
+    }
+
+    mAppOpsManager->stopWatchingMode(listener);
+    return true;
+}
+
+void IncrementalService::onAppOpChanged(const std::string& packageName) {
+    if (!unregisterAppOpsCallback(packageName)) {
         return;
     }
 
-    {
-        std::unique_lock lock{mCallbacksLock};
-        if (!mCallbackRegistered.insert(packageName).second) {
-            return;
-        }
-    }
-
-    /* TODO(b/152633648): restore callback after it's not crashing Binder anymore.
-    sp<AppOpsListener> listener = new AppOpsListener(*this, packageName);
-    mAppOpsManager->startWatchingMode(AppOpsManager::OP_GET_USAGE_STATS, String16(packageName.c_str()), listener);
-    */
-}
-
-void IncrementalService::onAppOppChanged(const std::string& packageName) {
     std::vector<IfsMountPtr> affected;
     {
         std::lock_guard l(mLock);
         affected.reserve(mMounts.size());
         for (auto&& [id, ifs] : mMounts) {
-            if (ifs->dataLoaderFilesystemParams.readLogsEnabled && ifs->dataLoaderParams.packageName == packageName) {
+            if (ifs->mountId == id && ifs->dataLoaderParams.packageName == packageName) {
                 affected.push_back(ifs);
             }
         }
     }
-    /* TODO(b/152633648): restore callback after it's not crashing Kernel anymore.
     for (auto&& ifs : affected) {
-        applyStorageParams(*ifs);
+        applyStorageParams(*ifs, false);
     }
-    */
 }
 
 binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId,
@@ -1378,8 +1449,14 @@
     return binder::Status::ok();
 }
 
-void IncrementalService::AppOpsListener::opChanged(int32_t op, const String16&) {
-    incrementalService.onAppOppChanged(packageName);
+void IncrementalService::AppOpsListener::opChanged(int32_t, const String16&) {
+    incrementalService.onAppOpChanged(packageName);
+}
+
+binder::Status IncrementalService::IncrementalServiceConnector::setStorageParams(
+        bool enableReadLogs, int32_t* _aidl_return) {
+    *_aidl_return = incrementalService.setStorageParams(storage, enableReadLogs);
+    return binder::Status::ok();
 }
 
 } // namespace android::incremental
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index ff69633..db14a79 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -39,8 +39,8 @@
 
 #include "ServiceWrappers.h"
 #include "android/content/pm/BnDataLoaderStatusListener.h"
+#include "android/os/incremental/BnIncrementalServiceConnector.h"
 #include "incfs.h"
-#include "dataloader_ndk.h"
 #include "path.h"
 
 using namespace android::os::incremental;
@@ -140,7 +140,7 @@
                                       DataLoaderStatusListener externalListener)
               : incrementalService(incrementalService), externalListener(externalListener) {}
         // Callbacks interface
-        binder::Status onStatusChanged(MountId mount, int newStatus) override;
+        binder::Status onStatusChanged(MountId mount, int newStatus) final;
 
     private:
         IncrementalService& incrementalService;
@@ -150,14 +150,27 @@
     class AppOpsListener : public android::BnAppOpsCallback {
     public:
         AppOpsListener(IncrementalService& incrementalService, std::string packageName) : incrementalService(incrementalService), packageName(std::move(packageName)) {}
-        void opChanged(int32_t op, const String16& packageName) override;
+        void opChanged(int32_t op, const String16& packageName) final;
 
     private:
         IncrementalService& incrementalService;
         const std::string packageName;
     };
 
+    class IncrementalServiceConnector : public BnIncrementalServiceConnector {
+    public:
+        IncrementalServiceConnector(IncrementalService& incrementalService, int32_t storage)
+              : incrementalService(incrementalService), storage(storage) {}
+        binder::Status setStorageParams(bool enableReadLogs, int32_t* _aidl_return) final;
+
+    private:
+        IncrementalService& incrementalService;
+        int32_t const storage;
+    };
+
 private:
+    static const bool sEnablePerfLogging;
+
     struct IncFsMount {
         struct Bind {
             StorageId storage;
@@ -182,7 +195,6 @@
         StorageMap storages;
         BindMap bindPoints;
         DataLoaderParamsParcel dataLoaderParams;
-        DataLoaderFilesystemParams dataLoaderFilesystemParams;
         std::atomic<int> nextStorageDirNo{0};
         std::atomic<int> dataLoaderStatus = -1;
         bool dataLoaderStartRequested = false;
@@ -193,9 +205,7 @@
               : root(std::move(root)),
                 control(std::move(control)),
                 mountId(mountId),
-                incrementalService(incrementalService) {
-            dataLoaderFilesystemParams.readLogsEnabled = false;
-        }
+                incrementalService(incrementalService) {}
         IncFsMount(IncFsMount&&) = delete;
         IncFsMount& operator=(IncFsMount&&) = delete;
         ~IncFsMount();
@@ -231,13 +241,16 @@
     void deleteStorage(IncFsMount& ifs);
     void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock);
     MountMap::iterator getStorageSlotLocked();
-    std::string normalizePathToStorage(const IfsMountPtr incfs, StorageId storage,
+    std::string normalizePathToStorage(const IfsMountPtr& incfs, StorageId storage,
                                        std::string_view path);
+    std::string normalizePathToStorageLocked(IncFsMount::StorageMap::iterator storageIt,
+                                             std::string_view path);
 
-    int applyStorageParams(IncFsMount& ifs);
+    binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
 
     void registerAppOpsCallback(const std::string& packageName);
-    void onAppOppChanged(const std::string& packageName);
+    bool unregisterAppOpsCallback(const std::string& packageName);
+    void onAppOpChanged(const std::string& packageName);
 
     // Member variables
     std::unique_ptr<VoldServiceWrapper> const mVold;
@@ -252,7 +265,7 @@
     BindPathMap mBindsByPath;
 
     std::mutex mCallbacksLock;
-    std::set<std::string> mCallbackRegistered;
+    std::map<std::string, sp<AppOpsListener>> mCallbackRegistered;
 
     std::atomic_bool mSystemReady = false;
     StorageId mNextId = 0;
diff --git a/services/incremental/IncrementalServiceValidation.h b/services/incremental/IncrementalServiceValidation.h
index 24f9f7f..48894c6 100644
--- a/services/incremental/IncrementalServiceValidation.h
+++ b/services/incremental/IncrementalServiceValidation.h
@@ -41,7 +41,8 @@
             : -EIO;
 }
 
-inline binder::Status CheckPermissionForDataDelivery(const char* permission, const char* operation) {
+inline binder::Status CheckPermissionForDataDelivery(const char* permission, const char* operation,
+                                                     const char* package) {
     using android::base::StringPrintf;
 
     int32_t pid;
@@ -52,23 +53,23 @@
                          StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission));
     }
 
+    String16 packageName{package};
+
     // Caller must also have op granted.
     PermissionController pc;
-    // Package is a required parameter. Need to obtain one.
-    Vector<String16> packages;
-    pc.getPackagesForUid(uid, packages);
-    if (packages.empty()) {
+    if (auto packageUid = pc.getPackageUid(packageName, 0); packageUid != uid) {
         return Exception(binder::Status::EX_SECURITY,
-                         StringPrintf("UID %d / PID %d has no packages", uid, pid));
+                         StringPrintf("UID %d / PID %d does not own package %s", uid, pid,
+                                      package));
     }
-    switch (auto result = pc.noteOp(String16(operation), uid, packages[0]); result) {
+    switch (auto result = pc.noteOp(String16(operation), uid, packageName); result) {
         case PermissionController::MODE_ALLOWED:
         case PermissionController::MODE_DEFAULT:
             return binder::Status::ok();
         default:
             return Exception(binder::Status::EX_SECURITY,
-                             StringPrintf("UID %d / PID %d lacks app-op %s, error %d", uid, pid,
-                                          operation, result));
+                             StringPrintf("UID %d / PID %d / package %s lacks app-op %s, error %d",
+                                          uid, pid, package, operation, result));
     }
 }
 
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 449b457..84bf1ff 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include "IncrementalServiceValidation.h"
+
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <android/content/pm/DataLoaderParamsParcel.h>
@@ -85,7 +87,10 @@
 class AppOpsManagerWrapper {
 public:
     virtual ~AppOpsManagerWrapper() = default;
+    virtual binder::Status checkPermission(const char* permission, const char* operation,
+                                           const char* package) const = 0;
     virtual void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) = 0;
+    virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
 };
 
 class ServiceManagerWrapper {
@@ -105,17 +110,19 @@
     ~RealVoldService() = default;
     binder::Status mountIncFs(const std::string& backingPath, const std::string& targetDir,
                               int32_t flags,
-                              IncrementalFileSystemControlParcel* _aidl_return) const override {
+                              IncrementalFileSystemControlParcel* _aidl_return) const final {
         return mInterface->mountIncFs(backingPath, targetDir, flags, _aidl_return);
     }
-    binder::Status unmountIncFs(const std::string& dir) const override {
+    binder::Status unmountIncFs(const std::string& dir) const final {
         return mInterface->unmountIncFs(dir);
     }
     binder::Status bindMount(const std::string& sourceDir,
-                             const std::string& targetDir) const override {
+                             const std::string& targetDir) const final {
         return mInterface->bindMount(sourceDir, targetDir);
     }
-    binder::Status setIncFsMountOptions(const ::android::os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs) const override {
+    binder::Status setIncFsMountOptions(
+            const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
+            bool enableReadLogs) const final {
         return mInterface->setIncFsMountOptions(control, enableReadLogs);
     }
 
@@ -131,13 +138,13 @@
     binder::Status initializeDataLoader(MountId mountId, const DataLoaderParamsParcel& params,
                                         const FileSystemControlParcel& control,
                                         const sp<IDataLoaderStatusListener>& listener,
-                                        bool* _aidl_return) const override {
+                                        bool* _aidl_return) const final {
         return mInterface->initializeDataLoader(mountId, params, control, listener, _aidl_return);
     }
-    binder::Status getDataLoader(MountId mountId, sp<IDataLoader>* _aidl_return) const override {
+    binder::Status getDataLoader(MountId mountId, sp<IDataLoader>* _aidl_return) const final {
         return mInterface->getDataLoader(mountId, _aidl_return);
     }
-    binder::Status destroyDataLoader(MountId mountId) const override {
+    binder::Status destroyDataLoader(MountId mountId) const final {
         return mInterface->destroyDataLoader(mountId);
     }
 
@@ -148,9 +155,18 @@
 class RealAppOpsManager : public AppOpsManagerWrapper {
 public:
     ~RealAppOpsManager() = default;
-    void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) override {
+    binder::Status checkPermission(const char* permission, const char* operation,
+                                   const char* package) const final {
+        return android::incremental::CheckPermissionForDataDelivery(permission, operation, package);
+    }
+    void startWatchingMode(int32_t op, const String16& packageName,
+                           const sp<IAppOpsCallback>& callback) final {
         mAppOpsManager.startWatchingMode(op, packageName, callback);
     }
+    void stopWatchingMode(const sp<IAppOpsCallback>& callback) final {
+        mAppOpsManager.stopWatchingMode(callback);
+    }
+
 private:
     android::AppOpsManager mAppOpsManager;
 };
@@ -174,36 +190,35 @@
 public:
     RealIncFs() = default;
     ~RealIncFs() = default;
-    Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) const override {
+    Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) const final {
         return incfs::createControl(cmd, pendingReads, logs);
     }
     ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id,
-                       NewFileParams params) const override {
+                       NewFileParams params) const final {
         return incfs::makeFile(control, path, mode, id, params);
     }
-    ErrorCode makeDir(const Control& control, std::string_view path, int mode) const override {
+    ErrorCode makeDir(const Control& control, std::string_view path, int mode) const final {
         return incfs::makeDir(control, path, mode);
     }
-    RawMetadata getMetadata(const Control& control, FileId fileid) const override {
+    RawMetadata getMetadata(const Control& control, FileId fileid) const final {
         return incfs::getMetadata(control, fileid);
     }
-    RawMetadata getMetadata(const Control& control, std::string_view path) const override {
+    RawMetadata getMetadata(const Control& control, std::string_view path) const final {
         return incfs::getMetadata(control, path);
     }
-    FileId getFileId(const Control& control, std::string_view path) const override {
+    FileId getFileId(const Control& control, std::string_view path) const final {
         return incfs::getFileId(control, path);
     }
-    ErrorCode link(const Control& control, std::string_view from,
-                   std::string_view to) const override {
+    ErrorCode link(const Control& control, std::string_view from, std::string_view to) const final {
         return incfs::link(control, from, to);
     }
-    ErrorCode unlink(const Control& control, std::string_view path) const override {
+    ErrorCode unlink(const Control& control, std::string_view path) const final {
         return incfs::unlink(control, path);
     }
-    base::unique_fd openForSpecialOps(const Control& control, FileId id) const override {
+    base::unique_fd openForSpecialOps(const Control& control, FileId id) const final {
         return base::unique_fd{incfs::openForSpecialOps(control, id).release()};
     }
-    ErrorCode writeBlocks(Span<const DataBlock> blocks) const override {
+    ErrorCode writeBlocks(Span<const DataBlock> blocks) const final {
         return incfs::writeBlocks(blocks);
     }
 };
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 5553f68..0635ae1 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -221,7 +221,28 @@
 };
 
 class MockAppOpsManager : public AppOpsManagerWrapper {
+public:
+    MOCK_CONST_METHOD3(checkPermission, binder::Status(const char*, const char*, const char*));
     MOCK_METHOD3(startWatchingMode, void(int32_t, const String16&, const sp<IAppOpsCallback>&));
+    MOCK_METHOD1(stopWatchingMode, void(const sp<IAppOpsCallback>&));
+
+    void checkPermissionSuccess() {
+        ON_CALL(*this, checkPermission(_, _, _)).WillByDefault(Return(android::incremental::Ok()));
+    }
+    void checkPermissionFails() {
+        ON_CALL(*this, checkPermission(_, _, _))
+                .WillByDefault(
+                        Return(android::incremental::Exception(binder::Status::EX_SECURITY, {})));
+    }
+    void initializeStartWatchingMode() {
+        ON_CALL(*this, startWatchingMode(_, _, _))
+                .WillByDefault(Invoke(this, &MockAppOpsManager::storeCallback));
+    }
+    void storeCallback(int32_t, const String16&, const sp<IAppOpsCallback>& cb) {
+        mStoredCallback = cb;
+    }
+
+    sp<IAppOpsCallback> mStoredCallback;
 };
 
 class MockServiceManager : public ServiceManagerWrapper {
@@ -418,9 +439,15 @@
     mVold->setIncFsMountOptionsSuccess();
     mDataLoaderManager->initializeDataLoaderSuccess();
     mDataLoaderManager->getDataLoaderSuccess();
+    mAppOpsManager->checkPermissionSuccess();
     EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, _));
+    // We are calling setIncFsMountOptions(true).
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    // After setIncFsMountOptions succeeded expecting to start watching.
+    EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+    // Not expecting callback removal.
+    EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
     int storageId =
             mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
@@ -429,6 +456,56 @@
     ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0);
 }
 
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
+    mVold->mountIncFsSuccess();
+    mIncFs->makeFileSuccess();
+    mVold->bindMountSuccess();
+    mVold->setIncFsMountOptionsSuccess();
+    mDataLoaderManager->initializeDataLoaderSuccess();
+    mDataLoaderManager->getDataLoaderSuccess();
+    mAppOpsManager->checkPermissionSuccess();
+    mAppOpsManager->initializeStartWatchingMode();
+    EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_));
+    EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+    // We are calling setIncFsMountOptions(true).
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    // setIncFsMountOptions(false) is called on the callback.
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+    // After setIncFsMountOptions succeeded expecting to start watching.
+    EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
+    // After callback is called, disable read logs and remove callback.
+    EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(1);
+    TemporaryDir tempDir;
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
+                                               IncrementalService::CreateOptions::CreateNew);
+    ASSERT_GE(storageId, 0);
+    ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0);
+    ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get());
+    mAppOpsManager->mStoredCallback->opChanged(0, {});
+}
+
+TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionFails) {
+    mVold->mountIncFsSuccess();
+    mIncFs->makeFileSuccess();
+    mVold->bindMountSuccess();
+    mDataLoaderManager->initializeDataLoaderSuccess();
+    mDataLoaderManager->getDataLoaderSuccess();
+    mAppOpsManager->checkPermissionFails();
+    EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_));
+    EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+    // checkPermission fails, no calls to set opitions,  start or stop WatchingMode.
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0);
+    EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
+    EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
+    TemporaryDir tempDir;
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
+                                               IncrementalService::CreateOptions::CreateNew);
+    ASSERT_GE(storageId, 0);
+    ASSERT_LT(mIncrementalService->setStorageParams(storageId, true), 0);
+}
+
 TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) {
     mVold->mountIncFsSuccess();
     mIncFs->makeFileSuccess();
@@ -436,9 +513,14 @@
     mVold->setIncFsMountOptionsFails();
     mDataLoaderManager->initializeDataLoaderSuccess();
     mDataLoaderManager->getDataLoaderSuccess();
+    mAppOpsManager->checkPermissionSuccess();
     EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, _));
+    // We are calling setIncFsMountOptions.
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+    // setIncFsMountOptions fails, no calls to start or stop WatchingMode.
+    EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
+    EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
     int storageId =
             mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2a914ec..e2a2473 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -136,6 +136,7 @@
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.ShortcutService;
 import com.android.server.pm.UserManagerService;
+import com.android.server.pm.dex.SystemServerDexLoadReporter;
 import com.android.server.policy.PermissionPolicyService;
 import com.android.server.policy.PhoneWindowManager;
 import com.android.server.policy.role.LegacyRoleResolutionPolicy;
@@ -519,10 +520,8 @@
             // Initialize native services.
             System.loadLibrary("android_servers");
 
-            // Debug builds - allow heap profiling.
-            if (Build.IS_DEBUGGABLE) {
-                initZygoteChildHeapProfiling();
-            }
+            // Allow heap / perf profiling.
+            initZygoteChildHeapProfiling();
 
             // Debug builds - spawn a thread to monitor for fd leaks.
             if (Build.IS_DEBUGGABLE) {
@@ -839,6 +838,11 @@
             Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
         }
 
+        // Now that the package manager has started, register the dex load reporter to capture any
+        // dex files loaded by system server.
+        // These dex files will be optimized by the BackgroundDexOptService.
+        SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService);
+
         mFirstBoot = mPackageManagerService.isFirstBoot();
         mPackageManager = mSystemContext.getPackageManager();
         t.traceEnd();
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index 138f982..f8d197a 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -199,6 +199,12 @@
                 CROSS_PROFILE_APP_PACKAGE_NAME, PERSONAL_PROFILE_UID, PERSONAL_PROFILE_USER_ID);
         ShadowApplicationPackageManager.setPackageUidAsUser(
                 CROSS_PROFILE_APP_PACKAGE_NAME, WORK_PROFILE_UID, WORK_PROFILE_USER_ID);
+        when(mPackageManagerInternal.getPackageUidInternal(
+                CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, PERSONAL_PROFILE_USER_ID))
+                .thenReturn(PERSONAL_PROFILE_UID);
+        when(mPackageManagerInternal.getPackageUidInternal(
+                CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, WORK_PROFILE_USER_ID))
+                .thenReturn(WORK_PROFILE_UID);
     }
 
     @Before
@@ -456,6 +462,19 @@
     }
 
     @Test
+    public void canUserAttemptToConfigureInteractAcrossProfiles_platformSignedAppWithAutomaticPermission_returnsFalse() {
+        mockCrossProfileAppNotWhitelistedByOem();
+        shadowOf(mContext).grantPermissions(
+                Process.myPid(),
+                PERSONAL_PROFILE_UID,
+                Manifest.permission.INTERACT_ACROSS_PROFILES);
+
+        assertThat(mCrossProfileAppsServiceImpl
+                .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
+                .isFalse();
+    }
+
+    @Test
     public void canUserAttemptToConfigureInteractAcrossProfiles_returnsTrue() {
         assertThat(mCrossProfileAppsServiceImpl
                 .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
@@ -528,6 +547,11 @@
                 .thenReturn(new ArrayList<>());
     }
 
+    private void mockCrossProfileAppNotWhitelistedByOem() {
+        when(mDevicePolicyManagerInternal.getDefaultCrossProfilePackages())
+                .thenReturn(new ArrayList<>());
+    }
+
     private boolean receivedManifestCanInteractAcrossProfilesChangedBroadcast() {
         final UserHandle userHandle = UserHandle.of(PERSONAL_PROFILE_USER_ID);
         if (!mSentUserBroadcasts.containsKey(userHandle)) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 7bf1d98..0445bff 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -16,7 +16,10 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY;
+import static android.accessibilityservice.AccessibilityService.ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS;
 import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_HOME;
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_STATUS;
 import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION;
 import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES;
 import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
@@ -69,6 +72,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.graphics.Region;
+import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -93,6 +97,7 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
 
@@ -162,6 +167,7 @@
     @Mock private IAccessibilityInteractionConnectionCallback mMockCallback;
     @Mock private FingerprintGestureDispatcher mMockFingerprintGestureDispatcher;
     @Mock private MagnificationController mMockMagnificationController;
+    @Mock private RemoteCallback.OnResultListener mMockListener;
 
     @Before
     public void setup() {
@@ -705,6 +711,38 @@
         }));
     }
 
+    @Test
+    public void takeScreenshot_NoA11yAccess_returnErrorCode() throws InterruptedException {
+        // no checkAccessibilityAccess, should return error code.
+        when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
+        when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
+
+        mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY,
+                new RemoteCallback(mMockListener));
+        mHandler.sendLastMessage();
+
+        verify(mMockListener).onResult(Mockito.argThat(
+                bundle -> ERROR_TAKE_SCREENSHOT_NO_ACCESSIBILITY_ACCESS
+                        == bundle.getInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS)));
+    }
+
+    @Test
+    public void takeScreenshot_invalidDisplay_returnErrorCode() throws InterruptedException {
+        when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
+        when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(true);
+
+        final DisplayManager displayManager = new DisplayManager(mMockContext);
+        when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn(displayManager);
+
+        mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY + 1,
+                new RemoteCallback(mMockListener));
+        mHandler.sendLastMessage();
+
+        verify(mMockListener).onResult(Mockito.argThat(
+                bundle -> ERROR_TAKE_SCREENSHOT_INVALID_DISPLAY
+                        == bundle.getInt(KEY_ACCESSIBILITY_SCREENSHOT_STATUS)));
+    }
+
     private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType,
             int feedbackType, int flags, String[] packageNames, int notificationTimeout) {
         serviceInfo.eventTypes = eventType;
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 234c987..7b3417a 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -60,6 +60,7 @@
     @Mock HysteresisLevels mAmbientBrightnessThresholds;
     @Mock HysteresisLevels mScreenBrightnessThresholds;
     @Mock Handler mNoopHandler;
+    @Mock DisplayDeviceConfig mDisplayDeviceConfig;
 
     private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
     @Before
@@ -82,7 +83,8 @@
                 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
                 INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
                 DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
-                mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext);
+                mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext,
+                mDisplayDeviceConfig);
         controller.setLoggingEnabled(true);
 
         // Configure the brightness controller and grab an instance of the sensor listener,
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
new file mode 100644
index 0000000..301a9fe
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.server.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Point;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class LogicalDisplayTest {
+    private static final int DISPLAY_ID = 0;
+    private static final int LAYER_STACK = 0;
+    private static final int DISPLAY_WIDTH = 100;
+    private static final int DISPLAY_HEIGHT = 200;
+
+    private LogicalDisplay mLogicalDisplay;
+    private DisplayDevice mDisplayDevice;
+
+    @Before
+    public void setUp() {
+        // Share classloader to allow package private access.
+        System.setProperty("dexmaker.share_classloader", "true");
+        mDisplayDevice = mock(DisplayDevice.class);
+        DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
+        displayDeviceInfo.width = DISPLAY_WIDTH;
+        displayDeviceInfo.height = DISPLAY_HEIGHT;
+        displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+        mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
+        when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(displayDeviceInfo);
+
+        ArrayList<DisplayDevice> displayDevices = new ArrayList<>();
+        displayDevices.add(mDisplayDevice);
+        mLogicalDisplay.updateLocked(displayDevices);
+    }
+
+    @Test
+    public void testGetDisplayPosition() {
+        Point expectedPosition = new Point();
+
+        SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+        mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+        assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
+
+        expectedPosition.set(20, 40);
+        mLogicalDisplay.setDisplayOffsetsLocked(20, 40);
+        mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+        assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
+
+        expectedPosition.set(40, -20);
+        DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = DISPLAY_HEIGHT;
+        displayInfo.logicalHeight = DISPLAY_WIDTH;
+        displayInfo.rotation = Surface.ROTATION_90;
+        mLogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked(displayInfo);
+        mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
+        assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
index 7c9a81d..a525814 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
@@ -43,7 +43,9 @@
 @RunWith(AndroidJUnit4.class)
 public class AppSaturationControllerTest {
 
-    private static final String TEST_PACKAGE_NAME = "com.android.test";
+    private static final String TEST_CALLER_PACKAGE_NAME = "com.android.test.caller";
+    private static final String TEST_CALLER_PACKAGE_NAME_TWO = "com.android.test.caller.two";
+    private static final String TEST_AFFECTED_PACKAGE_NAME = "com.android.test.affected";
 
     private int mUserId;
     private AppSaturationController mAppSaturationController;
@@ -70,8 +72,11 @@
     public void addColorTransformController_appliesExistingSaturation() {
         final WeakReference<ColorTransformController> ref = new WeakReference<>(
                 mColorTransformController);
-        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
-        mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+                        30);
+        mAppSaturationController
+                .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
         AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
         verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
     }
@@ -80,14 +85,19 @@
     public void setSaturationLevel_resetToDefault() {
         final WeakReference<ColorTransformController> ref = new WeakReference<>(
                 mColorTransformController);
-        mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+        mAppSaturationController
+                .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
         verify(mColorTransformController, never())
                 .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
-        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+                        30);
         AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
         verify(mColorTransformController, times(1))
                 .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
-        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100);
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+                        100);
         AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix);
         verify(mColorTransformController, times(2))
                 .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
@@ -97,19 +107,76 @@
     public void setSaturationLevel_updateLevel() {
         final WeakReference<ColorTransformController> ref = new WeakReference<>(
                 mColorTransformController);
-        mAppSaturationController.addColorTransformController(TEST_PACKAGE_NAME, mUserId, ref);
+        mAppSaturationController
+                .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
         verify(mColorTransformController, never())
                 .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
-        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 30);
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+                        30);
         AppSaturationController.computeGrayscaleTransformMatrix(.3f, mMatrix);
         verify(mColorTransformController).applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
-        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 70);
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+                        70);
         AppSaturationController.computeGrayscaleTransformMatrix(.7f, mMatrix);
         verify(mColorTransformController, times(2))
                 .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
-        mAppSaturationController.setSaturationLevel(TEST_PACKAGE_NAME, mUserId, 100);
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+                        100);
         AppSaturationController.computeGrayscaleTransformMatrix(1.0f, mMatrix);
         verify(mColorTransformController, times(3))
                 .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
     }
+
+    @Test
+    public void setSaturationLevel_multipleCallers_appliesStrongest() {
+        final WeakReference<ColorTransformController> ref = new WeakReference<>(
+                mColorTransformController);
+        mAppSaturationController
+                .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
+        verify(mColorTransformController, never())
+                .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+                        30);
+        AppSaturationController.computeGrayscaleTransformMatrix(0.3f, mMatrix);
+        verify(mColorTransformController, times(1))
+                .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME_TWO, TEST_AFFECTED_PACKAGE_NAME,
+                        mUserId,
+                        70);
+        verify(mColorTransformController, times(2))
+                .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+    }
+
+    @Test
+    public void setSaturationLevel_multipleCallers_removingOneDoesNotAffectTheOther() {
+        final WeakReference<ColorTransformController> ref = new WeakReference<>(
+                mColorTransformController);
+        mAppSaturationController
+                .addColorTransformController(TEST_AFFECTED_PACKAGE_NAME, mUserId, ref);
+        verify(mColorTransformController, never())
+                .applyAppSaturation(any(), eq(TRANSLATION_VECTOR));
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME, TEST_AFFECTED_PACKAGE_NAME, mUserId,
+                        70);
+        AppSaturationController.computeGrayscaleTransformMatrix(0.7f, mMatrix);
+        verify(mColorTransformController, times(1))
+                .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME_TWO, TEST_AFFECTED_PACKAGE_NAME,
+                        mUserId,
+                        30);
+        AppSaturationController.computeGrayscaleTransformMatrix(0.3f, mMatrix);
+        verify(mColorTransformController, times(2))
+                .applyAppSaturation(eq(mMatrix), eq(TRANSLATION_VECTOR));
+        mAppSaturationController
+                .setSaturationLevel(TEST_CALLER_PACKAGE_NAME_TWO, TEST_AFFECTED_PACKAGE_NAME,
+                        mUserId,
+                        100);
+        AppSaturationController.computeGrayscaleTransformMatrix(0.7f, mMatrix);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 5109de5..b60e993 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -17,7 +17,9 @@
 package com.android.server.pm;
 
 
-import static org.hamcrest.Matchers.arrayContaining;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -48,7 +50,6 @@
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 
-import org.hamcrest.Matcher;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -58,7 +59,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.security.cert.CertificateException;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -650,32 +651,32 @@
         final int hasProviderAppId = Process.FIRST_APPLICATION_UID + 1;
         final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2;
         PackageSetting system = simulateAddPackage(appsFilter, pkg("some.system.pkg"), systemAppId);
-        PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("some.system.pkg"),
+        PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("com.some.package"),
                 seesNothingAppId);
         PackageSetting hasProvider = simulateAddPackage(appsFilter,
-                pkgWithProvider("com.some.package", "com.some.authority"), hasProviderAppId);
+                pkgWithProvider("com.some.other.package", "com.some.authority"), hasProviderAppId);
         PackageSetting queriesProvider = simulateAddPackage(appsFilter,
-                pkgQueriesProvider("com.some.other.package", "com.some.authority"),
+                pkgQueriesProvider("com.yet.some.other.package", "com.some.authority"),
                 queriesProviderAppId);
 
         final int[] systemFilter =
                 appsFilter.getVisibilityWhitelist(system, new int[]{0}, mExisting).get(0);
-        assertThat(Arrays.asList(systemFilter), arrayContaining(systemAppId));
+        assertThat(toList(systemFilter), empty());
 
         final int[] seesNothingFilter =
                 appsFilter.getVisibilityWhitelist(seesNothing, new int[]{0}, mExisting).get(0);
-        assertThat(Arrays.asList(seesNothingFilter),
-                arrayContaining(systemAppId, seesNothingAppId));
+        assertThat(toList(seesNothingFilter),
+                contains(seesNothingAppId));
 
         final int[] hasProviderFilter =
                 appsFilter.getVisibilityWhitelist(hasProvider, new int[]{0}, mExisting).get(0);
-        assertThat(Arrays.asList(hasProviderFilter),
-                arrayContaining(systemAppId, hasProviderAppId, queriesProviderAppId));
+        assertThat(toList(hasProviderFilter),
+                contains(hasProviderAppId, queriesProviderAppId));
 
         int[] queriesProviderFilter =
                 appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
-        assertThat(Arrays.asList(queriesProviderFilter),
-                arrayContaining(systemAppId, queriesProviderAppId));
+        assertThat(toList(queriesProviderFilter),
+                contains(queriesProviderAppId));
 
         // provider read
         appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId);
@@ -683,11 +684,16 @@
         // ensure implicit access is included in the filter
         queriesProviderFilter =
                 appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
-        assertThat(Arrays.asList(queriesProviderFilter),
-                arrayContaining(systemAppId, hasProviderAppId, queriesProviderAppId));
+        assertThat(toList(queriesProviderFilter),
+                contains(hasProviderAppId, queriesProviderAppId));
     }
 
-    private void assertThat(List<int[]> asList, Matcher<Integer[]> arrayContainingInAnyOrder) {
+    private List<Integer> toList(int[] array) {
+        ArrayList<Integer> ret = new ArrayList<>(array.length);
+        for (int i = 0; i < array.length; i++) {
+            ret.add(i, array[i]);
+        }
+        return ret;
     }
 
     private interface WithSettingBuilder {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index d69e1b8..8398585 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -85,6 +85,9 @@
     private TestData mBarUser0UnsupportedClassLoader;
     private TestData mBarUser0DelegateLastClassLoader;
 
+    private TestData mSystemServerJar;
+    private TestData mSystemServerJarInvalid;
+
     private int mUser0;
     private int mUser1;
 
@@ -108,6 +111,9 @@
         mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
                 DELEGATE_LAST_CLASS_LOADER_NAME);
 
+        mSystemServerJar = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME);
+        mSystemServerJarInvalid = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME);
+
         mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null,
                 mInstaller, mInstallLock);
 
@@ -587,6 +593,25 @@
         assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
     }
 
+
+    @Test
+    public void testNotifySystemServerUse() {
+        List<String> dexFiles = new ArrayList<String>();
+        dexFiles.add("/system/framework/foo");
+        notifyDexLoad(mSystemServerJar, dexFiles, mUser0);
+        PackageUseInfo pui = getPackageUseInfo(mSystemServerJar);
+        assertIsUsedByOtherApps(mSystemServerJar, pui, false);
+    }
+
+    @Test
+    public void testNotifySystemServerInvalidUse() {
+        List<String> dexFiles = new ArrayList<String>();
+        dexFiles.add("/data/foo");
+        notifyDexLoad(mSystemServerJarInvalid, dexFiles, mUser0);
+        assertNoUseInfo(mSystemServerJarInvalid);
+        assertNoDclInfo(mSystemServerJarInvalid);
+    }
+
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
             String[] expectedContexts) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 5df4509..adf4551 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -77,25 +77,25 @@
         String fooDataDir = "/data/user/0/com.google.foo/";
 
         mFooBaseUser0 = new TestData(fooPackageName,
-                fooCodeDir + "base.apk", 0, ISA, false, true, fooPackageName);
+                fooCodeDir + "base.apk", 0, ISA, true, fooPackageName);
 
         mFooSplit1User0 = new TestData(fooPackageName,
-                fooCodeDir + "split-1.apk", 0, ISA, false, true, fooPackageName);
+                fooCodeDir + "split-1.apk", 0, ISA, true, fooPackageName);
 
         mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooCodeDir + "split-2.apk", 0, ISA, true, true, "used.by.other.com");
+                fooCodeDir + "split-2.apk", 0, ISA, true, "used.by.other.com");
 
         mFooSecondary1User0 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 0, ISA, false, false, fooPackageName);
+                fooDataDir + "sec-1.dex", 0, ISA, false, fooPackageName);
 
         mFooSecondary1User1 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 1, ISA, false, false, fooPackageName);
+                fooDataDir + "sec-1.dex", 1, ISA, false, fooPackageName);
 
         mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooDataDir + "sec-2.dex", 0, ISA, true, false, "used.by.other.com");
+                fooDataDir + "sec-2.dex", 0, ISA, false, "used.by.other.com");
 
         mInvalidIsa = new TestData(fooPackageName,
-                fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER");
+                fooCodeDir + "base.apk", 0, "INVALID_ISA", true, "INALID_USER");
 
         String barPackageName = "com.google.bar";
         String barCodeDir = "/data/app/com.google.bar/";
@@ -103,11 +103,11 @@
         String barDataDir1 = "/data/user/1/com.google.bar/";
 
         mBarBaseUser0 = new TestData(barPackageName,
-                barCodeDir + "base.apk", 0, ISA, false, true, barPackageName);
+                barCodeDir + "base.apk", 0, ISA, true, barPackageName);
         mBarSecondary1User0 = new TestData(barPackageName,
-                barDataDir + "sec-1.dex", 0, ISA, false, false, barPackageName);
+                barDataDir + "sec-1.dex", 0, ISA, false, barPackageName);
         mBarSecondary2User1 = new TestData(barPackageName,
-                barDataDir1 + "sec-2.dex", 1, ISA, false, false, barPackageName);
+                barDataDir1 + "sec-2.dex", 1, ISA, false, barPackageName);
     }
 
     @Test
@@ -134,7 +134,9 @@
     public void testRecordSplitPrimarySequence() {
         // Assert new information.
         assertTrue(record(mFooBaseUser0));
-        // Assert no new information.
+        assertTrue(record(mFooSplit1User0));
+        // Assert no new information if we add again
+        assertFalse(record(mFooBaseUser0));
         assertFalse(record(mFooSplit1User0));
 
         assertPackageDexUsage(mFooBaseUser0);
@@ -192,7 +194,7 @@
         for (int i = 1; i <= tooManyFiles; i++) {
             String fooPackageName = "com.google.foo";
             TestData testData = new TestData(fooPackageName,
-                    "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false, false,
+                    "/data/user/0/" + fooPackageName + "/sec-" + i + "1.dex", 0, ISA, false,
                     fooPackageName);
             if (i < tooManyFiles) {
                 assertTrue("Adding " + testData.mDexFile, record(testData));
@@ -200,7 +202,11 @@
             } else {
                 assertFalse("Adding " + testData.mDexFile, record(testData));
             }
-            assertPackageDexUsage(mPackageDexUsage, null, null, expectedSecondaries);
+            assertPackageDexUsage(
+                    mPackageDexUsage,
+                    /* usdeBy=*/ (Set<String>) null,
+                    /* primaryDex= */ null,
+                    expectedSecondaries);
         }
     }
 
@@ -276,7 +282,7 @@
         Map<String, Set<String>> packageToCodePaths = new HashMap<>();
         packageToCodePaths.put(mBarBaseUser0.mPackageName,
                 new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
-        mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+        mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths, new ArrayList<String>());
 
         // Assert that only user 1 files are there.
         assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1);
@@ -284,6 +290,41 @@
     }
 
     @Test
+    public void testSyncDataKeepPackages() {
+        PackageDexUsage packageDexUsage = new PackageDexUsage();
+        // Write the record we want to keep and which won't be keep by default.
+        Set<String> fooUsers = new HashSet<>(Arrays.asList(
+                new String[] {mFooBaseUser0.mPackageName}));
+        assertTrue(record(packageDexUsage, mFooBaseUser0, fooUsers));
+        // Write a record that would be kept by default.
+        Set<String> barUsers = new HashSet<>(Arrays.asList(
+                new String[] {"another.package", mFooBaseUser0.mPackageName}));
+        assertTrue(record(packageDexUsage, mBarBaseUser0, barUsers));
+
+        // Construct the user packages and their code paths (things that will be
+        // kept by default during sync).
+        Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+        packageToUsersMap.put(mBarBaseUser0.mPackageName,
+                new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
+        Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+        packageToCodePaths.put(mBarBaseUser0.mPackageName,
+                new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+
+        // Sync data.
+        List<String> keepData = new ArrayList<String>();
+        keepData.add(mFooBaseUser0.mPackageName);
+        packageDexUsage.syncData(packageToUsersMap, packageToCodePaths, keepData);
+
+        // Assert that both packages are kept
+        assertPackageDexUsage(packageDexUsage, fooUsers, mFooBaseUser0);
+        // "another.package" should not be in the loading packages after sync.
+        Set<String> expectedBarUsers = new HashSet<>(Arrays.asList(
+                new String[] {mFooBaseUser0.mPackageName}));
+        assertPackageDexUsage(packageDexUsage, expectedBarUsers,
+                mBarBaseUser0.updateUsedBy(mFooBaseUser0.mPackageName));
+    }
+
+    @Test
     public void testRemovePackage() {
         // Record Bar secondaries for two different users.
         assertTrue(record(mBarSecondary1User0));
@@ -345,9 +386,8 @@
             mFooSplit2UsedByOtherApps0.mDexFile,
             mFooSplit2UsedByOtherApps0.mOwnerUserId,
             mFooSplit2UsedByOtherApps0.mLoaderIsa,
-            /*mIsUsedByOtherApps*/false,
             mFooSplit2UsedByOtherApps0.mPrimaryOrSplit,
-            mFooSplit2UsedByOtherApps0.mUsedBy);
+            /*usedBy=*/ null);
         assertPackageDexUsage(noLongerUsedByOtherApps);
     }
 
@@ -371,19 +411,19 @@
         assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
         assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
 
-        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
-        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary2UsedByOtherApps0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary2UsedByOtherApps0, usersExtra));
 
         packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
         // Verify that the users were recorded.
         Set<String> userAll = new HashSet<>(users);
         userAll.addAll(usersExtra);
         assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooSplit2UsedByOtherApps0,
-                mFooSecondary1User0);
+                mFooSecondary2UsedByOtherApps0);
     }
 
     @Test
-    public void testRecordDexFileUsersNotTheOwningPackage() {
+    public void testRecordDexFileUsersAndTheOwningPackage() {
         PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
         Set<String> users = new HashSet<>(Arrays.asList(
                 new String[] {mFooSplit2UsedByOtherApps0.mPackageName}));
@@ -393,13 +433,13 @@
         assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
         assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
 
-        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
-        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
-
         packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
-        // Verify that only the non owning packages were recorded.
-        assertPackageDexUsage(packageDexUsageRecordUsers, usersExtra, mFooSplit2UsedByOtherApps0,
-                mFooSecondary1User0);
+
+        Set<String> expectedUsers = new HashSet<>(users);
+        expectedUsers.addAll(usersExtra);
+        // Verify that all loading packages were recorded.
+        assertPackageDexUsage(
+                packageDexUsageRecordUsers, expectedUsers, mFooSplit2UsedByOtherApps0);
     }
 
     @Test
@@ -421,44 +461,97 @@
     }
 
     @Test
-    public void testRecordClassLoaderContextTransitionFromUnknown() {
-        // Record a secondary dex file.
-        TestData unknownContext = mFooSecondary1User0.updateClassLoaderContext(
-                PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT);
-        assertTrue(record(unknownContext));
-
-        assertPackageDexUsage(null, unknownContext);
-        writeAndReadBack();
-        assertPackageDexUsage(null, unknownContext);
-
-        // Now update the secondary dex record with a class loader context. This simulates the
-        // version 2 to version 3 upgrade.
-
-        assertTrue(record(mFooSecondary1User0));
-
-        assertPackageDexUsage(null, mFooSecondary1User0);
-        writeAndReadBack();
-        assertPackageDexUsage(null, mFooSecondary1User0);
-    }
-
-    @Test
     public void testDexUsageClassLoaderContext() {
         final boolean isUsedByOtherApps = false;
         final int userId = 0;
         PackageDexUsage.DexUseInfo validContext = new DexUseInfo(isUsedByOtherApps, userId,
                 "valid_context", "arm");
-        assertFalse(validContext.isUnknownClassLoaderContext());
+        assertFalse(validContext.isUnsupportedClassLoaderContext());
         assertFalse(validContext.isVariableClassLoaderContext());
 
         PackageDexUsage.DexUseInfo variableContext = new DexUseInfo(isUsedByOtherApps, userId,
                 PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, "arm");
-        assertFalse(variableContext.isUnknownClassLoaderContext());
+        assertFalse(variableContext.isUnsupportedClassLoaderContext());
         assertTrue(variableContext.isVariableClassLoaderContext());
+    }
 
-        PackageDexUsage.DexUseInfo unknownContext = new DexUseInfo(isUsedByOtherApps, userId,
-                PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT, "arm");
-        assertTrue(unknownContext.isUnknownClassLoaderContext());
-        assertFalse(unknownContext.isVariableClassLoaderContext());
+    @Test
+    public void testRead() {
+        String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+        // Equivalent to
+        //   record(mFooSplit2UsedByOtherApps0);
+        //   record(mFooSecondary1User0);
+        //   record(mFooSecondary2UsedByOtherApps0);
+        //   record(mBarBaseUser0);
+        //   record(mBarSecondary1User0);
+        String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__2\n"
+                + "com.google.foo\n"
+                + "+/data/app/com.google.foo/split-2.apk\n"
+                + "@used.by.other.com\n"
+                + "#/data/user/0/com.google.foo/sec-2.dex\n"
+                + "0,1," + ISA + "\n"
+                + "@used.by.other.com\n"
+                + "PCL[/data/user/0/com.google.foo/sec-2.dex]\n"
+                + "#/data/user/0/com.google.foo/sec-1.dex\n"
+                + "0,0," + ISA + "\n"
+                + "@\n"
+                + "PCL[/data/user/0/com.google.foo/sec-1.dex]\n"
+                + "com.google.bar\n"
+                + "+/data/app/com.google.bar/base.apk\n"
+                + "@com.google.bar\n"
+                + "#/data/user/0/com.google.bar/sec-1.dex\n"
+                + "0,0," + ISA + "\n"
+                + "@\n"
+                + "PCL[/data/user/0/com.google.bar/sec-1.dex]";
+
+        PackageDexUsage packageDexUsage = new PackageDexUsage();
+        try {
+            packageDexUsage.read(new StringReader(content));
+        } catch (IOException e) {
+            fail();
+        }
+
+        // After the read we must sync the data to fill the missing information on the code paths.
+        Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+        Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+
+        // Handle foo package.
+        packageToUsersMap.put(
+                mFooSplit2UsedByOtherApps0.mPackageName,
+                new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
+        packageToCodePaths.put(
+                mFooSplit2UsedByOtherApps0.mPackageName,
+                new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mDexFile,
+                        mFooSplit1User0.mDexFile, mFooBaseUser0.mDexFile)));
+        // Handle bar package.
+        packageToUsersMap.put(
+                mBarBaseUser0.mPackageName,
+                new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
+        packageToCodePaths.put(
+                mBarBaseUser0.mPackageName,
+                new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+        // Handle the loading package.
+        packageToUsersMap.put(
+                mFooSplit2UsedByOtherApps0.mUsedBy,
+                new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
+
+        // Sync the data.
+        packageDexUsage.syncData(packageToUsersMap, packageToCodePaths, new ArrayList<>());
+
+        // Assert foo code paths.
+        assertPackageDexUsage(
+                packageDexUsage,
+                /*nonDefaultUsers=*/ null,
+                mFooSplit2UsedByOtherApps0,
+                mFooSecondary2UsedByOtherApps0,
+                mFooSecondary1User0);
+
+        // Assert bar code paths.
+        assertPackageDexUsage(
+                packageDexUsage,
+                /*nonDefaultUsers=*/ null,
+                mBarBaseUser0,
+                mBarSecondary1User0);
     }
 
     @Test
@@ -483,77 +576,19 @@
     }
 
     @Test
-    public void testReadVersion1() {
+    public void testEnsureLoadingPackagesCanBeExtended() {
         String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
-        // Equivalent to
-        //   record(mFooSplit2UsedByOtherApps0);
-        //   record(mFooSecondary1User0);
-        //   record(mFooSecondary2UsedByOtherApps0);
-        //   record(mBarBaseUser0);
-        //   record(mBarSecondary1User0);
-        String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__1\n"
-                + "com.google.foo,1\n"
-                + "#/data/user/0/com.google.foo/sec-1.dex\n"
-                + "0,0," + isa + "\n"
-                + "#/data/user/0/com.google.foo/sec-2.dex\n"
-                + "0,1," + isa + "\n"
-                + "com.google.bar,0\n"
-                + "#/data/user/0/com.google.bar/sec-1.dex\n"
-                + "0,0," + isa + "\n";
-
+        String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__2\n"
+                + "com.google.foo\n"
+                + "+/data/app/com.google.foo/split-2.apk\n"
+                + "@\n";
         PackageDexUsage packageDexUsage = new PackageDexUsage();
         try {
             packageDexUsage.read(new StringReader(content));
         } catch (IOException e) {
             fail();
         }
-
-        // After the read we must sync the data to fill the missing information on the code paths.
-        Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
-        Map<String, Set<String>> packageToCodePaths = new HashMap<>();
-
-        // Handle foo package.
-        packageToUsersMap.put(mFooSplit2UsedByOtherApps0.mPackageName,
-            new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
-        packageToCodePaths.put(mFooSplit2UsedByOtherApps0.mPackageName,
-            new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mDexFile,
-                mFooSplit1User0.mDexFile, mFooBaseUser0.mDexFile)));
-        // Handle bar package.
-        packageToUsersMap.put(mBarBaseUser0.mPackageName,
-            new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
-        packageToCodePaths.put(mBarBaseUser0.mPackageName,
-            new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
-
-        // Sync the data.
-        packageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
-
-        // Update the class loaders to unknown before asserting if needed. Before version 2 we
-        // didn't have any.
-        String unknown = PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT;
-        TestData fooBaseUser0 = mFooBaseUser0.updateClassLoaderContext(unknown);
-        TestData fooSplit1User0 = mFooSplit1User0.updateClassLoaderContext(unknown);
-        TestData fooSplit2UsedByOtherApps0 =
-            mFooSplit2UsedByOtherApps0.updateClassLoaderContext(unknown);
-        TestData fooSecondary1User0 = mFooSecondary1User0.updateClassLoaderContext(unknown);
-        TestData fooSecondary2UsedByOtherApps0 =
-            mFooSecondary2UsedByOtherApps0.updateClassLoaderContext(unknown);
-        TestData barBaseUser0 = mBarBaseUser0.updateClassLoaderContext(unknown);
-        TestData barSecondary1User0 = mBarSecondary1User0.updateClassLoaderContext(unknown);
-
-        // Assert foo code paths. Note that we ignore the users during upgrade.
-        final Set<String> ignoredUsers = null;
-        assertPackageDexUsage(packageDexUsage, ignoredUsers,
-            fooSplit2UsedByOtherApps0, fooSecondary1User0, fooSecondary2UsedByOtherApps0);
-        // Because fooSplit2UsedByOtherApps0 is used by others, all the other code paths must
-        // share the same data.
-        assertPackageDexUsage(packageDexUsage, ignoredUsers,
-            fooSplit1User0.updateUseByOthers(true),
-            fooSecondary1User0, fooSecondary2UsedByOtherApps0);
-        assertPackageDexUsage(packageDexUsage, ignoredUsers, fooBaseUser0.updateUseByOthers(true),
-            fooSecondary1User0, fooSecondary2UsedByOtherApps0);
-
-        // Assert bar code paths. Note that we ignore the users during upgrade.
-        assertPackageDexUsage(packageDexUsage, ignoredUsers, barBaseUser0, barSecondary1User0);
+        record(packageDexUsage, mFooSplit2UsedByOtherApps0, mFooSplit2UsedByOtherApps0.getUsedBy());
     }
 
     private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
@@ -570,16 +605,18 @@
         String packageName = primary == null
                 ? secondaries.get(0).mPackageName
                 : primary.mPackageName;
-        boolean primaryUsedByOtherApps = primary != null && primary.mUsedByOtherApps;
+        boolean primaryUsedByOtherApps = primary != null && primary.isUsedByOtherApps();
         PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName);
 
         // Check package use info
         assertNotNull(pInfo);
         if (primary != null) {
-            assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile));
             if (users != null) {
                 assertEquals(pInfo.getLoadingPackages(primary.mDexFile), users);
+            } else if (pInfo.getLoadingPackages(primary.mDexFile) != null) {
+                assertEquals(pInfo.getLoadingPackages(primary.mDexFile), primary.getUsedBy());
             }
+            assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile));
         }
 
         Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
@@ -589,13 +626,15 @@
         for (TestData testData : secondaries) {
             DexUseInfo dInfo = dexUseInfoMap.get(testData.mDexFile);
             assertNotNull(dInfo);
-            assertEquals(testData.mUsedByOtherApps, dInfo.isUsedByOtherApps());
+            if (users != null) {
+                assertEquals(testData.mDexFile, dInfo.getLoadingPackages(), users);
+            } else {
+                assertEquals(testData.mDexFile, dInfo.getLoadingPackages(), testData.getUsedBy());
+            }
+            assertEquals(testData.isUsedByOtherApps(), dInfo.isUsedByOtherApps());
             assertEquals(testData.mOwnerUserId, dInfo.getOwnerUserId());
             assertEquals(1, dInfo.getLoaderIsas().size());
             assertTrue(dInfo.getLoaderIsas().contains(testData.mLoaderIsa));
-            if (users != null) {
-                 assertEquals(dInfo.getLoadingPackages(), users);
-            }
 
             assertEquals(testData.mClassLoaderContext, dInfo.getClassLoaderContext());
         }
@@ -603,7 +642,7 @@
 
     private boolean record(TestData testData) {
         return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile,
-               testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
+               testData.mOwnerUserId, testData.mLoaderIsa,
                testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext);
     }
 
@@ -611,7 +650,7 @@
         boolean result = true;
         for (String user : users) {
             result = result && packageDexUsage.record(testData.mPackageName, testData.mDexFile,
-                    testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
+                    testData.mOwnerUserId, testData.mLoaderIsa,
                     testData.mPrimaryOrSplit, user, testData.mClassLoaderContext);
         }
         return result;
@@ -640,37 +679,49 @@
         private final String mDexFile;
         private final int mOwnerUserId;
         private final String mLoaderIsa;
-        private final boolean mUsedByOtherApps;
         private final boolean mPrimaryOrSplit;
         private final String mUsedBy;
         private final String mClassLoaderContext;
 
         private TestData(String packageName, String dexFile, int ownerUserId,
-                String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, String usedBy) {
-            this(packageName, dexFile, ownerUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
-                    usedBy, "DefaultClassLoaderContextFor_" + dexFile);
+                String loaderIsa, boolean primaryOrSplit, String usedBy) {
+            this(packageName, dexFile, ownerUserId, loaderIsa, primaryOrSplit,
+                    usedBy, "PCL[" + dexFile + "]");
         }
         private TestData(String packageName, String dexFile, int ownerUserId,
-                String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, String usedBy,
+                String loaderIsa, boolean primaryOrSplit, String usedBy,
                 String classLoaderContext) {
             mPackageName = packageName;
             mDexFile = dexFile;
             mOwnerUserId = ownerUserId;
             mLoaderIsa = loaderIsa;
-            mUsedByOtherApps = isUsedByOtherApps;
             mPrimaryOrSplit = primaryOrSplit;
             mUsedBy = usedBy;
             mClassLoaderContext = classLoaderContext;
         }
 
         private TestData updateClassLoaderContext(String newContext) {
-            return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, mUsedByOtherApps,
+            return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa,
                     mPrimaryOrSplit, mUsedBy, newContext);
         }
 
-        private TestData updateUseByOthers(boolean newUsedByOthers) {
-            return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, newUsedByOthers,
-                mPrimaryOrSplit, mUsedBy, mClassLoaderContext);
+        private TestData updateUsedBy(String newUsedBy) {
+            return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa,
+                mPrimaryOrSplit, newUsedBy, mClassLoaderContext);
+        }
+
+        private boolean isUsedByOtherApps() {
+            return mUsedBy != null && !mPackageName.equals(mUsedBy);
+        }
+
+        private Set<String> getUsedBy() {
+            Set<String> users = new HashSet<>();
+            if ((mUsedBy != null) && (mPrimaryOrSplit || isUsedByOtherApps())) {
+                // We do not store the loading package for secondary dex files
+                // which are not used by others.
+                users.add(mUsedBy);
+            }
+            return users;
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
new file mode 100644
index 0000000..404f29c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.server.pm.parsing.library;
+
+import android.os.Build;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link ComGoogleAndroidMapsUpdater}
+ */
+@Presubmit
+@SmallTest
+@RunWith(JUnit4.class)
+public class ComGoogleAndroidMapsUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+    @Test
+    public void otherUsesLibraries() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary("other")
+                .addUsesOptionalLibrary("optional")
+                .addUsesLibrary("com.google.android.maps")
+                .hideAsParsed());
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.O)
+                .addUsesLibrary("other")
+                .addUsesOptionalLibrary("optional")
+                .hideAsParsed())
+                .hideAsFinal();
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesLibraries() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary("com.google.android.maps")
+                .hideAsParsed());
+
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed())
+                .hideAsFinal();
+
+        // No change is required because the package explicitly requests org.apache.http.legacy
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void in_usesOptionalLibraries() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesOptionalLibrary("com.google.android.maps")
+                .hideAsParsed());
+
+        AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .hideAsParsed())
+                .hideAsFinal();
+
+        // No change is required because the package explicitly requests org.apache.http.legacy
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, after);
+    }
+
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+        checkBackwardsCompatibility(before, after, ComGoogleAndroidMapsUpdater::new);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index ca38860..09c8142 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -148,6 +148,23 @@
         checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
     }
 
+    /**
+     * Ensures that the {@link PackageBackwardCompatibility} uses a
+     * {@link ComGoogleAndroidMapsUpdater}.
+     */
+    @Test
+    public void com_google_android_maps_in_usesLibraries() {
+        ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+                .addUsesLibrary("com.google.android.maps")
+                .hideAsParsed());
+
+        ParsingPackage after = PackageImpl.forTesting(PACKAGE_NAME)
+                .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
+
+        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
+    }
+
     private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
         checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
     }
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index eef9012..10981ab 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.systemconfig;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 
 import android.platform.test.annotations.Presubmit;
@@ -164,7 +166,7 @@
 
         mSysConfig.readPermissions(folder, /* No permission needed anyway */ 0);
 
-        final ArrayMap<String, Boolean>  packageOneExpected = new ArrayMap<>();
+        final ArrayMap<String, Boolean> packageOneExpected = new ArrayMap<>();
         packageOneExpected.put("com.android.package1.Full", true);
         packageOneExpected.put("com.android.package1.Relative", false);
 
@@ -181,7 +183,47 @@
     }
 
     /**
+     * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_APP_CONFIGS}
+     * permission flag for the tag: whitelisted-staged-installer.
+     */
+    @Test
+    public void readPermissions_allowAppConfigs_parsesStagedInstallerWhitelist()
+            throws IOException {
+        final String contents =
+                "<config>\n"
+                + "    <whitelisted-staged-installer package=\"com.android.package1\" />\n"
+                + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "staged-installer-whitelist.xml", contents);
+
+        mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getWhitelistedStagedInstallers())
+                .containsExactly("com.android.package1");
+    }
+
+    /**
+     * Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_APP_CONFIGS}
+     * permission flag for the tag: whitelisted-staged-installer.
+     */
+    @Test
+    public void readPermissions_notAllowAppConfigs_wontParseStagedInstallerWhitelist()
+            throws IOException {
+        final String contents =
+                "<config>\n"
+                + "    <whitelisted-staged-installer package=\"com.android.package1\" />\n"
+                + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "staged-installer-whitelist.xml", contents);
+
+        mSysConfig.readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08);
+
+        assertThat(mSysConfig.getWhitelistedStagedInstallers()).isEmpty();
+    }
+
+    /**
      * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
+     *
      * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
      * @return the folder
      */
@@ -194,7 +236,8 @@
 
     /**
      * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
-     * @param folder pre-existing subdirectory of mTemporaryFolder to put the file
+     *
+     * @param folder   pre-existing subdirectory of mTemporaryFolder to put the file
      * @param fileName name of the file (e.g. filename.xml) to create
      * @param contents contents to write to the file
      * @return the folder containing the newly created file (not the file itself!)
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index 47ad831..a23ade6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -18,7 +18,6 @@
 
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
@@ -77,8 +76,8 @@
 
     @Test
     public void testInvalidShortcutFlagEnabled_looksUpCorrectChannel() {
-        Settings.Global.putString(
-                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0);
 
         NotificationChannelExtractor extractor = new NotificationChannelExtractor();
         extractor.setConfig(mConfig);
@@ -97,7 +96,8 @@
         NotificationChannel updatedChannel =
                 new NotificationChannel("a", "", IMPORTANCE_HIGH);
         when(mConfig.getConversationNotificationChannel(
-                any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId(mContext)), eq(true), eq(false)))
+                any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId(mContext)),
+                eq(true), eq(false)))
                 .thenReturn(updatedChannel);
 
         assertNull(extractor.process(r));
@@ -106,8 +106,8 @@
 
     @Test
     public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() {
-        Settings.Global.putString(
-                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
 
         NotificationChannelExtractor extractor = new NotificationChannelExtractor();
         extractor.setConfig(mConfig);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index f9596b5..15220e1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4791,7 +4791,7 @@
         // enqueue toast -> no toasts enqueued
         ((INotificationManager) mService.mService).enqueueTextToast(testPackage, new Binder(),
                 "Text", 2000, 0, null);
-        verify(mStatusBar).showToast(any(), any(), any(), any(), anyInt(), any());
+        verify(mStatusBar).showToast(anyInt(), any(), any(), any(), any(), anyInt(), any());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 00b9273..3139bfa 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -63,7 +63,6 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.StatusBarNotification;
-import android.util.FeatureFlagUtils;
 import android.widget.RemoteViews;
 
 import androidx.test.filters.SmallTest;
@@ -124,8 +123,8 @@
         when(mMockContext.getResources()).thenReturn(getContext().getResources());
         when(mMockContext.getPackageManager()).thenReturn(mPm);
         when(mMockContext.getContentResolver()).thenReturn(mContentResolver);
-        Settings.Global.putString(mContentResolver,
-                FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
         ApplicationInfo appInfo = new ApplicationInfo();
         appInfo.targetSdkVersion = Build.VERSION_CODES.O;
         when(mMockContext.getApplicationInfo()).thenReturn(appInfo);
@@ -1138,8 +1137,8 @@
 
     @Test
     public void testIsConversation_bypassShortcutFlagEnabled() {
-        Settings.Global.putString(mContentResolver,
-                FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0);
         StatusBarNotification sbn = getMessagingStyleNotification();
         NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
         record.setShortcutInfo(null);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index af60511..ed5ec6a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -22,7 +22,6 @@
 import static android.app.NotificationManager.IMPORTANCE_MAX;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ;
 
 import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
 
@@ -2922,9 +2921,10 @@
     }
 
     @Test
-    public void testPlaceholderConversationId_flagOn() throws Exception {
-        Settings.Global.putString(
-                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+    public void testPlaceholderConversationId_shortcutNotRequired() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 0);
+
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
 
         final String xml = "<ranking version=\"1\">\n"
@@ -2942,9 +2942,9 @@
     }
 
     @Test
-    public void testPlaceholderConversationId_flagOff() throws Exception {
-        Settings.Global.putString(
-                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+    public void testPlaceholderConversationId_shortcutRequired() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
 
         final String xml = "<ranking version=\"1\">\n"
@@ -2962,9 +2962,9 @@
     }
 
     @Test
-    public void testNormalConversationId_flagOff() throws Exception {
-        Settings.Global.putString(
-                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+    public void testNormalConversationId_shortcutRequired() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
 
         final String xml = "<ranking version=\"1\">\n"
@@ -2982,9 +2982,9 @@
     }
 
     @Test
-    public void testNoConversationId_flagOff() throws Exception {
-        Settings.Global.putString(
-                mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+    public void testNoConversationId_shortcutRequired() throws Exception {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.REQUIRE_SHORTCUTS_FOR_CONVERSATIONS, 1);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger);
 
         final String xml = "<ranking version=\"1\">\n"
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 12bdec6..0568be8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -22,6 +22,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
 
 import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
 import static com.android.server.wm.DisplayArea.Type.ANY;
@@ -72,7 +73,11 @@
         WindowManagerService wms = mSystemServices.getWindowManagerService();
         DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms);
         DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime");
-        DisplayArea<ActivityStack> tasks = new DisplayArea<>(wms, ANY, "Tasks");
+        DisplayContent displayContent = mock(DisplayContent.class);
+        TaskDisplayArea taskDisplayArea = new TaskDisplayArea(displayContent, wms, "Tasks",
+                FEATURE_DEFAULT_TASK_CONTAINER);
+        List<TaskDisplayArea> taskDisplayAreaList = new ArrayList<>();
+        taskDisplayAreaList.add(taskDisplayArea);
 
         final Feature foo;
         final Feature bar;
@@ -86,7 +91,7 @@
                         .all()
                         .except(TYPE_STATUS_BAR)
                         .build())
-                .build(wms, mock(DisplayContent.class), root, ime, tasks);
+                .build(wms, displayContent, root, ime, taskDisplayAreaList);
 
         policy.attachDisplayAreas();
 
@@ -98,9 +103,9 @@
         assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)),
                 is(not(decendantOfOneOf(policy.getDisplayAreas(bar)))));
 
-        assertThat(tasks,
+        assertThat(taskDisplayArea,
                 is(decendantOfOneOf(policy.getDisplayAreas(foo))));
-        assertThat(tasks,
+        assertThat(taskDisplayArea,
                 is(decendantOfOneOf(policy.getDisplayAreas(bar))));
 
         assertThat(ime,
@@ -109,7 +114,8 @@
                 is(decendantOfOneOf(policy.getDisplayAreas(bar))));
 
         List<DisplayArea<?>> actualOrder = collectLeafAreas(root);
-        Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, tasks);
+        Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime,
+                taskDisplayArea);
         actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList());
 
         Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
index 4e4627b..6834ee5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -77,8 +77,7 @@
 
         @Override
         public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
-                DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer,
-                TaskDisplayArea taskDisplayArea) {
+                DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer) {
             throw new RuntimeException("test stub");
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 618e608..880c486 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -54,17 +54,6 @@
     }
 
     @Test
-    public void testDisplayArea_positionChanged_throwsIfIncompatibleSibling() {
-        WindowManagerService wms = mWmsRule.getWindowManagerService();
-        DisplayArea<WindowContainer> parent = new SurfacelessDisplayArea<>(wms, ANY, "Parent");
-        DisplayArea<WindowContainer> child1 = new DisplayArea<>(wms, ANY, "Child1");
-        DisplayArea<WindowContainer> child2 = new DisplayArea<>(wms, ANY, "Child2");
-
-        parent.addChild(child1, 0);
-        assertThrows(IllegalStateException.class, () -> parent.addChild(child2, 0));
-    }
-
-    @Test
     public void testType_typeOf() {
         WindowManagerService wms = mWmsRule.getWindowManagerService();
 
@@ -87,10 +76,10 @@
         checkSiblings(BELOW_TASKS, ABOVE_TASKS);
         checkSiblings(ANY, ABOVE_TASKS);
         checkSiblings(ABOVE_TASKS, ABOVE_TASKS);
+        checkSiblings(ANY, ANY);
 
         assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS));
         assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY));
-        assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, ANY));
         assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index 89bc65b..b6eb901 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -30,7 +30,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
-import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -41,7 +40,6 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
 
 import android.platform.test.annotations.Presubmit;
 import android.util.IntArray;
@@ -49,7 +47,6 @@
 import android.view.InsetsState;
 import android.view.test.InsetsModeSession;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.AfterClass;
@@ -168,6 +165,18 @@
     }
 
     @Test
+    public void testControlsForDispatch_forceShowSystemBarsFromExternal_appHasNoControl() {
+        mDisplayContent.getDisplayPolicy().setForceShowSystemBars(true);
+        addWindow(TYPE_STATUS_BAR, "statusBar");
+        addWindow(TYPE_NAVIGATION_BAR, "navBar");
+
+        final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
+
+        // The focused app window cannot control system bars.
+        assertNull(controls);
+    }
+
+    @Test
     public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() {
         addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
                 .getControllableInsetProvider().getSource().setVisible(false);
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 add4e9c..ea933df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1124,8 +1124,6 @@
             }
         });
         assertSecurityException(expectCallable,
-                () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
-        assertSecurityException(expectCallable,
                 () -> mService.startActivityFromRecents(0, new Bundle()));
         assertSecurityException(expectCallable, () -> mService.getTaskSnapshot(0, true));
         assertSecurityException(expectCallable, () -> mService.registerTaskStackListener(null));
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 6d2b7b1..f19550c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -23,7 +23,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
@@ -318,7 +317,7 @@
         // Assume activity transition should animate when no
         // IRecentsAnimationController#setDeferCancelUntilNextTransition called.
         assertFalse(mController.shouldDeferCancelWithScreenshot());
-        assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE));
+        assertTrue(activity.shouldAnimate());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 19824bf7..a708533 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -16,8 +16,18 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 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.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -27,6 +37,9 @@
 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.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 
 import android.platform.test.annotations.Presubmit;
 
@@ -38,10 +51,10 @@
 import org.junit.runner.RunWith;
 
 /**
- * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
+ * Tests for the {@link TaskDisplayArea} container.
  *
  * Build/Install/Run:
- *  atest WmTests:TaskStackContainersTests
+ *  atest WmTests:TaskDisplayAreaTests
  */
 @SmallTest
 @Presubmit
@@ -134,4 +147,49 @@
         assertEquals("The testing DisplayContent should be moved to top with task",
                 mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack);
     }
+
+    @Test
+    public void testReuseTaskAsStack() {
+        final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final Task newStack = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
+        doReturn(newStack).when(taskDisplayArea).createStack(anyInt(), anyInt(), anyBoolean(),
+                any(), any(), anyBoolean());
+
+        final int type = ACTIVITY_TYPE_STANDARD;
+        assertGetOrCreateStack(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
+                true /* reuseCandidate */);
+        assertGetOrCreateStack(WINDOWING_MODE_UNDEFINED, type, candidateTask,
+                true /* reuseCandidate */);
+        assertGetOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, type, candidateTask,
+                true /* reuseCandidate */);
+        assertGetOrCreateStack(WINDOWING_MODE_FREEFORM, type, candidateTask,
+                true /* reuseCandidate */);
+        assertGetOrCreateStack(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask,
+                true /* reuseCandidate */);
+        assertGetOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, type, candidateTask,
+                false /* reuseCandidate */);
+        assertGetOrCreateStack(WINDOWING_MODE_PINNED, type, candidateTask,
+                false /* reuseCandidate */);
+
+        final int windowingMode = WINDOWING_MODE_FULLSCREEN;
+        assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_HOME, candidateTask,
+                false /* reuseCandidate */);
+        assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_RECENTS, candidateTask,
+                false /* reuseCandidate */);
+        assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_ASSISTANT, candidateTask,
+                false /* reuseCandidate */);
+        assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_DREAM, candidateTask,
+                false /* reuseCandidate */);
+    }
+
+    private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask,
+            boolean reuseCandidate) {
+        final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
+        final ActivityStack stack = taskDisplayArea.getOrCreateStack(windowingMode, activityType,
+                false /* onTop */, null /* intent */, candidateTask /* candidateTask */);
+        assertEquals(reuseCandidate, stack == candidateTask);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 62d311a..06ca6c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -69,8 +69,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -119,13 +117,6 @@
         return createTaskStackOnDisplay(mDisplayContent);
     }
 
-    @Before
-    public void setUp() {
-        // We defer callbacks since we need to adjust task surface visibility, but for these tests,
-        // just run the callbacks synchronously
-        mWm.mAtmService.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer((r) -> r.run());
-    }
-
     @Test
     public void testAppearVanish() throws RemoteException {
         final ActivityStack stack = createStack();
@@ -201,6 +192,21 @@
     }
 
     @Test
+    public void testTaskNoDraw() throws RemoteException {
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack, false /* fakeDraw */);
+        final ITaskOrganizer organizer = registerMockOrganizer();
+
+        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        verify(organizer, never()).onTaskAppeared(any());
+        assertTrue(stack.isOrganized());
+
+        mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer);
+        verify(organizer, never()).onTaskVanished(any());
+        assertFalse(stack.isOrganized());
+    }
+
+    @Test
     public void testClearOrganizer() throws RemoteException {
         final ActivityStack stack = createStack();
         final Task task = createTask(stack);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index d48e827..413ae13 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -33,6 +33,8 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 
 import android.app.WindowConfiguration;
@@ -144,7 +146,7 @@
 
         // Stack removal is deferred if one of its child is animating.
         doReturn(true).when(stack).hasWindowsAlive();
-        doReturn(true).when(task).isAnimating(TRANSITION | CHILDREN);
+        doReturn(true).when(task).isAnimating(eq(TRANSITION | CHILDREN), anyInt());
 
         stack.removeIfPossible();
         // For the case of deferred removal the task controller will still be connected to the its
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index 18737c2..57c7504 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -43,7 +43,9 @@
         // hard-code to FULLSCREEN for tests.
         setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         spyOn(this);
-        spyOn(mTaskContainers);
+        for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
+            spyOn(getTaskDisplayAreaAt(i));
+        }
 
         final DisplayRotation displayRotation = getDisplayRotation();
         spyOn(displayRotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 118c2e4..353c781 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -21,6 +21,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -35,6 +36,7 @@
 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.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -423,6 +425,35 @@
     }
 
     @Test
+    public void testIsAnimating_typesToCheck() {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
+        final TestWindowContainer window = builder.setIsAnimating(true).setLayer(0).build();
+
+        assertTrue(window.isAnimating());
+        assertFalse(window.isAnimating(0, ANIMATION_TYPE_SCREEN_ROTATION));
+        assertTrue(window.isAnimating(0, ANIMATION_TYPE_APP_TRANSITION));
+        assertFalse(window.isAnimatingExcluding(0, ANIMATION_TYPE_APP_TRANSITION));
+
+        final TestWindowContainer child = window.addChildWindow();
+        assertFalse(child.isAnimating());
+        assertTrue(child.isAnimating(PARENTS));
+        assertTrue(child.isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION));
+        assertFalse(child.isAnimating(PARENTS, ANIMATION_TYPE_SCREEN_ROTATION));
+
+        final WindowState windowState = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
+                mDisplayContent, "TestWindowState");
+        WindowContainer parent = windowState.getParent();
+        spyOn(windowState.mSurfaceAnimator);
+        doReturn(true).when(windowState.mSurfaceAnimator).isAnimating();
+        doReturn(ANIMATION_TYPE_APP_TRANSITION).when(
+                windowState.mSurfaceAnimator).getAnimationType();
+        assertTrue(parent.isAnimating(CHILDREN));
+
+        windowState.setControllableInsetProvider(mock(InsetsSourceProvider.class));
+        assertFalse(parent.isAnimating(CHILDREN));
+    }
+
+    @Test
     public void testIsVisible() {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
@@ -895,6 +926,7 @@
             mWaitForTransitStart = waitTransitStart;
             spyOn(mSurfaceAnimator);
             doReturn(mIsAnimating).when(mSurfaceAnimator).isAnimating();
+            doReturn(ANIMATION_TYPE_APP_TRANSITION).when(mSurfaceAnimator).getAnimationType();
         }
 
         TestWindowContainer getParentWindow() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 820d381..71b35b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -40,6 +40,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
@@ -47,6 +48,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertEquals;
@@ -55,6 +58,7 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -65,6 +69,7 @@
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.util.Size;
 import android.view.DisplayCutout;
@@ -568,6 +573,36 @@
     }
 
     @Test
+    public void testReportResizedWithRemoteException() {
+        final WindowState win = mChildAppWindowAbove;
+        makeWindowVisible(win, win.getParentWindow());
+        win.mLayoutSeq = win.getDisplayContent().mLayoutSeq;
+        win.updateResizingWindowIfNeeded();
+
+        assertThat(mWm.mResizingWindows).contains(win);
+        assertTrue(win.getOrientationChanging());
+
+        mWm.mResizingWindows.remove(win);
+        spyOn(win.mClient);
+        try {
+            doThrow(new RemoteException("test")).when(win.mClient).resized(any() /* frame */,
+                    any() /* contentInsets */, any() /* visibleInsets */, any() /* stableInsets */,
+                    anyBoolean() /* reportDraw */, any() /* mergedConfig */,
+                    any() /* backDropFrame */, anyBoolean() /* forceLayout */,
+                    anyBoolean() /* alwaysConsumeSystemBars */, anyInt() /* displayId */,
+                    any() /* displayCutout */);
+        } catch (RemoteException ignored) {
+        }
+        win.reportResized();
+        win.updateResizingWindowIfNeeded();
+
+        // Even "resized" throws remote exception, it is still considered as reported. So the window
+        // shouldn't be resized again (which may block unfreeze in real case).
+        assertThat(mWm.mResizingWindows).doesNotContain(win);
+        assertFalse(win.getOrientationChanging());
+    }
+
+    @Test
     public void testGetTransformationMatrix() {
         final int PARENT_WINDOW_OFFSET = 1;
         final int DISPLAY_IN_PARENT_WINDOW_OFFSET = 2;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 1ca2e318..397f73c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -134,6 +134,7 @@
                 mChildAppWindowBelow = createCommonWindow(mAppWindow,
                         TYPE_APPLICATION_MEDIA_OVERLAY,
                         "mChildAppWindowBelow");
+                mDisplayContent.getDisplayPolicy().setForceShowSystemBars(false);
             }
 
             // Adding a display will cause freezing the display. Make sure to wait until it's
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 6c0f2b0..edbdd4e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -40,6 +40,9 @@
 import android.hardware.soundtrigger.SoundTriggerModule;
 import android.os.Binder;
 import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.RemoteException;
@@ -113,6 +116,13 @@
 
     private PowerSaveModeListener mPowerSaveModeListener;
 
+    // Handler to process call state changes will delay to allow time for the audio
+    // and sound trigger HALs to process the end of call notifications
+    // before we re enable pending recognition requests.
+    private final Handler mHandler;
+    private static final int MSG_CALL_STATE_CHANGED = 0;
+    private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000;
+
     SoundTriggerHelper(Context context) {
         ArrayList <ModuleProperties> modules = new ArrayList<>();
         int status = SoundTrigger.listModules(modules);
@@ -130,6 +140,31 @@
             // TODO: Figure out how to determine which module corresponds to the DSP hardware.
             mModuleProperties = modules.get(0);
         }
+
+        Looper looper = Looper.myLooper();
+        if (looper == null) {
+            looper = Looper.getMainLooper();
+        }
+        if (looper != null) {
+            mHandler = new Handler(looper) {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case MSG_CALL_STATE_CHANGED:
+                            synchronized (mLock) {
+                                onCallStateChangedLocked(
+                                        TelephonyManager.CALL_STATE_OFFHOOK == msg.arg1);
+                            }
+                            break;
+                        default:
+                            Slog.e(TAG, "unknown message in handler:" + msg.what);
+                            break;
+                    }
+                }
+            };
+        } else {
+            mHandler = null;
+        }
     }
 
     /**
@@ -227,6 +262,37 @@
         return status;
     }
 
+    private int prepareForRecognition(ModelData modelData) {
+        if (mModule == null) {
+            mModule = SoundTrigger.attachModule(mModuleProperties.getId(), this, null);
+            if (mModule == null) {
+                Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module");
+                return STATUS_ERROR;
+            }
+        }
+        // Load the model if it is not loaded.
+        if (!modelData.isModelLoaded()) {
+            // Before we try and load this model, we should first make sure that any other
+            // models that don't have an active recognition/dead callback are unloaded. Since
+            // there is a finite limit on the number of models that the hardware may be able to
+            // have loaded, we want to make sure there's room for our model.
+            stopAndUnloadDeadModelsLocked();
+            int[] handle = new int[] { 0 };
+            int status = mModule.loadSoundModel(modelData.getSoundModel(), handle);
+            if (status != SoundTrigger.STATUS_OK) {
+                Slog.w(TAG, "prepareForRecognition: loadSoundModel failed with status: " + status);
+                return status;
+            }
+            modelData.setHandle(handle[0]);
+            modelData.setLoaded();
+            if (DBG) {
+                Slog.d(TAG, "prepareForRecognition: Sound model loaded with handle:" + handle[0]);
+            }
+        }
+        return STATUS_OK;
+    }
+
+
     /**
      * Starts recognition for the given sound model. A single routine for both keyphrase and
      * generic sound models.
@@ -248,12 +314,17 @@
                 Slog.w(TAG, "Attempting startRecognition without the capability");
                 return STATUS_ERROR;
             }
-            if (mModule == null) {
-                mModule = SoundTrigger.attachModule(mModuleProperties.getId(), this, null);
-                if (mModule == null) {
-                    Slog.w(TAG, "startRecognition cannot attach to sound trigger module");
-                    return STATUS_ERROR;
+
+            IRecognitionStatusCallback oldCallback = modelData.getCallback();
+            if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) {
+                Slog.w(TAG, "Canceling previous recognition for model id: "
+                        + modelData.getModelId());
+                try {
+                    oldCallback.onError(STATUS_ERROR);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "RemoteException in onDetectionStopped", e);
                 }
+                modelData.clearCallback();
             }
 
             // If the existing SoundModel is different (for the same UUID for Generic and same
@@ -287,48 +358,25 @@
                 }
             }
 
-            IRecognitionStatusCallback oldCallback = modelData.getCallback();
-            if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) {
-                Slog.w(TAG, "Canceling previous recognition for model id: " +
-                        modelData.getModelId());
-                try {
-                    oldCallback.onError(STATUS_ERROR);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "RemoteException in onDetectionStopped", e);
-                }
-                modelData.clearCallback();
-            }
-
-            // Load the model if it is not loaded.
-            if (!modelData.isModelLoaded()) {
-                // Before we try and load this model, we should first make sure that any other
-                // models that don't have an active recognition/dead callback are unloaded. Since
-                // there is a finite limit on the number of models that the hardware may be able to
-                // have loaded, we want to make sure there's room for our model.
-                stopAndUnloadDeadModelsLocked();
-                int[] handle = new int[] { INVALID_VALUE };
-                int status = mModule.loadSoundModel(soundModel, handle);
-                if (status != SoundTrigger.STATUS_OK) {
-                    Slog.w(TAG, "loadSoundModel call failed with " + status);
-                    return status;
-                }
-                if (handle[0] == INVALID_VALUE) {
-                    Slog.w(TAG, "loadSoundModel call returned invalid sound model handle");
-                    return STATUS_ERROR;
-                }
-                modelData.setHandle(handle[0]);
-                modelData.setLoaded();
-                Slog.d(TAG, "Sound model loaded with handle:" + handle[0]);
-            }
             modelData.setCallback(callback);
             modelData.setRequested(true);
             modelData.setRecognitionConfig(recognitionConfig);
             modelData.setSoundModel(soundModel);
 
-            int status = startRecognitionLocked(modelData,
+            if (!isRecognitionAllowed()) {
+                initializeTelephonyAndPowerStateListeners();
+                return STATUS_OK;
+            }
+
+            int status = prepareForRecognition(modelData);
+            if (status != STATUS_OK) {
+                Slog.w(TAG, "startRecognition failed to prepare model for recognition");
+                return status;
+            }
+            status = startRecognitionLocked(modelData,
                     false /* Don't notify for synchronous calls */);
 
-                                // Initialize power save, call active state monitoring logic.
+            // Initialize power save, call active state monitoring logic.
             if (status == STATUS_OK) {
                 initializeTelephonyAndPowerStateListeners();
             }
@@ -398,7 +446,8 @@
             if (DBG) {
                 Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId + ", callback =" +
                         callback.asBinder());
-                Slog.d(TAG, "current callback=" + (modelData == null ? "null" :
+                Slog.d(TAG, "current callback="
+                        + ((modelData == null || modelData.getCallback() == null) ? "null" :
                             modelData.getCallback().asBinder()));
             }
             int status = stopRecognition(modelData, callback);
@@ -507,8 +556,8 @@
         synchronized (mLock) {
             MetricsLogger.count(mContext, "sth_unload_keyphrase_sound_model", 1);
             ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
-            if (mModule == null || modelData == null || modelData.getHandle() == INVALID_VALUE ||
-                    !modelData.isKeyphraseModel()) {
+            if (mModule == null || modelData == null || !modelData.isModelLoaded()
+                    || !modelData.isKeyphraseModel()) {
                 return STATUS_ERROR;
             }
 
@@ -943,6 +992,10 @@
             return STATUS_OK;
         }
         if (start) {
+            int status = prepareForRecognition(model);
+            if (status != STATUS_OK) {
+                return status;
+            }
             return startRecognitionLocked(model, notify);
         } else {
             return stopRecognitionLocked(model, notify);
@@ -992,8 +1045,15 @@
         @Override
         public void onCallStateChanged(int state, String arg1) {
             if (DBG) Slog.d(TAG, "onCallStateChanged: " + state);
-            synchronized (mLock) {
-                onCallStateChangedLocked(TelephonyManager.CALL_STATE_OFFHOOK == state);
+
+            if (mHandler != null) {
+                synchronized (mLock) {
+                    mHandler.removeMessages(MSG_CALL_STATE_CHANGED);
+                    Message msg = mHandler.obtainMessage(MSG_CALL_STATE_CHANGED, state, 0);
+                    mHandler.sendMessageDelayed(
+                            msg, (TelephonyManager.CALL_STATE_OFFHOOK == state) ? 0
+                                    : CALL_INACTIVE_MSG_DELAY_MS);
+                }
             }
         }
     }
@@ -1216,9 +1276,8 @@
     // models.
     private int startRecognitionLocked(ModelData modelData, boolean notify) {
         IRecognitionStatusCallback callback = modelData.getCallback();
-        int handle = modelData.getHandle();
         RecognitionConfig config = modelData.getRecognitionConfig();
-        if (callback == null || handle == INVALID_VALUE || config == null) {
+        if (callback == null || !modelData.isModelLoaded() || config == null) {
             // Nothing to do here.
             Slog.w(TAG, "startRecognition: Bad data passed in.");
             MetricsLogger.count(mContext, "sth_start_recognition_error", 1);
@@ -1235,7 +1294,7 @@
         if (mModule == null) {
             return STATUS_ERROR;
         }
-        int status = mModule.startRecognition(handle, config);
+        int status = mModule.startRecognition(modelData.getHandle(), config);
         if (status != SoundTrigger.STATUS_OK) {
             Slog.w(TAG, "startRecognition failed with " + status);
             MetricsLogger.count(mContext, "sth_start_recognition_error", 1);
@@ -1376,7 +1435,7 @@
 
         // Model handle is an integer used by the HAL as an identifier for sound
         // models.
-        private int mModelHandle = INVALID_VALUE;
+        private int mModelHandle;
 
         // The SoundModel instance, one of KeyphraseSoundModel or GenericSoundModel.
         private SoundModel mSoundModel = null;
@@ -1436,7 +1495,6 @@
 
         synchronized void clearState() {
             mModelState = MODEL_NOTLOADED;
-            mModelHandle = INVALID_VALUE;
             mRecognitionConfig = null;
             mRequested = false;
             mCallback = null;
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index d5851d8..0c25cfb 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -34,18 +34,21 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.pm.BackgroundDexOptService;
 import com.android.server.wm.ActivityMetricsLaunchObserver;
 import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
 import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
 import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
+import java.util.ArrayList;
 import java.util.concurrent.TimeUnit;
 import java.util.HashMap;
 
@@ -286,6 +289,7 @@
 
     private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver();
     private final EventSequenceValidator mEventSequenceValidator = new EventSequenceValidator();
+    private final DexOptPackagesUpdated mDexOptPackagesUpdated = new DexOptPackagesUpdated();
     private boolean mRegisteredListeners = false;
 
     private void registerInProcessListenersLocked() {
@@ -308,9 +312,22 @@
         launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver);
         launchObserverRegistry.registerLaunchObserver(mEventSequenceValidator);
 
+        BackgroundDexOptService.addPackagesUpdatedListener(mDexOptPackagesUpdated);
+
+
         mRegisteredListeners = true;
     }
 
+    private class DexOptPackagesUpdated implements BackgroundDexOptService.PackagesUpdatedListener {
+        @Override
+        public void onPackagesUpdated(ArraySet<String> updatedPackages) {
+            String[] updated = updatedPackages.toArray(new String[0]);
+            for (String packageName : updated) {
+                Log.d(TAG, "onPackagesUpdated: " + packageName);
+            }
+        }
+    }
+
     private class AppLaunchObserver implements ActivityMetricsLaunchObserver {
         // We add a synthetic sequence ID here to make it easier to differentiate new
         // launch sequences on the native side.
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index f3e9de0..3048ad7 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -311,7 +311,7 @@
         }
         // If the user or profile is current, permission is granted.
         // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
-        return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid);
+        return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, pid, uid);
     }
 
     private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 327e8b3..56f3c3e 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1147,6 +1147,21 @@
             "support_ims_conference_event_package_bool";
 
     /**
+     * Determines whether processing of conference event package data received on a device other
+     * than the conference host is supported.
+     * <p>
+     * When a device A merges calls B and C into a conference it is considered the conference host
+     * and B and C are considered the conference peers.
+     * <p>
+     * When {@code true}, the conference peer will display the conference state if it receives
+     * conference event package data from the network.  When {@code false}, the conference peer will
+     * ignore conference event package data received from the network.
+     * @hide
+     */
+    public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL =
+            "support_ims_conference_event_package_on_peer_bool";
+
+    /**
      * Determines whether High Definition audio property is displayed in the dialer UI.
      * If {@code false}, remove the HD audio property from the connection so that HD audio related
      * UI is not displayed. If {@code true}, keep HD audio property as it is configured.
@@ -1163,6 +1178,25 @@
             "support_ims_conference_call_bool";
 
     /**
+     * Determines whether the device will locally disconnect an IMS conference when the participant
+     * count drops to zero.  When {@code true}, it is assumed the carrier does NOT disconnect a
+     * conference when the participant count drops to zero and that the device must do this by
+     * disconnecting the conference locally.  When {@code false}, it is assumed that the carrier
+     * is responsible for disconnecting the conference when there are no longer any participants
+     * present.
+     * <p>
+     * Note: both {@link #KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL} and
+     * {@link #KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL} must be true for this configuration to
+     * have any effect.
+     * <p>
+     * Defaults to {@code false}, meaning the carrier network is responsible for disconnecting an
+     * empty IMS conference.
+     * @hide
+     */
+    public static final String KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL =
+            "local_disconnect_empty_ims_conference_bool";
+
+    /**
      * Determines whether video conference calls are supported by a carrier.  When {@code true},
      * video calls can be merged into conference calls, {@code false} otherwiwse.
      * <p>
@@ -3778,8 +3812,10 @@
         sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true);
+        sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false);
         sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5);
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 2b1d9e5..18e2592 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -113,6 +113,7 @@
     public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
     public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
     public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 55;
+    public static final int EVENT_SIM_STATE_UPDATED = BASE + 56;
 
     /***** Constants *****/
 
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index 2482068..02c75ed 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -16,7 +16,7 @@
     name: "ApkVerityTest",
     srcs: ["src/**/*.java"],
     libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
-    test_suites: ["general-tests", "vts-core"],
+    test_suites: ["general-tests", "vts"],
     target_required: [
         "block_device_writer_module",
     ],
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index 2760fe8..37fbc29 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -48,6 +48,6 @@
     },
 
     auto_gen_config: false,
-    test_suites: ["general-tests", "pts", "vts-core"],
+    test_suites: ["general-tests", "pts", "vts"],
     gtest: false,
 }
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index 548af0c..498cb7c 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -18,7 +18,6 @@
 
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
-
 import static java.lang.Math.max;
 import static java.lang.Math.min;
 
@@ -31,6 +30,7 @@
 import android.graphics.Insets;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -44,11 +44,11 @@
 import android.view.animation.LinearInterpolator;
 import android.widget.LinearLayout;
 
-import androidx.appcompat.app.AppCompatActivity;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.appcompat.app.AppCompatActivity;
+
 public class WindowInsetsActivity extends AppCompatActivity {
 
     private View mRoot;
@@ -191,6 +191,40 @@
                 mTransitions.forEach(it -> it.onFinish(animation));
             }
         });
+
+        findViewById(R.id.floating_action_button).setOnClickListener(
+                v -> v.getWindowInsetsController().controlWindowInsetsAnimation(ime(), -1,
+                new LinearInterpolator(), null /* cancellationSignal */,
+                new WindowInsetsAnimationControlListener() {
+                    @Override
+                    public void onReady(
+                            WindowInsetsAnimationController controller,
+                            int types) {
+                        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+                        anim.setDuration(1500);
+                        anim.addUpdateListener(animation
+                                -> controller.setInsetsAndAlpha(
+                                controller.getShownStateInsets(),
+                                (float) animation.getAnimatedValue(),
+                                anim.getAnimatedFraction()));
+                        anim.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                super.onAnimationEnd(animation);
+                                controller.finish(true);
+                            }
+                        });
+                        anim.start();
+                    }
+
+                    @Override
+                    public void onCancelled(WindowInsetsAnimationController controller) {
+                    }
+
+                    @Override
+                    public void onFinished(WindowInsetsAnimationController controller) {
+                    }
+                }));
     }
 
     @Override
@@ -200,57 +234,6 @@
         getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false));
     }
 
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        getWindow().getInsetsController().addOnControllableInsetsChangedListener(
-                new OnControllableInsetsChangedListener() {
-
-                    boolean hasControl = false;
-                    @Override
-                    public void onControllableInsetsChanged(WindowInsetsController controller,
-                            int types) {
-                        if ((types & ime()) != 0 && !hasControl) {
-                            hasControl = true;
-                            controller.controlWindowInsetsAnimation(ime(), -1,
-                                    new LinearInterpolator(), null /* cancellationSignal */,
-                                    new WindowInsetsAnimationControlListener() {
-                                        @Override
-                                        public void onReady(
-                                                WindowInsetsAnimationController controller,
-                                                int types) {
-                                            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-                                            anim.setDuration(1500);
-                                            anim.addUpdateListener(animation
-                                                    -> controller.setInsetsAndAlpha(
-                                                    controller.getShownStateInsets(),
-                                                    (float) animation.getAnimatedValue(),
-                                                    anim.getAnimatedFraction()));
-                                            anim.addListener(new AnimatorListenerAdapter() {
-                                                @Override
-                                                public void onAnimationEnd(Animator animation) {
-                                                    super.onAnimationEnd(animation);
-                                                    controller.finish(true);
-                                                }
-                                            });
-                                            anim.start();
-                                        }
-
-                                        @Override
-                                        public void onFinished(
-                                                WindowInsetsAnimationController controller) {
-                                        }
-
-                                        @Override
-                                        public void onCancelled(
-                                                WindowInsetsAnimationController controller) {
-                                        }
-                                    });
-                        }
-                    }
-                });
-    }
-
     static class Transition {
         private int mEndBottom;
         private int mStartBottom;
diff --git a/tests/net/common/java/android/net/DependenciesTest.java b/tests/net/common/java/android/net/DependenciesTest.java
new file mode 100644
index 0000000..ac1c28a
--- /dev/null
+++ b/tests/net/common/java/android/net/DependenciesTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.net;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A simple class that tests dependencies to java standard tools from the
+ * Network stack. These tests are not meant to be comprehensive tests of
+ * the relevant APIs : such tests belong in the relevant test suite for
+ * these dependencies. Instead, this just makes sure coverage is present
+ * by calling the methods in the exact way (or a representative way of how)
+ * they are called in the network stack.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DependenciesTest {
+    // Used to in ipmemorystore's RegularMaintenanceJobService to convert
+    // 24 hours into seconds
+    @Test
+    public void testTimeUnit() {
+        final int hours = 24;
+        final long inSeconds = TimeUnit.HOURS.toMillis(hours);
+        assertEquals(inSeconds, hours * 60 * 60 * 1000);
+    }
+
+    private byte[] makeTrivialArray(final int size) {
+        final byte[] src = new byte[size];
+        for (int i = 0; i < size; ++i) {
+            src[i] = (byte) i;
+        }
+        return src;
+    }
+
+    // Used in ApfFilter to find an IP address from a byte array
+    @Test
+    public void testArrays() {
+        final int size = 128;
+        final byte[] src = makeTrivialArray(size);
+
+        // Test copy
+        final int copySize = 16;
+        final int offset = 24;
+        final byte[] expected = new byte[copySize];
+        for (int i = 0; i < copySize; ++i) {
+            expected[i] = (byte) (offset + i);
+        }
+
+        final byte[] copy = Arrays.copyOfRange(src, offset, offset + copySize);
+        assertArrayEquals(expected, copy);
+        assertArrayEquals(new byte[0], Arrays.copyOfRange(src, size, size));
+    }
+
+    // Used mainly in the Dhcp code
+    @Test
+    public void testCopyOf() {
+        final byte[] src = makeTrivialArray(128);
+        final byte[] copy = Arrays.copyOf(src, src.length);
+        assertArrayEquals(src, copy);
+        assertFalse(src == copy);
+
+        assertArrayEquals(new byte[0], Arrays.copyOf(src, 0));
+
+        final int excess = 16;
+        final byte[] biggerCopy = Arrays.copyOf(src, src.length + excess);
+        for (int i = src.length; i < src.length + excess; ++i) {
+            assertEquals(0, biggerCopy[i]);
+        }
+        for (int i = src.length - 1; i >= 0; --i) {
+            assertEquals(src[i], biggerCopy[i]);
+        }
+    }
+
+    // Used mainly in DnsUtils but also various other places
+    @Test
+    public void testAsList() {
+        final int size = 24;
+        final Object[] src = new Object[size];
+        final ArrayList<Object> expected = new ArrayList<>(size);
+        for (int i = 0; i < size; ++i) {
+            final Object o = new Object();
+            src[i] = o;
+            expected.add(o);
+        }
+        assertEquals(expected, Arrays.asList(src));
+    }
+}
diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
new file mode 100644
index 0000000..ef15b66
--- /dev/null
+++ b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.net
+
+import android.net.wifi.aware.DiscoverySession
+import android.net.wifi.aware.PeerHandle
+import android.net.wifi.aware.WifiAwareNetworkSpecifier
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+
+import com.android.testutils.assertParcelSane
+
+import java.lang.IllegalStateException
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MatchAllNetworkSpecifierTest {
+    @Test
+    fun testParcel() {
+        assertParcelSane(MatchAllNetworkSpecifier(), 0)
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testSatisfiedBy() {
+        val specifier = MatchAllNetworkSpecifier()
+        val discoverySession = Mockito.mock(DiscoverySession::class.java)
+        val peerHandle = Mockito.mock(PeerHandle::class.java)
+        val wifiAwareNetworkSpecifier = WifiAwareNetworkSpecifier.Builder(discoverySession,
+                peerHandle).build()
+        specifier.satisfiedBy(wifiAwareNetworkSpecifier)
+    }
+}
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 316a83a..3f8261d 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -17,6 +17,8 @@
 package android.net;
 
 import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+import static android.net.NetworkCapabilities.MAX_TRANSPORT;
+import static android.net.NetworkCapabilities.MIN_TRANSPORT;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
@@ -32,10 +34,12 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
 import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
+import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
 import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
 
 import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
@@ -45,18 +49,28 @@
 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.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.net.wifi.aware.DiscoverySession;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
 import android.os.Build;
+import android.os.Process;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 
 import androidx.core.os.BuildCompat;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mockito;
 
 import java.util.Arrays;
 import java.util.Set;
@@ -67,6 +81,12 @@
     private static final String TEST_SSID = "TEST_SSID";
     private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
 
+    @Rule
+    public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
+
+    private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
+    private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
+
     private boolean isAtLeastR() {
         // BuildCompat.isAtLeastR() is used to check the Android version before releasing Android R.
         // Build.VERSION.SDK_INT > Build.VERSION_CODES.Q is used to check the Android version after
@@ -441,7 +461,7 @@
         return range;
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testSetAdministratorUids() {
         NetworkCapabilities nc =
                 new NetworkCapabilities().setAdministratorUids(new int[] {2, 1, 3});
@@ -449,7 +469,7 @@
         assertArrayEquals(new int[] {1, 2, 3}, nc.getAdministratorUids());
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testSetAdministratorUidsWithDuplicates() {
         try {
             new NetworkCapabilities().setAdministratorUids(new int[] {1, 1});
@@ -510,6 +530,12 @@
         assertFalse(nc2.appliesToUid(12));
         assertTrue(nc1.appliesToUid(22));
         assertTrue(nc2.appliesToUid(22));
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testCombineCapabilities_AdministratorUids() {
+        final NetworkCapabilities nc1 = new NetworkCapabilities();
+        final NetworkCapabilities nc2 = new NetworkCapabilities();
 
         final int[] adminUids = {3, 6, 12};
         nc1.setAdministratorUids(adminUids);
@@ -518,7 +544,7 @@
         assertArrayEquals(nc2.getAdministratorUids(), adminUids);
 
         final int[] adminUidsOtherOrder = {3, 12, 6};
-        nc1.setAdministratorUids(adminUids);
+        nc1.setAdministratorUids(adminUidsOtherOrder);
         assertTrue(nc2.equalsAdministratorUids(nc1));
 
         final int[] adminUids2 = {11, 1, 12, 3, 6};
@@ -672,4 +698,238 @@
         assertEquals(TRANSPORT_VPN, transportTypes[2]);
         assertEquals(TRANSPORT_TEST, transportTypes[3]);
     }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testTelephonyNetworkSpecifier() {
+        final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+        final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_WIFI)
+                .setNetworkSpecifier(specifier)
+                .build();
+        assertEquals(specifier, nc1.getNetworkSpecifier());
+        try {
+            final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                    .setNetworkSpecifier(specifier)
+                    .build();
+            fail("Must have a single transport type. Without transport type or multiple transport"
+                    + " types is invalid.");
+        } catch (IllegalStateException expected) { }
+    }
+
+    @Test
+    public void testWifiAwareNetworkSpecifier() {
+        final NetworkCapabilities nc = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_WIFI_AWARE);
+        // If NetworkSpecifier is not set, the default value is null.
+        assertNull(nc.getNetworkSpecifier());
+        final WifiAwareNetworkSpecifier specifier = new WifiAwareNetworkSpecifier.Builder(
+                mDiscoverySession, mPeerHandle).build();
+        nc.setNetworkSpecifier(specifier);
+        assertEquals(specifier, nc.getNetworkSpecifier());
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testAdministratorUidsAndOwnerUid() {
+        // Test default owner uid.
+        // If the owner uid is not set, the default value should be Process.INVALID_UID.
+        final NetworkCapabilities nc1 = new NetworkCapabilities.Builder().build();
+        assertEquals(Process.INVALID_UID, nc1.getOwnerUid());
+        // Test setAdministratorUids and getAdministratorUids.
+        final int[] administratorUids = {1001, 10001};
+        final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                .setAdministratorUids(administratorUids)
+                .build();
+        assertTrue(Arrays.equals(administratorUids, nc2.getAdministratorUids()));
+        // Test setOwnerUid and getOwnerUid.
+        // The owner UID must be included in administrator UIDs, or throw IllegalStateException.
+        try {
+            final NetworkCapabilities nc3 = new NetworkCapabilities.Builder()
+                    .setOwnerUid(1001)
+                    .build();
+            fail("The owner UID must be included in administrator UIDs.");
+        } catch (IllegalStateException expected) { }
+        final NetworkCapabilities nc4 = new NetworkCapabilities.Builder()
+                .setAdministratorUids(administratorUids)
+                .setOwnerUid(1001)
+                .build();
+        assertEquals(1001, nc4.getOwnerUid());
+        try {
+            final NetworkCapabilities nc5 = new NetworkCapabilities.Builder()
+                    .setAdministratorUids(null)
+                    .build();
+            fail("Should not set null into setAdministratorUids");
+        } catch (NullPointerException expected) { }
+    }
+
+    @Test
+    public void testLinkBandwidthKbps() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        // The default value of LinkDown/UpstreamBandwidthKbps should be LINK_BANDWIDTH_UNSPECIFIED.
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkDownstreamBandwidthKbps());
+        assertEquals(LINK_BANDWIDTH_UNSPECIFIED, nc.getLinkUpstreamBandwidthKbps());
+        nc.setLinkDownstreamBandwidthKbps(512);
+        nc.setLinkUpstreamBandwidthKbps(128);
+        assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+        assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+        assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+        assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+    }
+
+    @Test
+    public void testSignalStrength() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        // The default value of signal strength should be SIGNAL_STRENGTH_UNSPECIFIED.
+        assertEquals(SIGNAL_STRENGTH_UNSPECIFIED, nc.getSignalStrength());
+        nc.setSignalStrength(-80);
+        assertEquals(-80, nc.getSignalStrength());
+        assertNotEquals(-50, nc.getSignalStrength());
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testDeduceRestrictedCapability() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        // Default capabilities don't have restricted capability.
+        assertFalse(nc.deduceRestrictedCapability());
+        // If there is a force restricted capability, then the network capabilities is restricted.
+        nc.addCapability(NET_CAPABILITY_OEM_PAID);
+        nc.addCapability(NET_CAPABILITY_INTERNET);
+        assertTrue(nc.deduceRestrictedCapability());
+        // Except for the force restricted capability, if there is any unrestricted capability in
+        // capabilities, then the network capabilities is not restricted.
+        nc.removeCapability(NET_CAPABILITY_OEM_PAID);
+        nc.addCapability(NET_CAPABILITY_CBS);
+        assertFalse(nc.deduceRestrictedCapability());
+        // Except for the force restricted capability, the network capabilities will only be treated
+        // as restricted when there is no any unrestricted capability.
+        nc.removeCapability(NET_CAPABILITY_INTERNET);
+        assertTrue(nc.deduceRestrictedCapability());
+    }
+
+    private void assertNoTransport(NetworkCapabilities nc) {
+        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+            assertFalse(nc.hasTransport(i));
+        }
+    }
+
+    // Checks that all transport types from MIN_TRANSPORT to maxTransportType are set and all
+    // transport types from maxTransportType + 1 to MAX_TRANSPORT are not set when positiveSequence
+    // is true. If positiveSequence is false, then the check sequence is opposite.
+    private void checkCurrentTransportTypes(NetworkCapabilities nc, int maxTransportType,
+            boolean positiveSequence) {
+        for (int i = MIN_TRANSPORT; i <= maxTransportType; i++) {
+            if (positiveSequence) {
+                assertTrue(nc.hasTransport(i));
+            } else {
+                assertFalse(nc.hasTransport(i));
+            }
+        }
+        for (int i = MAX_TRANSPORT; i > maxTransportType; i--) {
+            if (positiveSequence) {
+                assertFalse(nc.hasTransport(i));
+            } else {
+                assertTrue(nc.hasTransport(i));
+            }
+        }
+    }
+
+    @Test
+    public void testMultipleTransportTypes() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        assertNoTransport(nc);
+        // Test adding multiple transport types.
+        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+            nc.addTransportType(i);
+            checkCurrentTransportTypes(nc, i, true /* positiveSequence */);
+        }
+        // Test removing multiple transport types.
+        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+            nc.removeTransportType(i);
+            checkCurrentTransportTypes(nc, i, false /* positiveSequence */);
+        }
+        assertNoTransport(nc);
+        nc.addTransportType(TRANSPORT_WIFI);
+        assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(nc.hasTransport(TRANSPORT_VPN));
+        nc.addTransportType(TRANSPORT_VPN);
+        assertTrue(nc.hasTransport(TRANSPORT_WIFI));
+        assertTrue(nc.hasTransport(TRANSPORT_VPN));
+        nc.removeTransportType(TRANSPORT_WIFI);
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        assertTrue(nc.hasTransport(TRANSPORT_VPN));
+        nc.removeTransportType(TRANSPORT_VPN);
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(nc.hasTransport(TRANSPORT_VPN));
+        assertNoTransport(nc);
+    }
+
+    @Test
+    public void testAddAndRemoveTransportType() {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        try {
+            nc.addTransportType(-1);
+            fail("Should not set invalid transport type into addTransportType");
+        } catch (IllegalArgumentException expected) { }
+        try {
+            nc.removeTransportType(-1);
+            fail("Should not set invalid transport type into removeTransportType");
+        } catch (IllegalArgumentException e) { }
+    }
+
+    private class TestTransportInfo implements TransportInfo {
+        TestTransportInfo() {
+        }
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    public void testBuilder() {
+        final int ownerUid = 1001;
+        final int signalStrength = -80;
+        final int requestUid = 10100;
+        final int[] administratorUids = {ownerUid, 10001};
+        final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
+        final TestTransportInfo transportInfo = new TestTransportInfo();
+        final String ssid = "TEST_SSID";
+        final String packageName = "com.google.test.networkcapabilities";
+        final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_WIFI)
+                .addTransportType(TRANSPORT_CELLULAR)
+                .removeTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_EIMS)
+                .addCapability(NET_CAPABILITY_CBS)
+                .removeCapability(NET_CAPABILITY_CBS)
+                .setAdministratorUids(administratorUids)
+                .setOwnerUid(ownerUid)
+                .setLinkDownstreamBandwidthKbps(512)
+                .setLinkUpstreamBandwidthKbps(128)
+                .setNetworkSpecifier(specifier)
+                .setTransportInfo(transportInfo)
+                .setSignalStrength(signalStrength)
+                .setSsid(ssid)
+                .setRequestorUid(requestUid)
+                .setRequestorPackageName(packageName)
+                .build();
+        assertEquals(1, nc.getTransportTypes().length);
+        assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]);
+        assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS));
+        assertFalse(nc.hasCapability(NET_CAPABILITY_CBS));
+        assertTrue(Arrays.equals(administratorUids, nc.getAdministratorUids()));
+        assertEquals(ownerUid, nc.getOwnerUid());
+        assertEquals(512, nc.getLinkDownstreamBandwidthKbps());
+        assertNotEquals(128, nc.getLinkDownstreamBandwidthKbps());
+        assertEquals(128, nc.getLinkUpstreamBandwidthKbps());
+        assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
+        assertEquals(specifier, nc.getNetworkSpecifier());
+        assertEquals(transportInfo, nc.getTransportInfo());
+        assertEquals(signalStrength, nc.getSignalStrength());
+        assertNotEquals(-50, nc.getSignalStrength());
+        assertEquals(ssid, nc.getSsid());
+        assertEquals(requestUid, nc.getRequestorUid());
+        assertEquals(packageName, nc.getRequestorPackageName());
+        // Cannot assign null into NetworkCapabilities.Builder
+        try {
+            final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null);
+            fail("Should not set null into NetworkCapabilities.Builder");
+        } catch (NullPointerException expected) { }
+        assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+    }
 }
diff --git a/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
new file mode 100644
index 0000000..7b22e45
--- /dev/null
+++ b/tests/net/common/java/android/net/netstats/NetworkStatsApiTest.kt
@@ -0,0 +1,209 @@
+/*
+ * 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.net.netstats
+
+import android.net.NetworkStats
+import android.net.NetworkStats.DEFAULT_NETWORK_NO
+import android.net.NetworkStats.DEFAULT_NETWORK_YES
+import android.net.NetworkStats.Entry
+import android.net.NetworkStats.IFACE_VT
+import android.net.NetworkStats.METERED_NO
+import android.net.NetworkStats.METERED_YES
+import android.net.NetworkStats.ROAMING_NO
+import android.net.NetworkStats.ROAMING_YES
+import android.net.NetworkStats.SET_DEFAULT
+import android.net.NetworkStats.SET_FOREGROUND
+import android.net.NetworkStats.TAG_NONE
+import android.os.Build
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.assertFieldCountEquals
+import com.android.testutils.assertNetworkStatsEquals
+import com.android.testutils.assertParcelingIsLossless
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+
+@RunWith(JUnit4::class)
+@SmallTest
+class NetworkStatsApiTest {
+    @Rule
+    @JvmField
+    val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q)
+
+    private val testStatsEmpty = NetworkStats(0L, 0)
+
+    // Note that these variables need to be initialized outside of constructor, initialize
+    // here with methods that don't exist in Q devices will result in crash.
+
+    // stats1 and stats2 will have some entries with common keys, which are expected to
+    // be merged if performing add on these 2 stats.
+    private lateinit var testStats1: NetworkStats
+    private lateinit var testStats2: NetworkStats
+
+    // This is a result of adding stats1 and stats2, while the merging of common key items is
+    // subject to test later, this should not be initialized with for a loop to add stats1
+    // and stats2 above.
+    private lateinit var testStats3: NetworkStats
+
+    companion object {
+        private const val TEST_IFACE = "test0"
+        private const val TEST_UID1 = 1001
+        private const val TEST_UID2 = 1002
+    }
+
+    @Before
+    fun setUp() {
+        testStats1 = NetworkStats(0L, 0)
+                // Entries which only appear in set1.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4))
+                // Entries which are common for set1 and set2.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0))
+                .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8))
+                .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0))
+        assertEquals(8, testStats1.size())
+
+        testStats2 = NetworkStats(0L, 0)
+                // Entries which are common for set1 and set2.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+                .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7))
+                .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0))
+                // Entry which only appears in set2.
+                .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+        assertEquals(5, testStats2.size())
+
+        testStats3 = NetworkStats(0L, 9)
+                // Entries which are unique either in stats1 or stats2.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
+                .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+                // Entries which are common for stats1 and stats2 are being merged.
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1))
+                .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49))
+                .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15))
+                .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0))
+        assertEquals(9, testStats3.size())
+    }
+
+    @Test
+    fun testAddEntry() {
+        val expectedEntriesInStats2 = arrayOf(
+                Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1),
+                Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45),
+                Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7),
+                Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0),
+                Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
+
+        // While testStats* are already initialized with addEntry, verify content added
+        // matches expectation.
+        for (i in expectedEntriesInStats2.indices) {
+            val entry = testStats2.getValues(i, null)
+            assertEquals(expectedEntriesInStats2[i], entry)
+        }
+
+        // Verify entry updated with addEntry.
+        val stats = testStats2.addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12, -5, 7, 0, 9))
+        assertEquals(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16, -2, 9, 1, 9),
+                stats.getValues(3, null))
+    }
+
+    @Test
+    fun testAdd() {
+        var stats = NetworkStats(0L, 0)
+        assertNetworkStatsEquals(testStatsEmpty, stats)
+        stats = stats.add(testStats2)
+        assertNetworkStatsEquals(testStats2, stats)
+        stats = stats.add(testStats1)
+        // EMPTY + STATS2 + STATS1 = STATS3
+        assertNetworkStatsEquals(testStats3, stats)
+    }
+
+    @Test
+    fun testParcelUnparcel() {
+        assertParcelingIsLossless(testStatsEmpty)
+        assertParcelingIsLossless(testStats1)
+        assertParcelingIsLossless(testStats2)
+        assertFieldCountEquals(15, NetworkStats::class.java)
+    }
+
+    @Test
+    fun testDescribeContents() {
+        assertEquals(0, testStatsEmpty.describeContents())
+        assertEquals(0, testStats1.describeContents())
+        assertEquals(0, testStats2.describeContents())
+        assertEquals(0, testStats3.describeContents())
+    }
+
+    @Test
+    fun testSubtract() {
+        // STATS3 - STATS2 = STATS1
+        assertNetworkStatsEquals(testStats1, testStats3.subtract(testStats2))
+        // STATS3 - STATS1 = STATS2
+        assertNetworkStatsEquals(testStats2, testStats3.subtract(testStats1))
+    }
+
+    @Test
+    fun testMethodsDontModifyReceiver() {
+        listOf(testStatsEmpty, testStats1, testStats2, testStats3).forEach {
+            val origStats = it.clone()
+            it.addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
+                    METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
+            it.add(testStats3)
+            it.subtract(testStats1)
+            assertNetworkStatsEquals(origStats, it)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index e71d599..98f705f 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -503,6 +503,53 @@
     }
 
     @Test
+    public void testRemoveEmptyEntries() throws Exception {
+        // Test empty stats.
+        final NetworkStats statsEmpty = new NetworkStats(TEST_START, 3);
+        assertEquals(0, statsEmpty.removeEmptyEntries().size());
+
+        // Test stats with non-zero entry.
+        final NetworkStats statsNonZero = new NetworkStats(TEST_START, 1)
+                .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+        assertEquals(1, statsNonZero.size());
+        final NetworkStats expectedNonZero = statsNonZero.removeEmptyEntries();
+        assertEquals(1, expectedNonZero.size());
+        assertValues(expectedNonZero, 0, TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+                ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 128L, 0L, 2L, 20L);
+
+        // Test stats with empty entry.
+        final NetworkStats statsZero = new NetworkStats(TEST_START, 1)
+                .insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+        assertEquals(1, statsZero.size());
+        final NetworkStats expectedZero = statsZero.removeEmptyEntries();
+        assertEquals(1, statsZero.size()); // Assert immutable.
+        assertEquals(0, expectedZero.size());
+
+        // Test stats with multiple entries.
+        final NetworkStats statsMultiple = new NetworkStats(TEST_START, 0)
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 0L, 8L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 4L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 2L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 0L, 0L, 1L);
+        assertEquals(9, statsMultiple.size());
+        final NetworkStats expectedMultiple = statsMultiple.removeEmptyEntries();
+        assertEquals(9, statsMultiple.size()); // Assert immutable.
+        assertEquals(7, expectedMultiple.size());
+        assertValues(expectedMultiple.getTotalIncludingTags(null), 14L, 104L, 4L, 4L, 21L);
+
+        // Test stats with multiple empty entries.
+        assertEquals(statsMultiple.size(), statsMultiple.subtract(statsMultiple).size());
+        assertEquals(0, statsMultiple.subtract(statsMultiple).removeEmptyEntries().size());
+    }
+
+    @Test
     public void testClone() throws Exception {
         final NetworkStats original = new NetworkStats(TEST_START, 5)
                 .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index 9b24887..d0ebb52 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -181,7 +181,7 @@
         Nat464Xlat nat = makeNat464Xlat();
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         // Start clat.
         nat.start();
@@ -222,7 +222,7 @@
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
         InOrder inOrder = inOrder(mNetd, mConnectivity);
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         nat.start();
 
@@ -309,7 +309,7 @@
         Nat464Xlat nat = makeNat464Xlat();
         ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         nat.start();
 
@@ -348,7 +348,7 @@
     public void testStopBeforeClatdStarts() throws Exception {
         Nat464Xlat nat = makeNat464Xlat();
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         nat.start();
 
@@ -380,7 +380,7 @@
     public void testStopAndClatdNeverStarts() throws Exception {
         Nat464Xlat nat = makeNat464Xlat();
 
-        nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX));
+        nat.setNat64PrefixFromDns(new IpPrefix(NAT64_PREFIX));
 
         nat.start();
 
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index d56f2be..b513463e 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -160,7 +160,7 @@
     int exclusiveField = 0;
     int defaultState = INT_MAX;
     int triggerStateReset = INT_MAX;
-    bool nested;
+    bool nested = true;
 
     int uidField = 0;
 
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 2029479..99beb7b 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -81,7 +81,6 @@
     libs: [
         "framework-annotations-lib",
         "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
-        "framework-telephony-stubs",
     ],
     srcs: [
         ":framework-wifi-updatable-sources",
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index ed54ad1..737b7c7 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.MacAddress;
-import android.net.MatchAllNetworkSpecifier;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.os.Parcel;
@@ -553,13 +552,6 @@
     /** @hide */
     @Override
     public boolean canBeSatisfiedBy(NetworkSpecifier other) {
-        if (this == other) {
-            return true;
-        }
-        // Any generic requests should be satisifed by a specific wifi network.
-        if (other == null || other instanceof MatchAllNetworkSpecifier) {
-            return true;
-        }
         if (other instanceof WifiNetworkAgentSpecifier) {
             return ((WifiNetworkAgentSpecifier) other).satisfiesNetworkSpecifier(this);
         }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index 53a7d03..fc0ef46 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -382,11 +382,11 @@
     /**
      * Validate NetworkSpecifier matching.
      * a) Create a network specifier for WPA_PSK network
-     * b) Ensure that the specifier matches {@code null} and {@link MatchAllNetworkSpecifier}
+     * b) Ensure that the specifier does not match {@code null} and {@link MatchAllNetworkSpecifier}
      * specifiers.
      */
     @Test
-    public void testWifiNetworkSpecifierSatisfiesNullAndAllMatch() {
+    public void testWifiNetworkSpecifierDoesNotSatisfyNullAndAllMatch() {
         WifiConfiguration wifiConfiguration = new WifiConfiguration();
         wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
@@ -396,8 +396,8 @@
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
                         wifiConfiguration);
 
-        assertTrue(specifier.canBeSatisfiedBy(null));
-        assertTrue(specifier.canBeSatisfiedBy(new MatchAllNetworkSpecifier()));
+        assertFalse(specifier.canBeSatisfiedBy(null));
+        assertFalse(specifier.canBeSatisfiedBy(new MatchAllNetworkSpecifier()));
     }
 
     /**