Merge "Don't update persistent msgs when dozing"
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 8237383..e9ce87f 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -3331,7 +3331,7 @@
         mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
         mMaintenanceStartTime = 0;
         mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
-        cancelLightAlarmLocked();
+        cancelAllLightAlarmsLocked();
     }
 
     @GuardedBy("this")
@@ -3406,7 +3406,7 @@
                     mLightState == LIGHT_STATE_IDLE
                             || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK;
         } else {
-            Slog.wtfStack(TAG, "stepLightIdleStateLocked called in invalid state");
+            Slog.wtfStack(TAG, "stepLightIdleStateLocked called in invalid state: " + mLightState);
             return;
         }
 
@@ -3441,7 +3441,7 @@
                 // connectivity...  let's try to wait until the network comes back.
                 // We'll only wait for another full idle period, however, and then give up.
                 scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
-                mNextLightAlarmTime = 0;
+                cancelLightAlarmLocked();
                 if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
                 mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
                 EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3467,7 +3467,7 @@
             // We're entering IDLE. We may have used less than curLightIdleBudget for the
             // maintenance window, so reschedule the alarm starting from now.
             scheduleLightMaintenanceAlarmLocked(mNextLightIdleDelay);
-            mNextLightAlarmTime = 0;
+            cancelLightAlarmLocked();
             if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
             mLightState = LIGHT_STATE_IDLE;
             EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3609,7 +3609,7 @@
                 moveToStateLocked(STATE_IDLE, reason);
                 if (mLightState != LIGHT_STATE_OVERRIDE) {
                     mLightState = LIGHT_STATE_OVERRIDE;
-                    cancelLightAlarmLocked();
+                    cancelAllLightAlarmsLocked();
                 }
                 addEvent(EVENT_DEEP_IDLE, null);
                 mGoingIdleWakeLock.acquire();
@@ -3950,11 +3950,21 @@
     }
 
     @GuardedBy("this")
-    void cancelLightAlarmLocked() {
+    private void cancelAllLightAlarmsLocked() {
+        cancelLightAlarmLocked();
+        cancelLightMaintenanceAlarmLocked();
+    }
+
+    @GuardedBy("this")
+    private void cancelLightAlarmLocked() {
         if (mNextLightAlarmTime != 0) {
             mNextLightAlarmTime = 0;
             mAlarmManager.cancel(mLightAlarmListener);
         }
+    }
+
+    @GuardedBy("this")
+    private void cancelLightMaintenanceAlarmLocked() {
         if (mNextLightMaintenanceAlarmTime != 0) {
             mNextLightMaintenanceAlarmTime = 0;
             mAlarmManager.cancel(mLightMaintenanceAlarmListener);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 48f8581..2b0a833 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -2171,7 +2171,7 @@
                     }
                     scheduleCutoff();
                 } else if (mRegularJobTimer && priorityLowered) {
-                    scheduleCutoff();
+                    rescheduleCutoff();
                 }
             }
         }
@@ -2207,7 +2207,7 @@
                                 mRunningBgJobs.valueAt(i).getEffectivePriority());
                     }
                     if (mLowestPriority != oldPriority) {
-                        scheduleCutoff();
+                        rescheduleCutoff();
                     }
                 }
             }
@@ -2707,7 +2707,7 @@
                             if (DEBUG) {
                                 Slog.d(TAG, pkg + " had early REACHED_QUOTA message");
                             }
-                            mPkgTimers.get(pkg.userId, pkg.packageName).scheduleCutoff();
+                            mPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
                         }
                         break;
                     }
@@ -2729,7 +2729,7 @@
                             if (DEBUG) {
                                 Slog.d(TAG, pkg + " had early REACHED_EJ_QUOTA message");
                             }
-                            mEJPkgTimers.get(pkg.userId, pkg.packageName).scheduleCutoff();
+                            mEJPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
                         }
                         break;
                     }
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index a6a007f..57a9d3b 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -57,7 +57,6 @@
 
 import libcore.util.EmptyArray;
 
-import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
@@ -104,54 +103,6 @@
     @GuardedBy("mLock")
     private final BalanceThresholdAlarmQueue mBalanceThresholdAlarmQueue;
 
-    /**
-     * Comparator to use to sort apps before we distribute ARCs so that we try to give the most
-     * important apps ARCs first.
-     */
-    @VisibleForTesting
-    final Comparator<PackageInfo> mPackageDistributionComparator =
-            new Comparator<PackageInfo>() {
-                @Override
-                public int compare(PackageInfo pi1, PackageInfo pi2) {
-                    final ApplicationInfo appInfo1 = pi1.applicationInfo;
-                    final ApplicationInfo appInfo2 = pi2.applicationInfo;
-                    // Put any packages that don't declare an application at the end. A missing
-                    // <application> tag likely means the app won't be doing any work anyway.
-                    if (appInfo1 == null) {
-                        if (appInfo2 == null) {
-                            return 0;
-                        }
-                        return 1;
-                    } else if (appInfo2 == null) {
-                        return -1;
-                    }
-                    // Privileged apps eat first. They're likely required for the device to
-                    // function properly.
-                    // TODO: include headless system apps
-                    if (appInfo1.isPrivilegedApp()) {
-                        if (!appInfo2.isPrivilegedApp()) {
-                            return -1;
-                        }
-                    } else if (appInfo2.isPrivilegedApp()) {
-                        return 1;
-                    }
-
-                    // Sort by most recently used.
-                    final long timeSinceLastUsedMs1 =
-                            mAppStandbyInternal.getTimeSinceLastUsedByUser(
-                                    pi1.packageName, UserHandle.getUserId(pi1.applicationInfo.uid));
-                    final long timeSinceLastUsedMs2 =
-                            mAppStandbyInternal.getTimeSinceLastUsedByUser(
-                                    pi2.packageName, UserHandle.getUserId(pi2.applicationInfo.uid));
-                    if (timeSinceLastUsedMs1 < timeSinceLastUsedMs2) {
-                        return -1;
-                    } else if (timeSinceLastUsedMs1 > timeSinceLastUsedMs2) {
-                        return 1;
-                    }
-                    return 0;
-                }
-            };
-
     private static final int MSG_CHECK_BALANCE = 0;
 
     Agent(@NonNull InternalResourceService irs, @NonNull Scribe scribe) {
@@ -668,7 +619,6 @@
     @GuardedBy("mLock")
     void distributeBasicIncomeLocked(int batteryLevel) {
         List<PackageInfo> pkgs = mIrs.getInstalledPackages();
-        pkgs.sort(mPackageDistributionComparator);
 
         final long now = getCurrentTimeMillis();
         for (int i = 0; i < pkgs.size(); ++i) {
@@ -709,8 +659,6 @@
                 mIrs.getMaxCirculationLocked() / mIrs.getInstalledPackages().size();
         final long now = getCurrentTimeMillis();
 
-        pkgs.sort(mPackageDistributionComparator);
-
         for (int i = 0; i < pkgs.size(); ++i) {
             final PackageInfo packageInfo = pkgs.get(i);
             if (!shouldGiveCredits(packageInfo)) {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9564dde..c5410a0 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -189,6 +189,8 @@
                 instrument.abi = nextArgRequired();
             } else if (opt.equals("--no-restart")) {
                 instrument.noRestart = true;
+            } else if (opt.equals("--always-check-signature")) {
+                instrument.alwaysCheckSignature = true;
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 return;
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 0b439df..a0562d9 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -16,6 +16,7 @@
 
 package com.android.commands.am;
 
+import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
@@ -95,6 +96,7 @@
     public Bundle args = new Bundle();
     // Required
     public String componentNameArg;
+    public boolean alwaysCheckSignature = false;
 
     /**
      * Construct the instrument command runner.
@@ -519,6 +521,9 @@
             if (noRestart) {
                 flags |= INSTR_FLAG_NO_RESTART;
             }
+            if (alwaysCheckSignature) {
+                flags |= INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
+            }
             if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
                         abi)) {
                 throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index c134822..4f0ede9 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -23,6 +23,7 @@
 
 cc_defaults {
     name: "idmap2_defaults",
+    cpp_std: "gnu++2b",
     tidy: true,
     tidy_checks: [
         "modernize-*",
@@ -31,6 +32,7 @@
         "android-*",
         "misc-*",
         "readability-*",
+        "-readability-identifier-length",
     ],
     tidy_checks_as_errors: [
         "modernize-*",
@@ -52,7 +54,6 @@
         "-readability-const-return-type",
         "-readability-convert-member-functions-to-static",
         "-readability-else-after-return",
-        "-readability-identifier-length",
         "-readability-named-parameter",
         "-readability-redundant-access-specifiers",
         "-readability-uppercase-literal-suffix",
@@ -113,6 +114,7 @@
         "libidmap2/proto/*.proto",
     ],
     host_supported: true,
+    tidy: false,
     proto: {
         type: "lite",
         export_proto_headers: true,
diff --git a/cmds/idmap2/idmap2d/Main.cpp b/cmds/idmap2/idmap2d/Main.cpp
index 2707049..dad09a0 100644
--- a/cmds/idmap2/idmap2d/Main.cpp
+++ b/cmds/idmap2/idmap2d/Main.cpp
@@ -15,14 +15,14 @@
  */
 
 #define ATRACE_TAG ATRACE_TAG_RESOURCES
+#define LOG_TAG "idmap2d"
+#include "android-base/logging.h"
 
 #include <binder/BinderService.h>
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 
 #include <cstdlib>  // EXIT_{FAILURE,SUCCESS}
-#include <iostream>
-#include <sstream>
 
 #include "Idmap2Service.h"
 #include "android-base/macros.h"
@@ -35,14 +35,17 @@
 using android::os::Idmap2Service;
 
 int main(int argc ATTRIBUTE_UNUSED, char** argv ATTRIBUTE_UNUSED) {
+  LOG(INFO) << "Starting";
   IPCThreadState::disableBackgroundScheduling(true);
   status_t ret = BinderService<Idmap2Service>::publish();
   if (ret != android::OK) {
+    LOG(ERROR) << "Failed to start: " << ret;
     return EXIT_FAILURE;
   }
   sp<ProcessState> ps(ProcessState::self());
   ps->startThreadPool();
   ps->giveThreadPoolName();
   IPCThreadState::self()->joinThreadPool();
+  LOG(INFO) << "Exiting";
   return EXIT_SUCCESS;
 }
diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp
index f2f8854..f9c4fa3 100644
--- a/cmds/idmap2/tests/ResultTests.cpp
+++ b/cmds/idmap2/tests/ResultTests.cpp
@@ -259,7 +259,8 @@
 }
 
 struct NoCopyContainer {
-  uint32_t value;  // NOLINT(misc-non-private-member-variables-in-classes)
+  uint32_t value = 0;  // NOLINT(misc-non-private-member-variables-in-classes)
+  NoCopyContainer() = default;
   NoCopyContainer(const NoCopyContainer&) = delete;
   NoCopyContainer& operator=(const NoCopyContainer&) = delete;
 };
@@ -268,7 +269,7 @@
   if (!succeed) {
     return Error("foo");
   }
-  std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{0U});
+  std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{});
   p->value = 42U;
   return std::move(p);
 }
diff --git a/core/api/current.txt b/core/api/current.txt
index c8d99d5..5b1f59a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17126,6 +17126,9 @@
 
   public final class SensorPrivacyManager {
     method public boolean supportsSensorToggle(int);
+    method public boolean supportsSensorToggle(int, int);
+    field public static final int TOGGLE_TYPE_HARDWARE = 2; // 0x2
+    field public static final int TOGGLE_TYPE_SOFTWARE = 1; // 0x1
   }
 
   public static class SensorPrivacyManager.Sensors {
@@ -39570,12 +39573,21 @@
     field public static final String ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE";
     field public static final String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH";
     field public static final String DETAILS_META_DATA = "android.speech.DETAILS";
-    field public static final String EXTRA_AUDIO_INJECT_SOURCE = "android.speech.extra.AUDIO_INJECT_SOURCE";
+    field @Deprecated public static final String EXTRA_AUDIO_INJECT_SOURCE = "android.speech.extra.AUDIO_INJECT_SOURCE";
+    field public static final String EXTRA_AUDIO_SOURCE = "android.speech.extra.AUDIO_SOURCE";
+    field public static final String EXTRA_AUDIO_SOURCE_CHANNEL_COUNT = "android.speech.extra.AUDIO_SOURCE_CHANNEL_COUNT";
+    field public static final String EXTRA_AUDIO_SOURCE_ENCODING = "android.speech.extra.AUDIO_SOURCE_ENCODING";
+    field public static final String EXTRA_AUDIO_SOURCE_SAMPLING_RATE = "android.speech.extra.AUDIO_SOURCE_SAMPLING_RATE";
+    field public static final String EXTRA_BIASING_STRINGS = "android.speech.extra.BIASING_STRINGS";
     field public static final String EXTRA_CALLING_PACKAGE = "calling_package";
     field public static final String EXTRA_CONFIDENCE_SCORES = "android.speech.extra.CONFIDENCE_SCORES";
+    field public static final String EXTRA_ENABLE_BIASING_DEVICE_CONTEXT = "android.speech.extra.ENABLE_BIASING_DEVICE_CONTEXT";
+    field public static final String EXTRA_ENABLE_FORMATTING = "android.speech.extra.ENABLE_FORMATTING";
+    field public static final String EXTRA_HIDE_PARTIAL_TRAILING_PUNCTUATION = "android.speech.extra.HIDE_PARTIAL_TRAILING_PUNCTUATION";
     field public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE";
     field public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL";
     field public static final String EXTRA_LANGUAGE_PREFERENCE = "android.speech.extra.LANGUAGE_PREFERENCE";
+    field public static final String EXTRA_MASK_OFFENSIVE_WORDS = "android.speech.extra.MASK_OFFENSIVE_WORDS";
     field public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS";
     field public static final String EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE = "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE";
     field public static final String EXTRA_ORIGIN = "android.speech.extra.ORIGIN";
@@ -39586,12 +39598,14 @@
     field public static final String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT";
     field public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
     field public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
-    field public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION";
+    field public static final String EXTRA_SEGMENTED_SESSION = "android.speech.extra.SEGMENTED_SESSION";
     field public static final String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
     field public static final String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
     field public static final String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS";
     field public static final String EXTRA_SUPPORTED_LANGUAGES = "android.speech.extra.SUPPORTED_LANGUAGES";
     field public static final String EXTRA_WEB_SEARCH_ONLY = "android.speech.extra.WEB_SEARCH_ONLY";
+    field public static final String FORMATTING_OPTIMIZE_LATENCY = "latency";
+    field public static final String FORMATTING_OPTIMIZE_QUALITY = "quality";
     field public static final String LANGUAGE_MODEL_FREE_FORM = "free_form";
     field public static final String LANGUAGE_MODEL_WEB_SEARCH = "web_search";
     field public static final int RESULT_AUDIO_ERROR = 5; // 0x5
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 67091d8..8d9c5db 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2794,7 +2794,7 @@
     method public void addActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener, @NonNull java.util.concurrent.Executor);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
-    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @NonNull java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -3759,13 +3759,25 @@
   public final class SensorPrivacyManager {
     method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
-    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean areAnySensorPrivacyTogglesEnabled(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int, int);
     method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
     method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
   }
 
   public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
-    method public void onSensorPrivacyChanged(int, boolean);
+    method public default void onSensorPrivacyChanged(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams);
+    method @Deprecated public void onSensorPrivacyChanged(int, boolean);
+  }
+
+  public static class SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams {
+    method public int getSensor();
+    method public int getToggleType();
+    method public boolean isEnabled();
   }
 
 }
@@ -10032,6 +10044,21 @@
     field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
   }
 
+  public final class PermissionGroupUsage implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public CharSequence getAttributionLabel();
+    method @Nullable public CharSequence getAttributionTag();
+    method public long getLastAccessTimeMillis();
+    method @NonNull public String getPackageName();
+    method @NonNull public String getPermissionGroupName();
+    method @Nullable public CharSequence getProxyLabel();
+    method public int getUid();
+    method public boolean isActive();
+    method public boolean isPhoneCall();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.permission.PermissionGroupUsage> CREATOR;
+  }
+
   public final class PermissionManager {
     method public int checkDeviceIdentifierAccess(@Nullable String, @Nullable String, @Nullable String, int, int);
     method public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
@@ -10048,6 +10075,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, long, int, int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String);
     field @RequiresPermission(android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS) public static final String ACTION_REVIEW_PERMISSION_DECISIONS = "android.permission.action.REVIEW_PERMISSION_DECISIONS";
+    field public static final String EXTRA_PERMISSION_USAGES = "android.permission.extra.PERMISSION_USAGES";
     field public static final int PERMISSION_GRANTED = 0; // 0x0
     field public static final int PERMISSION_HARD_DENIED = 2; // 0x2
     field public static final int PERMISSION_SOFT_DENIED = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e5165449..29b8248 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2058,17 +2058,6 @@
 
 package android.permission {
 
-  public final class PermGroupUsage {
-    ctor public PermGroupUsage(@NonNull String, int, @NonNull String, long, boolean, boolean, @Nullable CharSequence);
-    method @Nullable public CharSequence getAttribution();
-    method public long getLastAccess();
-    method @NonNull public String getPackageName();
-    method @NonNull public String getPermGroupName();
-    method public int getUid();
-    method public boolean isActive();
-    method public boolean isPhoneCall();
-  }
-
   public final class PermissionControllerManager {
     method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
@@ -2084,8 +2073,8 @@
   }
 
   public final class PermissionManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
-    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermissionGroupUsage> getIndicatorAppOpUsageData();
+    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermissionGroupUsage> getIndicatorAppOpUsageData(boolean);
     method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
     method @RequiresPermission(android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL) public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String, int);
   }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 89dd9ef..9f15105 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -179,6 +179,12 @@
      * @hide
      */
     public static final int INSTR_FLAG_NO_RESTART = 1 << 3;
+    /**
+     * Force the check that instrumentation and the target package are signed with the same
+     * certificate even if {@link Build#IS_DEBUGGABLE} is {@code true}.
+     * @hide
+     */
+    public static final int INSTR_FLAG_ALWAYS_CHECK_SIGNATURE = 1 << 4;
 
     static final class UidObserver extends IUidObserver.Stub {
         final OnUidImportanceListener mListener;
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 99ce147..02d140f 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -48,7 +48,6 @@
 import android.util.ArrayMap;
 import android.view.Surface;
 
-import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -223,7 +222,8 @@
          * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
          * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
          * @param executor The executor on which {@code callback} will be invoked. This is ignored
-         * if {@code callback} is {@code null}.
+         * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must
+         * not be null.
          * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
          * @return The newly created virtual display, or {@code null} if the application could
          * not create the virtual display.
@@ -237,7 +237,7 @@
                 @IntRange(from = 1) int densityDpi,
                 @Nullable Surface surface,
                 @VirtualDisplayFlag int flags,
-                @NonNull @CallbackExecutor Executor executor,
+                @Nullable @CallbackExecutor Executor executor,
                 @Nullable VirtualDisplay.Callback callback) {
             // TODO(b/205343547): Handle display groups properly instead of creating a new display
             //  group for every new virtual display created using this API.
@@ -253,7 +253,7 @@
                             .setFlags(getVirtualDisplayFlags(flags))
                             .build(),
                     callback,
-                    Objects.requireNonNull(executor));
+                    executor);
         }
 
         /**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index be58ba7..fbb03ca 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -32,6 +32,7 @@
 import android.os.Environment;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.util.ArrayMap;
@@ -1865,7 +1866,7 @@
     }
 
     public ApplicationInfo() {
-        createTimestamp = System.currentTimeMillis();
+        createTimestamp = SystemClock.uptimeMillis();
     }
 
     public ApplicationInfo(ApplicationInfo orig) {
@@ -1939,7 +1940,7 @@
         nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
         requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
         localeConfigRes = orig.localeConfigRes;
-        createTimestamp = System.currentTimeMillis();
+        createTimestamp = SystemClock.uptimeMillis();
     }
 
     public String toString() {
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 4399af9..a3cc01c 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -158,36 +158,30 @@
 
     }
 
+
+    /**
+     * Constant for software toggle.
+     */
+    public static final int TOGGLE_TYPE_SOFTWARE =
+            SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;
+
+    /**
+     * Constant for hardware toggle.
+     */
+    public static final int TOGGLE_TYPE_HARDWARE =
+            SensorPrivacyIndividualEnabledSensorProto.HARDWARE;
+
     /**
      * Types of toggles which can exist for sensor privacy
+     *
      * @hide
      */
-    public static class ToggleTypes {
-        private ToggleTypes() {}
-
-        /**
-         * Constant for software toggle.
-         */
-        public static final int SOFTWARE = SensorPrivacyIndividualEnabledSensorProto.SOFTWARE;
-
-        /**
-         * Constant for hardware toggle.
-         */
-        public static final int HARDWARE = SensorPrivacyIndividualEnabledSensorProto.HARDWARE;
-
-        /**
-         * Types of toggles which can exist for sensor privacy
-         *
-         * @hide
-         */
-        @IntDef(value = {
-                SOFTWARE,
-                HARDWARE
-        })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface ToggleType {}
-
-    }
+    @IntDef(value = {
+            TOGGLE_TYPE_SOFTWARE,
+            TOGGLE_TYPE_HARDWARE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ToggleType {}
 
     /**
      * Types of state which can exist for the sensor privacy toggle
@@ -232,20 +226,23 @@
         /**
          * Callback invoked when the sensor privacy state changes.
          *
+         * @param params Parameters describing the new state
+         */
+        default void onSensorPrivacyChanged(@NonNull SensorPrivacyChangedParams params) {
+            onSensorPrivacyChanged(params.mSensor, params.mEnabled);
+        }
+
+        /**
+         * Callback invoked when the sensor privacy state changes.
+         *
          * @param sensor the sensor whose state is changing
          * @param enabled true if sensor privacy is enabled, false otherwise.
+         *
+         * @deprecated Please use
+         * {@link #onSensorPrivacyChanged(SensorPrivacyChangedParams)}
          */
+        @Deprecated
         void onSensorPrivacyChanged(int sensor, boolean enabled);
-    }
-
-    /**
-     * A class implementing this interface can register with the {@link
-     * android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy
-     * state changes.
-     *
-     * @hide
-     */
-    public interface OnToggleSensorPrivacyChangedListener {
 
         /**
          * A class containing information about what the sensor privacy state has changed to.
@@ -262,7 +259,7 @@
                 mEnabled = enabled;
             }
 
-            public @ToggleTypes.ToggleType int getToggleType() {
+            public @ToggleType int getToggleType() {
                 return mToggleType;
             }
 
@@ -274,13 +271,6 @@
                 return mEnabled;
             }
         }
-
-        /**
-         * Callback invoked when the sensor privacy state changes.
-         *
-         * @param params Parameters describing the new state
-         */
-        void onSensorPrivacyChanged(@NonNull SensorPrivacyChangedParams params);
     }
 
     private static final Object sInstanceLock = new Object();
@@ -305,15 +295,15 @@
     /** Registered listeners */
     @GuardedBy("mLock")
     @NonNull
-    private final ArrayMap<OnToggleSensorPrivacyChangedListener, Executor> mToggleListeners =
+    private final ArrayMap<OnSensorPrivacyChangedListener, Executor> mToggleListeners =
             new ArrayMap<>();
 
     /** Listeners registered using the deprecated APIs and which
-     * OnToggleSensorPrivacyChangedListener they're using. */
+     * OnSensorPrivacyChangedListener they're using. */
     @GuardedBy("mLock")
     @NonNull
     private final ArrayMap<Pair<Integer, OnSensorPrivacyChangedListener>,
-            OnToggleSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>();
+            OnSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>();
 
     /** The singleton ISensorPrivacyListener for IPC which will be used to dispatch to local
      * listeners */
@@ -323,9 +313,9 @@
         public void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled) {
             synchronized (mLock) {
                 for (int i = 0; i < mToggleListeners.size(); i++) {
-                    OnToggleSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
+                    OnSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
                     mToggleListeners.valueAt(i).execute(() -> listener
-                            .onSensorPrivacyChanged(new OnToggleSensorPrivacyChangedListener
+                            .onSensorPrivacyChanged(new OnSensorPrivacyChangedListener
                                     .SensorPrivacyChangedParams(toggleType, sensor, enabled)));
                 }
             }
@@ -367,12 +357,24 @@
     }
 
     /**
+     * Returns the single instance of the SensorPrivacyManager.
+     *
+     * @hide
+     */
+    public static SensorPrivacyManager getInstance(Context context, ISensorPrivacyManager service) {
+        synchronized (sInstanceLock) {
+            sInstance = new SensorPrivacyManager(context, service);
+            return sInstance;
+        }
+    }
+
+    /**
      * Checks if the given toggle is supported on this device
      * @param sensor The sensor to check
      * @return whether the toggle for the sensor is supported on this device.
      */
     public boolean supportsSensorToggle(@Sensors.Sensor int sensor) {
-        return supportsSensorToggle(ToggleTypes.SOFTWARE, sensor);
+        return supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor);
     }
 
     /**
@@ -380,10 +382,8 @@
      * @param sensor The sensor to check
      * @return whether the toggle for the sensor is supported on this device.
      *
-     * @hide
      */
-    public boolean supportsSensorToggle(@ToggleTypes.ToggleType int toggleType,
-            @Sensors.Sensor int sensor) {
+    public boolean supportsSensorToggle(@ToggleType int toggleType, @Sensors.Sensor int sensor) {
         try {
             Pair key = new Pair(toggleType, sensor);
             synchronized (mLock) {
@@ -408,8 +408,6 @@
      * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
      *                       privacy changes.
      *
-     * {@link #addSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)}
-     *
      * @hide
      */
     @SystemApi
@@ -429,8 +427,6 @@
      * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
      *                 privacy changes.
      *
-     * {@link #addSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)}
-     *
      * @hide
      */
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
@@ -449,19 +445,22 @@
      * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
      *                       privacy changes.
      *
-     * {@link #addSensorPrivacyListener(Executor, OnToggleSensorPrivacyChangedListener)}
-     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
             @NonNull OnSensorPrivacyChangedListener listener) {
-
         Pair<Integer, OnSensorPrivacyChangedListener> pair = new Pair(sensor, listener);
-        OnToggleSensorPrivacyChangedListener toggleListener = params -> {
-            if (params.getSensor() == sensor) {
-                listener.onSensorPrivacyChanged(params.getSensor(), params.isEnabled());
+        OnSensorPrivacyChangedListener toggleListener = new OnSensorPrivacyChangedListener() {
+            @Override
+            public void onSensorPrivacyChanged(SensorPrivacyChangedParams params) {
+                if (params.getSensor() == sensor) {
+                    listener.onSensorPrivacyChanged(params.getSensor(), params.isEnabled());
+                }
+            }
+            @Override
+            public void onSensorPrivacyChanged(int sensor, boolean enabled) {
             }
         };
 
@@ -476,13 +475,14 @@
      * Registers a new listener to receive notification when the state of sensor privacy
      * changes.
      *
-     * @param listener the OnToggleSensorPrivacyChangedListener to be notified when the state of
+     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
      *                 sensor privacy changes.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public void addSensorPrivacyListener(@NonNull OnToggleSensorPrivacyChangedListener listener) {
+    public void addSensorPrivacyListener(@NonNull OnSensorPrivacyChangedListener listener) {
         addSensorPrivacyListener(mContext.getMainExecutor(), listener);
     }
 
@@ -492,14 +492,15 @@
      * changes.
      *
      * @param executor the executor to dispatch the callback on
-     * @param listener the OnToggleSensorPrivacyChangedListener to be notified when the state of
+     * @param listener the OnSensorPrivacyChangedListener to be notified when the state of
      *                 sensor privacy changes.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public void addSensorPrivacyListener(@NonNull Executor executor,
-            @NonNull OnToggleSensorPrivacyChangedListener listener) {
+            @NonNull OnSensorPrivacyChangedListener listener) {
         synchronized (mLock) {
             addSensorPrivacyListenerLocked(executor, listener);
         }
@@ -507,7 +508,7 @@
 
     @GuardedBy("mLock")
     private void addSensorPrivacyListenerLocked(@NonNull Executor executor,
-            @NonNull OnToggleSensorPrivacyChangedListener listener) {
+            @NonNull OnSensorPrivacyChangedListener listener) {
         if (!mToggleListenerRegistered) {
             try {
                 mService.addToggleSensorPrivacyListener(mIToggleListener);
@@ -529,10 +530,6 @@
      * @param listener the OnSensorPrivacyChangedListener to be unregistered from notifications when
      *                 sensor privacy changes.
      *
-     * {@link #removeSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)} with
-     * {@link #addSensorPrivacyListener(OnToggleSensorPrivacyChangedListener)} or
-     * {@link #addSensorPrivacyListener(Executor, OnToggleSensorPrivacyChangedListener)}
-     *
      * @hide
      */
     @SystemApi
@@ -541,7 +538,7 @@
             @NonNull OnSensorPrivacyChangedListener listener) {
         Pair<Integer, OnSensorPrivacyChangedListener> pair = new Pair(sensor, listener);
         synchronized (mLock) {
-            OnToggleSensorPrivacyChangedListener onToggleSensorPrivacyChangedListener =
+            OnSensorPrivacyChangedListener onToggleSensorPrivacyChangedListener =
                     mLegacyToggleListeners.remove(pair);
             if (onToggleSensorPrivacyChangedListener != null) {
                 removeSensorPrivacyListenerLocked(onToggleSensorPrivacyChangedListener);
@@ -553,14 +550,15 @@
      * Unregisters the specified listener from receiving notifications when the state of any sensor
      * privacy changes.
      *
-     * @param listener the {@link OnToggleSensorPrivacyChangedListener} to be unregistered from
+     * @param listener the {@link OnSensorPrivacyChangedListener} to be unregistered from
      *                 notifications when sensor privacy changes.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public void removeSensorPrivacyListener(
-            @NonNull OnToggleSensorPrivacyChangedListener listener) {
+            @NonNull OnSensorPrivacyChangedListener listener) {
         synchronized (mLock) {
             removeSensorPrivacyListenerLocked(listener);
         }
@@ -568,7 +566,7 @@
 
     @GuardedBy("mLock")
     private void removeSensorPrivacyListenerLocked(
-            @NonNull OnToggleSensorPrivacyChangedListener listener) {
+            @NonNull OnSensorPrivacyChangedListener listener) {
         mToggleListeners.remove(listener);
         if (mToggleListeners.size() == 0) {
             try {
@@ -586,12 +584,15 @@
      *
      * @return true if sensor privacy is currently enabled, false otherwise.
      *
+     * @deprecated Prefer to use {@link #isSensorPrivacyEnabled(int, int)}
+     *
      * @hide
      */
+    @Deprecated
     @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
-        return isSensorPrivacyEnabled(ToggleTypes.SOFTWARE, sensor);
+        return isSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor);
     }
 
     /**
@@ -601,8 +602,9 @@
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public boolean isSensorPrivacyEnabled(@ToggleTypes.ToggleType int toggleType,
+    public boolean isSensorPrivacyEnabled(@ToggleType int toggleType,
             @Sensors.Sensor int sensor) {
         try {
             return mService.isToggleSensorPrivacyEnabled(toggleType, sensor);
@@ -613,14 +615,16 @@
 
     /**
      * Returns whether sensor privacy is currently enabled for a specific sensor.
-     * Combines the state of the SW + HW toggles and returns the actual privacy state.
+     * Combines the state of the SW + HW toggles and returns true if either the
+     * SOFTWARE or the HARDWARE toggles are enabled.
      *
      * @return true if sensor privacy is currently enabled, false otherwise.
      *
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
-    public boolean isToggleSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
+    public boolean areAnySensorPrivacyTogglesEnabled(@Sensors.Sensor int sensor) {
         try {
             return mService.isCombinedToggleSensorPrivacyEnabled(sensor);
         } catch (RemoteException e) {
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index c053c92..c341731 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -957,7 +957,7 @@
     public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice,
             @NonNull VirtualDisplayConfig virtualDisplayConfig,
             @Nullable VirtualDisplay.Callback callback,
-            @NonNull Executor executor) {
+            @Nullable Executor executor) {
         return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice,
                 virtualDisplayConfig, callback, executor, null);
     }
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 889100d..a62bbf6 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -1054,6 +1054,14 @@
         @Nullable private final VirtualDisplay.Callback mCallback;
         @Nullable private final Executor mExecutor;
 
+        /**
+         * Creates a virtual display callback.
+         *
+         * @param callback The callback to call for virtual display events, or {@code null} if the
+         * caller does not wish to receive callback events.
+         * @param executor The executor to call the {@code callback} on. Must not be {@code null} if
+         * the callback is not {@code null}.
+         */
         VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) {
             mCallback = callback;
             mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null;
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index bf4b514..eed92c1 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -37,6 +37,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.io.IOException;
+
 /**
  * The SoundTriggerModule provides APIs to control sound models and sound detection
  * on a given sound trigger hardware module.
@@ -137,13 +139,39 @@
             if (model instanceof SoundTrigger.GenericSoundModel) {
                 SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel(
                         (SoundTrigger.GenericSoundModel) model);
-                soundModelHandle[0] = mService.loadModel(aidlModel);
+                try {
+                    soundModelHandle[0] = mService.loadModel(aidlModel);
+                } finally {
+                    // TODO(b/219825762): We should be able to use the entire object in a
+                    //  try-with-resources
+                    //   clause, instead of having to explicitly close internal fields.
+                    if (aidlModel.data != null) {
+                        try {
+                            aidlModel.data.close();
+                        } catch (IOException e) {
+                            Log.e(TAG, "Failed to close file", e);
+                        }
+                    }
+                }
                 return SoundTrigger.STATUS_OK;
             }
             if (model instanceof SoundTrigger.KeyphraseSoundModel) {
                 PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel(
                         (SoundTrigger.KeyphraseSoundModel) model);
-                soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+                try {
+                    soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
+                } finally {
+                    // TODO(b/219825762): We should be able to use the entire object in a
+                    //  try-with-resources
+                    //   clause, instead of having to explicitly close internal fields.
+                    if (aidlModel.common.data != null) {
+                        try {
+                            aidlModel.common.data.close();
+                        } catch (IOException e) {
+                            Log.e(TAG, "Failed to close file", e);
+                        }
+                    }
+                }
                 return SoundTrigger.STATUS_OK;
             }
             return SoundTrigger.STATUS_BAD_VALUE;
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 6b869f1..043e688 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -106,6 +106,9 @@
     /** @hide */
     public static final long TRACE_TAG_RRO = 1L << 26;
     /** @hide */
+    public static final long TRACE_TAG_THERMAL = 1L << 27;
+    /** @hide */
+
     public static final long TRACE_TAG_APEX_MANAGER = 1L << 18;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
diff --git a/core/java/android/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl
index 02db274..29b4570 100644
--- a/core/java/android/os/logcat/ILogcatManagerService.aidl
+++ b/core/java/android/os/logcat/ILogcatManagerService.aidl
@@ -19,10 +19,54 @@
 /**
  * @hide
  */
-interface ILogcatManagerService {
+oneway interface ILogcatManagerService {
+    /**
+     * The function is called by logd to notify LogcatManagerService
+     * that a client makes privileged log data access request.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void startThread(in int uid, in int gid, in int pid, in int fd);
+
+
+    /**
+     * The function is called by logd to notify LogcatManagerService
+     * that a client finished the privileged log data access.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void finishThread(in int uid, in int gid, in int pid, in int fd);
+
+
+    /**
+     * The function is called by UX component to notify
+     * LogcatManagerService that the user approved
+     * the privileged log data access.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void approve(in int uid, in int gid, in int pid, in int fd);
+
+
+    /**
+     * The function is called by UX component to notify
+     * LogcatManagerService that the user declined
+     * the privileged log data access.
+     *
+     * @param uid The UID of client who makes the request.
+     * @param gid The GID of client who makes the request.
+     * @param pid The PID of client who makes the request.
+     * @param fd  The FD (Socket) of client who makes the request.
+     */
     void decline(in int uid, in int gid, in int pid, in int fd);
 }
 
diff --git a/core/java/android/permission/PermGroupUsage.java b/core/java/android/permission/PermGroupUsage.java
deleted file mode 100644
index 440d6f2..0000000
--- a/core/java/android/permission/PermGroupUsage.java
+++ /dev/null
@@ -1,128 +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 android.permission;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-
-/**
- * Represents the usage of a permission group by an app. Supports package name, user, permission
- * group, whether or not the access is running or recent, whether the access is tied to a phone
- * call, and an optional special attribution.
- *
- * @hide
- */
-@TestApi
-public final class PermGroupUsage {
-
-    private final String mPackageName;
-    private final int mUid;
-    private final long mLastAccess;
-    private final String mPermGroupName;
-    private final boolean mIsActive;
-    private final boolean mIsPhoneCall;
-    private final CharSequence mAttribution;
-
-    /**
-     *
-     * @param packageName The package name of the using app
-     * @param uid The uid of the using app
-     * @param permGroupName The name of the permission group being used
-     * @param lastAccess The time of last access
-     * @param isActive Whether this is active
-     * @param isPhoneCall Whether this is a usage by the phone
-     * @param attribution An optional string attribution to show
-     * @hide
-     */
-    @TestApi
-    public PermGroupUsage(@NonNull String packageName, int uid,
-            @NonNull String permGroupName, long lastAccess, boolean isActive, boolean isPhoneCall,
-            @Nullable CharSequence attribution) {
-        this.mPackageName = packageName;
-        this.mUid = uid;
-        this.mPermGroupName = permGroupName;
-        this.mLastAccess = lastAccess;
-        this.mIsActive = isActive;
-        this.mIsPhoneCall = isPhoneCall;
-        this.mAttribution = attribution;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public @NonNull String getPackageName() {
-        return mPackageName;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public int getUid() {
-        return mUid;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public @NonNull String getPermGroupName() {
-        return mPermGroupName;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public long getLastAccess() {
-        return mLastAccess;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public boolean isActive() {
-        return mIsActive;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public boolean isPhoneCall() {
-        return mIsPhoneCall;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public @Nullable CharSequence getAttribution() {
-        return mAttribution;
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this))
-                + " packageName: " + mPackageName + ", UID: " + mUid + ", permGroup: "
-                + mPermGroupName + ", lastAccess: " + mLastAccess + ", isActive: " + mIsActive
-                + ", attribution: " + mAttribution;
-    }
-}
diff --git a/core/java/android/permission/PermissionGroupUsage.aidl b/core/java/android/permission/PermissionGroupUsage.aidl
new file mode 100644
index 0000000..f18698a
--- /dev/null
+++ b/core/java/android/permission/PermissionGroupUsage.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2022, 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.permission;
+
+parcelable PermissionGroupUsage;
\ No newline at end of file
diff --git a/core/java/android/permission/PermissionGroupUsage.java b/core/java/android/permission/PermissionGroupUsage.java
new file mode 100644
index 0000000..49b7463
--- /dev/null
+++ b/core/java/android/permission/PermissionGroupUsage.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2022 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.permission;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Represents the usage of a permission group by an app. Supports package name, user, permission
+ * group, whether or not the access is running or recent, whether the access is tied to a phone
+ * call, and an optional special attribution tag, label and proxy label.
+ *
+ * @hide
+ */
+@SystemApi
+@DataClass(
+        genHiddenConstructor = true,
+        genEqualsHashCode = true,
+        genToString = true
+)
+public final class PermissionGroupUsage implements Parcelable {
+
+    private final @NonNull String mPackageName;
+    private final int mUid;
+    private final long mLastAccessTimeMillis;
+    private final @NonNull String mPermissionGroupName;
+    private final boolean mActive;
+    private final boolean mPhoneCall;
+    private final @Nullable CharSequence mAttributionTag;
+    private final @Nullable CharSequence mAttributionLabel;
+    private final @Nullable CharSequence mProxyLabel;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/permission/PermissionGroupUsage.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new PermissionGroupUsage.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public PermissionGroupUsage(
+            @NonNull String packageName,
+            int uid,
+            long lastAccessTimeMillis,
+            @NonNull String permissionGroupName,
+            boolean active,
+            boolean phoneCall,
+            @Nullable CharSequence attributionTag,
+            @Nullable CharSequence attributionLabel,
+            @Nullable CharSequence proxyLabel) {
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mUid = uid;
+        this.mLastAccessTimeMillis = lastAccessTimeMillis;
+        this.mPermissionGroupName = permissionGroupName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPermissionGroupName);
+        this.mActive = active;
+        this.mPhoneCall = phoneCall;
+        this.mAttributionTag = attributionTag;
+        this.mAttributionLabel = attributionLabel;
+        this.mProxyLabel = proxyLabel;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * @return Package name for the usage
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * @return UID for the usage
+     */
+    @DataClass.Generated.Member
+    public int getUid() {
+        return mUid;
+    }
+
+    /**
+     * @return Last access time in millis for the usage
+     */
+    @CurrentTimeMillisLong
+    @DataClass.Generated.Member
+    public long getLastAccessTimeMillis() {
+        return mLastAccessTimeMillis;
+    }
+
+    /**
+     * @return Permission group name for the usage
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPermissionGroupName() {
+        return mPermissionGroupName;
+    }
+
+    /**
+     * @return If usage is active
+     */
+    @DataClass.Generated.Member
+    public boolean isActive() {
+        return mActive;
+    }
+
+    /**
+     * @return If usage is a phone call
+     */
+    @DataClass.Generated.Member
+    public boolean isPhoneCall() {
+        return mPhoneCall;
+    }
+
+    /**
+     * @return Attribution tag associated with the usage
+     */
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getAttributionTag() {
+        return mAttributionTag;
+    }
+
+    /**
+     * @return Attribution label associated with the usage
+     */
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getAttributionLabel() {
+        return mAttributionLabel;
+    }
+
+    /**
+     * @return Proxy label associated with the usage
+     */
+    @DataClass.Generated.Member
+    public @Nullable CharSequence getProxyLabel() {
+        return mProxyLabel;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "PermissionGroupUsage { " +
+                "packageName = " + mPackageName + ", " +
+                "uid = " + mUid + ", " +
+                "lastAccessTimeMillis = " + mLastAccessTimeMillis + ", " +
+                "permissionGroupName = " + mPermissionGroupName + ", " +
+                "active = " + mActive + ", " +
+                "phoneCall = " + mPhoneCall + ", " +
+                "attributionTag = " + mAttributionTag + ", " +
+                "attributionLabel = " + mAttributionLabel + ", " +
+                "proxyLabel = " + mProxyLabel +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(PermissionGroupUsage other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        PermissionGroupUsage that = (PermissionGroupUsage) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mPackageName, that.mPackageName)
+                && mUid == that.mUid
+                && mLastAccessTimeMillis == that.mLastAccessTimeMillis
+                && java.util.Objects.equals(mPermissionGroupName, that.mPermissionGroupName)
+                && mActive == that.mActive
+                && mPhoneCall == that.mPhoneCall
+                && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
+                && java.util.Objects.equals(mAttributionLabel, that.mAttributionLabel)
+                && java.util.Objects.equals(mProxyLabel, that.mProxyLabel);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+        _hash = 31 * _hash + mUid;
+        _hash = 31 * _hash + Long.hashCode(mLastAccessTimeMillis);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPermissionGroupName);
+        _hash = 31 * _hash + Boolean.hashCode(mActive);
+        _hash = 31 * _hash + Boolean.hashCode(mPhoneCall);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionLabel);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mProxyLabel);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        int flg = 0;
+        if (mActive) flg |= 0x10;
+        if (mPhoneCall) flg |= 0x20;
+        if (mAttributionTag != null) flg |= 0x40;
+        if (mAttributionLabel != null) flg |= 0x80;
+        if (mProxyLabel != null) flg |= 0x100;
+        dest.writeInt(flg);
+        dest.writeString(mPackageName);
+        dest.writeInt(mUid);
+        dest.writeLong(mLastAccessTimeMillis);
+        dest.writeString(mPermissionGroupName);
+        if (mAttributionTag != null) dest.writeCharSequence(mAttributionTag);
+        if (mAttributionLabel != null) dest.writeCharSequence(mAttributionLabel);
+        if (mProxyLabel != null) dest.writeCharSequence(mProxyLabel);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ PermissionGroupUsage(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        int flg = in.readInt();
+        boolean active = (flg & 0x10) != 0;
+        boolean phoneCall = (flg & 0x20) != 0;
+        String packageName = in.readString();
+        int uid = in.readInt();
+        long lastAccessTimeMillis = in.readLong();
+        String permissionGroupName = in.readString();
+        CharSequence attributionTag = (flg & 0x40) == 0 ? null : (CharSequence) in.readCharSequence();
+        CharSequence attributionLabel = (flg & 0x80) == 0 ? null : (CharSequence) in.readCharSequence();
+        CharSequence proxyLabel = (flg & 0x100) == 0 ? null : (CharSequence) in.readCharSequence();
+
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mUid = uid;
+        this.mLastAccessTimeMillis = lastAccessTimeMillis;
+        this.mPermissionGroupName = permissionGroupName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPermissionGroupName);
+        this.mActive = active;
+        this.mPhoneCall = phoneCall;
+        this.mAttributionTag = attributionTag;
+        this.mAttributionLabel = attributionLabel;
+        this.mProxyLabel = proxyLabel;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<PermissionGroupUsage> CREATOR
+            = new Parcelable.Creator<PermissionGroupUsage>() {
+        @Override
+        public PermissionGroupUsage[] newArray(int size) {
+            return new PermissionGroupUsage[size];
+        }
+
+        @Override
+        public PermissionGroupUsage createFromParcel(@NonNull android.os.Parcel in) {
+            return new PermissionGroupUsage(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1645067417023L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/permission/PermissionGroupUsage.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final  int mUid\nprivate final  long mLastAccessTimeMillis\nprivate final @android.annotation.NonNull java.lang.String mPermissionGroupName\nprivate final  boolean mActive\nprivate final  boolean mPhoneCall\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionTag\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionLabel\nprivate final @android.annotation.Nullable java.lang.CharSequence mProxyLabel\nclass PermissionGroupUsage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index fc7ac11..c509de6 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -193,6 +193,17 @@
      */
     public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false;
 
+    /**
+     * Intent extra: List of PermissionGroupUsages
+     * <p>
+     * Type: {@code List<PermissionGroupUsage>}
+     * </p>
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PERMISSION_USAGES =
+            "android.permission.extra.PERMISSION_USAGES";
+
     private final @NonNull Context mContext;
 
     private final IPackageManager mPackageManager;
@@ -1108,7 +1119,7 @@
     @TestApi
     @NonNull
     @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
-    public List<PermGroupUsage> getIndicatorAppOpUsageData() {
+    public List<PermissionGroupUsage> getIndicatorAppOpUsageData() {
         return getIndicatorAppOpUsageData(new AudioManager().isMicrophoneMute());
     }
 
@@ -1122,7 +1133,7 @@
     @TestApi
     @NonNull
     @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
-    public List<PermGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
+    public List<PermissionGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
         // Lazily initialize the usage helper
         initializeUsageHelper();
         return mUsageHelper.getOpUsageData(micMuted);
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 658e033..0b57842 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -42,7 +42,10 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.Attribution;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.icu.text.ListFormatter;
 import android.media.AudioManager;
 import android.os.Process;
@@ -70,20 +73,30 @@
 public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener,
         AppOpsManager.OnOpStartedListener {
 
-    /** Whether to show the mic and camera icons.  */
+    /**
+     * Whether to show the mic and camera icons.
+     */
     private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled";
 
-    /** Whether to show the location indicators. */
+    /**
+     * Whether to show the location indicators.
+     */
     private static final String PROPERTY_LOCATION_INDICATORS_ENABLED =
             "location_indicators_enabled";
 
-    /** Whether to show the Permissions Hub.  */
+    /**
+     * Whether to show the Permissions Hub.
+     */
     private static final String PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled";
 
-    /** How long after an access to show it as "recent" */
+    /**
+     * How long after an access to show it as "recent"
+     */
     private static final String RECENT_ACCESS_TIME_MS = "recent_access_time_ms";
 
-    /** How long after an access to show it as "running" */
+    /**
+     * How long after an access to show it as "running"
+     */
     private static final String RUNNING_ACCESS_TIME_MS = "running_access_time_ms";
 
     private static final String SYSTEM_PKG = "android";
@@ -132,7 +145,7 @@
     );
 
     private static @NonNull String getGroupForOp(String op) {
-        switch(op) {
+        switch (op) {
             case OPSTR_RECORD_AUDIO:
                 return MICROPHONE;
             case OPSTR_CAMERA:
@@ -158,6 +171,7 @@
 
     /**
      * Constructor for PermissionUsageHelper
+     *
      * @param context The context from which to derive the package information
      */
     public PermissionUsageHelper(@NonNull Context context) {
@@ -167,9 +181,9 @@
         mUserContexts = new ArrayMap<>();
         mUserContexts.put(Process.myUserHandle(), mContext);
         // TODO ntmyren: make this listen for flag enable/disable changes
-        String[] opStrs = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO };
+        String[] opStrs = {OPSTR_CAMERA, OPSTR_RECORD_AUDIO};
         mAppOpsManager.startWatchingActive(opStrs, context.getMainExecutor(), this);
-        int[] ops = { OP_CAMERA, OP_RECORD_AUDIO };
+        int[] ops = {OP_CAMERA, OP_RECORD_AUDIO};
         mAppOpsManager.startWatchingStarted(ops, this);
     }
 
@@ -225,8 +239,8 @@
 
     @Override
     public void onOpStarted(int op, int uid, String packageName, String attributionTag,
-                @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
-       // not part of an attribution chain. Do nothing
+            @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
+        // not part of an attribution chain. Do nothing
     }
 
     @Override
@@ -274,8 +288,8 @@
     /**
      * @see PermissionManager.getIndicatorAppOpUsageData
      */
-    public @NonNull List<PermGroupUsage> getOpUsageData(boolean isMicMuted) {
-        List<PermGroupUsage> usages = new ArrayList<>();
+    public @NonNull List<PermissionGroupUsage> getOpUsageData(boolean isMicMuted) {
+        List<PermissionGroupUsage> usages = new ArrayList<>();
 
         if (!shouldShowIndicators()) {
             return usages;
@@ -313,6 +327,9 @@
             }
         }
 
+        // map of package name -> map of attribution tag -> attribution labels
+        ArrayMap<String, Map<String, String>> subAttributionLabelsMap = new ArrayMap<>();
+
         for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) {
             boolean isPhone = false;
             String permGroup = usedPermGroups.get(permGroupNum);
@@ -320,6 +337,8 @@
             ArrayMap<OpUsage, CharSequence> usagesWithLabels =
                     getUniqueUsagesWithLabels(permGroup, rawUsages.get(permGroup));
 
+            updateSubattributionLabelsMap(rawUsages.get(permGroup), subAttributionLabelsMap);
+
             if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
                 isPhone = true;
                 permGroup = MICROPHONE;
@@ -330,15 +349,86 @@
 
             for (int usageNum = 0; usageNum < usagesWithLabels.size(); usageNum++) {
                 OpUsage usage = usagesWithLabels.keyAt(usageNum);
-                usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup,
-                        usage.lastAccessTime, usage.isRunning, isPhone,
-                        usagesWithLabels.valueAt(usageNum)));
+                String attributionLabel = subAttributionLabelsMap.getOrDefault(usage.packageName,
+                        new ArrayMap<>()).getOrDefault(usage.attributionTag, null);
+                usages.add(
+                        new PermissionGroupUsage(usage.packageName, usage.uid, usage.lastAccessTime,
+                                permGroup,
+                                usage.isRunning, isPhone, usage.attributionTag, attributionLabel,
+                                usagesWithLabels.valueAt(usageNum)));
             }
         }
 
         return usages;
     }
 
+    private void updateSubattributionLabelsMap(List<OpUsage> usages,
+            ArrayMap<String, Map<String, String>> subAttributionLabelsMap) {
+        if (usages == null || usages.isEmpty()) {
+            return;
+        }
+        for (OpUsage usage : usages) {
+            if (usage.attributionTag != null && !subAttributionLabelsMap.containsKey(
+                    usage.packageName)) {
+                subAttributionLabelsMap.put(usage.packageName,
+                        getSubattributionLabelsForPackage(usage.packageName, usage.uid));
+            }
+        }
+    }
+
+    /**
+     * Query attribution labels for a package
+     *
+     * @param packageName
+     * @param uid
+     * @return map of attribution tag -> attribution labels for a package
+     */
+    private ArrayMap<String, String> getSubattributionLabelsForPackage(String packageName,
+            int uid) {
+        ArrayMap<String, String> attributionLabelMap = new ArrayMap<>();
+        UserHandle user = UserHandle.getUserHandleForUid(uid);
+        try {
+            if (!isSubattributionSupported(packageName, uid)) {
+                return attributionLabelMap;
+            }
+            Context userContext = getUserContext(user);
+            PackageInfo packageInfo = userContext.getPackageManager().getPackageInfo(
+                    packageName,
+                    PackageManager.GET_PERMISSIONS | PackageManager.GET_ATTRIBUTIONS);
+            Context pkgContext = userContext.createPackageContext(packageInfo.packageName, 0);
+            for (Attribution attribution : packageInfo.attributions) {
+                try {
+                    String resourceForLabel = pkgContext.getString(attribution.getLabel());
+                    attributionLabelMap.put(attribution.getTag(), resourceForLabel);
+                } catch (Resources.NotFoundException e) {
+                    // Shouldn't happen, do nothing
+                }
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // Did not find the package, do nothing
+        }
+        return attributionLabelMap;
+    }
+
+    /**
+     * Returns true if the app supports subattribution.
+     */
+    private boolean isSubattributionSupported(String packageName, int uid) {
+        try {
+            PackageManager userPkgManager =
+                    getUserContext(UserHandle.getUserHandleForUid(uid)).getPackageManager();
+            ApplicationInfo appInfo = userPkgManager.getApplicationInfoAsUser(packageName,
+                    PackageManager.ApplicationInfoFlags.of(0),
+                    UserHandle.getUserId(uid));
+            if (appInfo != null) {
+                return appInfo.areAttributionsUserVisible();
+            }
+            return false;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
     /**
      * Get the raw usages from the system, and then parse out the ones that are not recent enough,
      * determine which permission group each belongs in, and removes duplicates (if the same app
@@ -346,7 +436,6 @@
      * running/recent info, if the usage is a phone call, per permission group.
      *
      * @param opNames a list of op names to get usage for
-     *
      * @return A map of permission group -> list of usages that are recent or running
      */
     private Map<String, List<OpUsage>> getOpUsages(List<String> opNames) {
@@ -378,6 +467,7 @@
                 List<String> attributionTags =
                         new ArrayList<>(opEntry.getAttributedOpEntries().keySet());
 
+
                 int numAttrEntries = opEntry.getAttributedOpEntries().size();
                 for (int attrOpEntryNum = 0; attrOpEntryNum < numAttrEntries; attrOpEntryNum++) {
                     String attributionTag = attributionTags.get(attrOpEntryNum);
@@ -456,6 +546,7 @@
         ArrayMap<OpUsage, ArrayList<CharSequence>> proxyLabels = new ArrayMap<>();
         // map of usage.proxy hash -> usage hash, telling us if a usage is a proxy
         ArrayMap<Integer, OpUsage> proxies = new ArrayMap<>();
+
         for (int i = 0; i < usages.size(); i++) {
             OpUsage usage = usages.get(i);
             allUsages.put(usage.getPackageIdHash(), usage);
@@ -588,7 +679,6 @@
                     } catch (PackageManager.NameNotFoundException e) {
                         // do nothing
                     }
-
                 }
                 usagesAndLabels.put(start.usage, proxyLabel);
             }
diff --git a/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
index 08a7205..f028ed3 100644
--- a/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
+++ b/core/java/android/service/selectiontoolbar/DefaultSelectionToolbarRenderService.java
@@ -24,6 +24,8 @@
 import android.util.SparseArray;
 import android.view.selectiontoolbar.ShowInfo;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.UUID;
 
 /**
@@ -130,5 +132,22 @@
             }
         }
     }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        int size = mToolbarCache.size();
+        pw.print("number selectionToolbar: "); pw.println(size);
+        String pfx = "  ";
+        for (int i = 0; i < size; i++) {
+            pw.print("#"); pw.println(i);
+            int callingUid = mToolbarCache.keyAt(i);
+            pw.print(pfx); pw.print("callingUid: "); pw.println(callingUid);
+            Pair<Long, RemoteSelectionToolbar> toolbarPair = mToolbarCache.valueAt(i);
+            RemoteSelectionToolbar selectionToolbar = toolbarPair.second;
+            pw.print(pfx); pw.print("selectionToolbar: ");
+            selectionToolbar.dump(pfx, pw);
+            pw.println();
+        }
+    }
 }
 
diff --git a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
index 04491f0..8fe6f71 100644
--- a/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
+++ b/core/java/android/service/selectiontoolbar/FloatingToolbarRoot.java
@@ -24,6 +24,8 @@
 import android.view.MotionEvent;
 import android.widget.LinearLayout;
 
+import java.io.PrintWriter;
+
 /**
  * This class is the root view for the selection toolbar. It is responsible for
  * detecting the click on the item and to also transfer input focus to the application.
@@ -40,6 +42,9 @@
     private final SelectionToolbarRenderService.TransferTouchListener mTransferTouchListener;
     private Rect mContentRect;
 
+    private int mLastDownX = -1;
+    private int mLastDownY = -1;
+
     public FloatingToolbarRoot(Context context, IBinder targetInputToken,
             SelectionToolbarRenderService.TransferTouchListener transferTouchListener) {
         super(context);
@@ -59,13 +64,13 @@
     @SuppressLint("ClickableViewAccessibility")
     public boolean dispatchTouchEvent(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            int downX = (int) event.getX();
-            int downY = (int) event.getY();
+            mLastDownX = (int) event.getX();
+            mLastDownY = (int) event.getY();
             if (DEBUG) {
-                Log.d(TAG, "downX=" + downX + " downY=" + downY);
+                Log.d(TAG, "downX=" + mLastDownX + " downY=" + mLastDownY);
             }
             // TODO(b/215497659): Check FLAG_WINDOW_IS_PARTIALLY_OBSCURED
-            if (!mContentRect.contains(downX, downY)) {
+            if (!mContentRect.contains(mLastDownX, mLastDownY)) {
                 if (DEBUG) {
                     Log.d(TAG, "Transfer touch focus to application.");
                 }
@@ -75,4 +80,10 @@
         }
         return super.dispatchTouchEvent(event);
     }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.println("FloatingToolbarRoot:");
+        pw.print(prefix + "  "); pw.print("last down X: "); pw.println(mLastDownX);
+        pw.print(prefix + "  "); pw.print("last down Y: "); pw.println(mLastDownY);
+    }
 }
diff --git a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
index 9a11923..d75fbc0 100644
--- a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
+++ b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
@@ -58,6 +58,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
 
+import java.io.PrintWriter;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
@@ -1372,4 +1373,32 @@
             Log.v(TAG, message);
         }
     }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("toolbar token: "); pw.println(mSelectionToolbarToken);
+        pw.print(prefix); pw.print("dismissed: "); pw.println(mDismissed);
+        pw.print(prefix); pw.print("hidden: "); pw.println(mHidden);
+        pw.print(prefix); pw.print("popup width: "); pw.println(mPopupWidth);
+        pw.print(prefix); pw.print("popup height: "); pw.println(mPopupHeight);
+        pw.print(prefix); pw.print("relative coords: "); pw.println(mRelativeCoordsForToolbar);
+        pw.print(prefix); pw.print("main panel size: "); pw.println(mMainPanelSize);
+        boolean hasOverflow = hasOverflow();
+        pw.print(prefix); pw.print("has overflow: "); pw.println(hasOverflow);
+        if (hasOverflow) {
+            pw.print(prefix); pw.print("overflow open: "); pw.println(mIsOverflowOpen);
+            pw.print(prefix); pw.print("overflow size: "); pw.println(mOverflowPanelSize);
+        }
+        if (mSurfaceControlViewHost != null) {
+            FloatingToolbarRoot root = (FloatingToolbarRoot) mSurfaceControlViewHost.getView();
+            root.dump(prefix, pw);
+        }
+        if (mMenuItems != null) {
+            int menuItemSize = mMenuItems.size();
+            pw.print(prefix); pw.print("number menu items: "); pw.println(menuItemSize);
+            for (int i = 0; i < menuItemSize; i++) {
+                pw.print(prefix); pw.print("#"); pw.println(i);
+                pw.print(prefix + "  "); pw.println(mMenuItems.get(i));
+            }
+        }
+    }
 }
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 271e307..cd18c19 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -32,21 +32,6 @@
  * Constants for supporting speech recognition through starting an {@link Intent}
  */
 public class RecognizerIntent {
-    /**
-     * The extra key used in an intent to the speech recognizer for voice search. Not
-     * generally to be used by developers. The system search dialog uses this, for example,
-     * to set a calling package for identification by a voice search API. If this extra
-     * is set by anyone but the system process, it should be overridden by the voice search
-     * implementation.
-     */
-    public static final String EXTRA_CALLING_PACKAGE = "calling_package";
-
-    /**
-     * The extra key used in an intent which is providing an already opened audio source for the
-     * RecognitionService to use. Data should be a URI to an audio resource.
-     */
-    public static final String EXTRA_AUDIO_INJECT_SOURCE =
-            "android.speech.extra.AUDIO_INJECT_SOURCE";
 
     private RecognizerIntent() {
         // Not for instantiating.
@@ -58,7 +43,7 @@
      * {@link Activity#onActivityResult}, if you start the intent using
      * {@link Activity#startActivityForResult(Intent, int)}), or forwarded via a PendingIntent
      * if one is provided.
-     * 
+     *
      * <p>Starting this intent with just {@link Activity#startActivity(Intent)} is not supported.
      * You must either use {@link Activity#startActivityForResult(Intent, int)}, or provide a
      * PendingIntent, to receive recognition results.
@@ -70,7 +55,7 @@
      * <ul>
      *   <li>{@link #EXTRA_LANGUAGE_MODEL}
      * </ul>
-     * 
+     *
      * <p>Optional extras:
      * <ul>
      *   <li>{@link #EXTRA_PROMPT}
@@ -79,12 +64,12 @@
      *   <li>{@link #EXTRA_RESULTS_PENDINGINTENT}
      *   <li>{@link #EXTRA_RESULTS_PENDINGINTENT_BUNDLE}
      * </ul>
-     * 
+     *
      * <p> Result extras (returned in the result, not to be specified in the request):
      * <ul>
      *   <li>{@link #EXTRA_RESULTS}
      * </ul>
-     * 
+     *
      * <p>NOTE: There may not be any applications installed to handle this action, so you should
      * make sure to catch {@link ActivityNotFoundException}.
      */
@@ -97,12 +82,12 @@
      *
      * <p>If you want to avoid triggering any type of action besides web search, you can use
      * the {@link #EXTRA_WEB_SEARCH_ONLY} extra.
-     * 
+     *
      * <p>Required extras:
      * <ul>
      *   <li>{@link #EXTRA_LANGUAGE_MODEL}
      * </ul>
-     * 
+     *
      * <p>Optional extras:
      * <ul>
      *   <li>{@link #EXTRA_PROMPT}
@@ -112,13 +97,13 @@
      *   <li>{@link #EXTRA_WEB_SEARCH_ONLY}
      *   <li>{@link #EXTRA_ORIGIN}
      * </ul>
-     * 
+     *
      * <p> Result extras (returned in the result, not to be specified in the request):
      * <ul>
      *   <li>{@link #EXTRA_RESULTS}
      *   <li>{@link #EXTRA_CONFIDENCE_SCORES} (optional)
      * </ul>
-     * 
+     *
      * <p>NOTE: There may not be any applications installed to handle this action, so you should
      * make sure to catch {@link ActivityNotFoundException}.
      */
@@ -157,6 +142,129 @@
             "android.speech.action.VOICE_SEARCH_HANDS_FREE";
 
     /**
+     * Optional {@link android.os.ParcelFileDescriptor} pointing to an already opened audio
+     * source for the recognizer to use. The caller of the recognizer is responsible for closing
+     * the audio. If this extra is not set or the recognizer does not support this feature, the
+     * recognizer will open the mic for audio and close it when the recognition is finished.
+     *
+     * <p>Along with this extra, please send {@link #EXTRA_AUDIO_SOURCE_CHANNEL_COUNT},
+     * {@link #EXTRA_AUDIO_SOURCE_ENCODING}, and {@link #EXTRA_AUDIO_SOURCE_SAMPLING_RATE}
+     * extras, otherwise the default values of these extras will be used.
+     *
+     * <p>Additionally, {@link #EXTRA_ENABLE_BIASING_DEVICE_CONTEXT} may have no effect when this
+     * extra is set.
+     *
+     * <p>This can also be used as the string value for {@link #EXTRA_SEGMENTED_SESSION} to
+     * enable segmented session mode. The audio must be passed in using this extra. The
+     * recognition session will end when and only when the audio is closed.
+     *
+     * @see #EXTRA_SEGMENTED_SESSION
+     */
+    public static final String EXTRA_AUDIO_SOURCE = "android.speech.extra.AUDIO_SOURCE";
+
+    /**
+     * Optional integer, to be used with {@link #EXTRA_AUDIO_SOURCE}, to indicate the number of
+     * channels in the audio. The default value is 1.
+     */
+    public static final String EXTRA_AUDIO_SOURCE_CHANNEL_COUNT =
+            "android.speech.extra.AUDIO_SOURCE_CHANNEL_COUNT";
+
+    /**
+     * Optional integer (from {@link android.media.AudioFormat}), to be used with
+     * {@link #EXTRA_AUDIO_SOURCE}, to indicate the audio encoding. The default value is
+     * {@link android.media.AudioFormat#ENCODING_PCM_16BIT}.
+     */
+    public static final String EXTRA_AUDIO_SOURCE_ENCODING =
+            "android.speech.extra.AUDIO_SOURCE_ENCODING";
+
+    /**
+     * Optional integer, to be used with {@link #EXTRA_AUDIO_SOURCE}, to indicate the sampling
+     * rate of the audio. The default value is 16000.
+     */
+    public static final String EXTRA_AUDIO_SOURCE_SAMPLING_RATE =
+            "android.speech.extra.AUDIO_SOURCE_SAMPLING_RATE";
+
+    /**
+     * Optional boolean to enable biasing towards device context. The recognizer will use the
+     * device context to tune the recognition results.
+     *
+     * <p>Depending on the recognizer implementation, this value may have no effect.
+     */
+    public static final String EXTRA_ENABLE_BIASING_DEVICE_CONTEXT =
+            "android.speech.extra.ENABLE_BIASING_DEVICE_CONTEXT";
+
+    /**
+     * Optional list of strings, towards which the recognizer should bias the recognition results.
+     * These are separate from the device context.
+     */
+    public static final String EXTRA_BIASING_STRINGS = "android.speech.extra.BIASING_STRINGS";
+
+    /**
+     * Optional string to enable text formatting (e.g. unspoken punctuation (examples: question
+     * mark, comma, period, etc.), capitalization, etc.) and specify the optimization strategy.
+     * If set, the partial and final result texts will be formatted. Each result list will
+     * contain two hypotheses in the order of 1) formatted text 2) raw text.
+     *
+     * <p>Depending on the recognizer implementation, this value may have no effect.
+     *
+     * @see #FORMATTING_OPTIMIZE_QUALITY
+     * @see #FORMATTING_OPTIMIZE_LATENCY
+     */
+    public static final String EXTRA_ENABLE_FORMATTING = "android.speech.extra.ENABLE_FORMATTING";
+
+    /**
+     * Optimizes formatting quality. This will increase latency but provide the highest
+     * punctuation quality. This is a value to use for {@link #EXTRA_ENABLE_FORMATTING}.
+     *
+     * @see #EXTRA_ENABLE_FORMATTING
+     */
+    public static final String FORMATTING_OPTIMIZE_QUALITY = "quality";
+    /**
+     * Optimizes formatting latency. This will result in a slightly lower quality of punctuation
+     * but can improve the experience for real-time use cases. This is a value to use for
+     * {@link #EXTRA_ENABLE_FORMATTING}.
+     *
+     * @see #EXTRA_ENABLE_FORMATTING
+     */
+    public static final String FORMATTING_OPTIMIZE_LATENCY = "latency";
+
+    /**
+     * Optional boolean, to be used with {@link #EXTRA_ENABLE_FORMATTING}, to prevent the
+     * recognizer adding punctuation after the last word of the partial results. The default is
+     * false.
+     */
+    public static final String EXTRA_HIDE_PARTIAL_TRAILING_PUNCTUATION =
+            "android.speech.extra.HIDE_PARTIAL_TRAILING_PUNCTUATION";
+
+    /**
+     * Optional boolean indicating whether the recognizer should mask the offensive words in
+     * recognition results. The Default is true.
+     */
+    public static final String EXTRA_MASK_OFFENSIVE_WORDS =
+            "android.speech.extra.MASK_OFFENSIVE_WORDS";
+
+    /**
+     * The extra key used in an intent to the speech recognizer for voice search. Not
+     * generally to be used by developers. The system search dialog uses this, for example,
+     * to set a calling package for identification by a voice search API. If this extra
+     * is set by anyone but the system process, it should be overridden by the voice search
+     * implementation.
+     */
+    public static final String EXTRA_CALLING_PACKAGE = "calling_package";
+
+    /**
+     * The extra key used in an intent which is providing an already opened audio source for the
+     * RecognitionService to use. Data should be a URI to an audio resource.
+     *
+     * <p>Depending on the recognizer implementation, this value may have no effect.
+     *
+     * @deprecated Replaced with {@link #EXTRA_AUDIO_SOURCE}
+     */
+    @Deprecated
+    public static final String EXTRA_AUDIO_INJECT_SOURCE =
+            "android.speech.extra.AUDIO_INJECT_SOURCE";
+
+    /**
      * Optional boolean to indicate that a "hands free" voice search was performed while the device
      * was in a secure mode. An example of secure mode is when the device's screen lock is active,
      * and it requires some form of authentication to be unlocked.
@@ -168,24 +276,29 @@
     public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
 
     /**
-     * The minimum length of an utterance. We will not stop recording before this amount of time.
-     * 
-     * Note that it is extremely rare you'd want to specify this value in an intent. If you don't
-     * have a very good reason to change these, you should leave them as they are. Note also that
-     * certain values may cause undesired or unexpected results - use judiciously! Additionally,
-     * depending on the recognizer implementation, these values may have no effect.
+     * Optional integer to indicate the minimum length of the recognition session. The recognizer
+     * will not stop recognizing speech before this amount of time.
+     *
+     * <p>Note that it is extremely rare you'd want to specify this value in an intent.
+     * Generally, it should be specified only when it is also used as the value for
+     * {@link #EXTRA_SEGMENTED_SESSION} to enable segmented session mode. Note also that certain
+     * values may cause undesired or unexpected results - use judiciously!
+     *
+     * <p>Depending on the recognizer implementation, these values may have no effect.
      */
     public static final String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS =
             "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
 
     /**
-     * The amount of time that it should take after we stop hearing speech to consider the input
-     * complete. 
-     * 
-     * Note that it is extremely rare you'd want to specify this value in an intent. If
-     * you don't have a very good reason to change these, you should leave them as they are. Note
-     * also that certain values may cause undesired or unexpected results - use judiciously!
-     * Additionally, depending on the recognizer implementation, these values may have no effect.
+     * The amount of time that it should take after the recognizer stops hearing speech to
+     * consider the input complete hence end the recognition session.
+     *
+     * <p>Note that it is extremely rare you'd want to specify this value in an intent.
+     * Generally, it should be specified only when it is also used as the value for
+     * {@link #EXTRA_SEGMENTED_SESSION} to enable segmented session mode. Note also that certain
+     * values may cause undesired or unexpected results - use judiciously!
+     *
+     * <p>Depending on the recognizer implementation, these values may have no effect.
      */
     public static final String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS =
             "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
@@ -193,8 +306,8 @@
     /**
      * The amount of time that it should take after we stop hearing speech to consider the input
      * possibly complete. This is used to prevent the endpointer cutting off during very short
-     * mid-speech pauses. 
-     * 
+     * mid-speech pauses.
+     *
      * Note that it is extremely rare you'd want to specify this value in an intent. If
      * you don't have a very good reason to change these, you should leave them as they are. Note
      * also that certain values may cause undesired or unexpected results - use judiciously!
@@ -208,21 +321,21 @@
      * {@link #ACTION_RECOGNIZE_SPEECH}. The recognizer uses this
      * information to fine tune the results. This extra is required. Activities implementing
      * {@link #ACTION_RECOGNIZE_SPEECH} may interpret the values as they see fit.
-     * 
+     *
      *  @see #LANGUAGE_MODEL_FREE_FORM
      *  @see #LANGUAGE_MODEL_WEB_SEARCH
      */
     public static final String EXTRA_LANGUAGE_MODEL = "android.speech.extra.LANGUAGE_MODEL";
 
-    /** 
-     * Use a language model based on free-form speech recognition.  This is a value to use for 
-     * {@link #EXTRA_LANGUAGE_MODEL}. 
+    /**
+     * Use a language model based on free-form speech recognition.  This is a value to use for
+     * {@link #EXTRA_LANGUAGE_MODEL}.
      * @see #EXTRA_LANGUAGE_MODEL
      */
     public static final String LANGUAGE_MODEL_FREE_FORM = "free_form";
-    /** 
-     * Use a language model based on web search terms.  This is a value to use for 
-     * {@link #EXTRA_LANGUAGE_MODEL}. 
+    /**
+     * Use a language model based on web search terms.  This is a value to use for
+     * {@link #EXTRA_LANGUAGE_MODEL}.
      * @see #EXTRA_LANGUAGE_MODEL
      */
     public static final String LANGUAGE_MODEL_WEB_SEARCH = "web_search";
@@ -236,7 +349,7 @@
      * {@link java.util.Locale#getDefault()}.
      */
     public static final String EXTRA_LANGUAGE = "android.speech.extra.LANGUAGE";
-    
+
     /**
      * Optional value which can be used to indicate the referer url of a page in which
      * speech was requested. For example, a web browser may choose to provide this for
@@ -244,12 +357,12 @@
      */
     public static final String EXTRA_ORIGIN = "android.speech.extra.ORIGIN";
 
-    /** 
+    /**
      * Optional limit on the maximum number of results to return. If omitted the recognizer
      * will choose how many results to return. Must be an integer.
      */
     public static final String EXTRA_MAX_RESULTS = "android.speech.extra.MAX_RESULTS";
-    
+
     /**
      * Optional boolean, to be used with {@link #ACTION_WEB_SEARCH}, to indicate whether to
      * only fire web searches in response to a user's speech. The default is false, meaning
@@ -267,18 +380,18 @@
     /**
      * When the intent is {@link #ACTION_RECOGNIZE_SPEECH}, the speech input activity will
      * return results to you via the activity results mechanism.  Alternatively, if you use this
-     * extra to supply a PendingIntent, the results will be added to its bundle and the 
+     * extra to supply a PendingIntent, the results will be added to its bundle and the
      * PendingIntent will be sent to its target.
      */
-    public static final String EXTRA_RESULTS_PENDINGINTENT = 
+    public static final String EXTRA_RESULTS_PENDINGINTENT =
             "android.speech.extra.RESULTS_PENDINGINTENT";
-    
+
     /**
      * If you use {@link #EXTRA_RESULTS_PENDINGINTENT} to supply a forwarding intent, you can
      * also use this extra to supply additional extras for the final intent.  The search results
      * will be added to this bundle, and the combined bundle will be sent to the target.
      */
-    public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = 
+    public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE =
             "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
 
     /** Result code returned when no matches are found for the given speech */
@@ -301,7 +414,7 @@
      * the lack of this extra indicates failure.
      */
     public static final String EXTRA_RESULTS = "android.speech.extra.RESULTS";
-    
+
     /**
      * A float array of confidence scores of the recognition results when performing
      * {@link #ACTION_RECOGNIZE_SPEECH}. The array should be the same size as the ArrayList
@@ -317,7 +430,7 @@
      * returned in an activity result.
      */
     public static final String EXTRA_CONFIDENCE_SCORES = "android.speech.extra.CONFIDENCE_SCORES";
-    
+
     /**
      * Returns the broadcast intent to fire with
      * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}
@@ -334,7 +447,7 @@
      * (Whether these are actually provided is up to the particular implementation. It is
      * recommended that {@link Activity}s implementing {@link #ACTION_WEB_SEARCH} provide this
      * information, but it is not required.)
-     * 
+     *
      * @param context a context object
      * @return the broadcast intent to fire or null if not available
      */
@@ -343,15 +456,15 @@
         ResolveInfo ri = context.getPackageManager().resolveActivity(
                 voiceSearchIntent, PackageManager.GET_META_DATA);
         if (ri == null || ri.activityInfo == null || ri.activityInfo.metaData == null) return null;
-        
+
         String className = ri.activityInfo.metaData.getString(DETAILS_META_DATA);
         if (className == null) return null;
-        
+
         Intent detailsIntent = new Intent(ACTION_GET_LANGUAGE_DETAILS);
         detailsIntent.setComponent(new ComponentName(ri.activityInfo.packageName, className));
         return detailsIntent;
     }
-    
+
     /**
      * Meta-data name under which an {@link Activity} implementing {@link #ACTION_WEB_SEARCH} can
      * use to expose the class name of a {@link BroadcastReceiver} which can respond to request for
@@ -370,7 +483,7 @@
      * are required to implement this. Thus retrieving this meta-data may be null.
      */
     public static final String DETAILS_META_DATA = "android.speech.DETAILS";
-    
+
     /**
      * A broadcast intent which can be fired to the {@link BroadcastReceiver} component specified
      * in the meta-data defined in the {@link #DETAILS_META_DATA} meta-data of an
@@ -388,7 +501,7 @@
      */
     public static final String ACTION_GET_LANGUAGE_DETAILS =
             "android.speech.action.GET_LANGUAGE_DETAILS";
-    
+
     /**
      * Specify this boolean extra in a broadcast of {@link #ACTION_GET_LANGUAGE_DETAILS} to
      * indicate that only the current language preference is needed in the response. This
@@ -397,7 +510,7 @@
      */
     public static final String EXTRA_ONLY_RETURN_LANGUAGE_PREFERENCE =
             "android.speech.extra.ONLY_RETURN_LANGUAGE_PREFERENCE";
-    
+
     /**
      * The key to the extra in the {@link Bundle} returned by {@link #ACTION_GET_LANGUAGE_DETAILS}
      * which is a {@link String} that represents the current language preference this user has
@@ -405,7 +518,7 @@
      */
     public static final String EXTRA_LANGUAGE_PREFERENCE =
             "android.speech.extra.LANGUAGE_PREFERENCE";
-    
+
     /**
      * The key to the extra in the {@link Bundle} returned by {@link #ACTION_GET_LANGUAGE_DETAILS}
      * which is an {@link ArrayList} of {@link String}s that represents the languages supported by
@@ -428,14 +541,21 @@
     public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE";
 
     /**
-     * Optional boolean, when true and supported by the recognizer implementation it will split
-     * the recognition results in segments, returned via
-     * {@link RecognitionListener#onSegmentResults(Bundle)} and terminate the session with
-     * {@link RecognitionListener#onEndOfSegmentedSession()}. There will be no call to
-     * {@link RecognitionListener#onResults(Bundle)}. Callers can use
-     * {@link #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS} and
-     * {@link #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS} to tune how long the segments
-     * will be. Defaults to false.
+     * Optional string to enable segmented session mode of the specified type, which can be
+     * {@link #EXTRA_AUDIO_SOURCE}, {@link #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS} or
+     * {@link #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS}. When segmented session mode is
+     * supported by the recognizer implementation and this extra is set, it will return the
+     * recognition results in segments via {@link RecognitionListener#onSegmentResults(Bundle)}
+     * and terminate the session with {@link RecognitionListener#onEndOfSegmentedSession()}.
+     *
+     * <p>When setting this extra, make sure the extra used as the string value here is also set
+     * in the same intent with proper value.
+     *
+     * <p>Depending on the recognizer implementation, this value may have no effect.
+     *
+     * @see #EXTRA_AUDIO_SOURCE
+     * @see #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS
+     * @see #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS
      */
-    public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION";
+    public static final String EXTRA_SEGMENTED_SESSION = "android.speech.extra.SEGMENTED_SESSION";
 }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index fce95c8..5500fb8 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -89,7 +89,9 @@
             ITYPE_BOTTOM_DISPLAY_CUTOUT,
             ITYPE_IME,
             ITYPE_CLIMATE_BAR,
-            ITYPE_EXTRA_NAVIGATION_BAR
+            ITYPE_EXTRA_NAVIGATION_BAR,
+            ITYPE_LOCAL_NAVIGATION_BAR_1,
+            ITYPE_LOCAL_NAVIGATION_BAR_2
     })
     public @interface InternalInsetsType {}
 
@@ -132,7 +134,11 @@
     public static final int ITYPE_CLIMATE_BAR = 20;
     public static final int ITYPE_EXTRA_NAVIGATION_BAR = 21;
 
-    static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
+    /** Additional types for local insets. **/
+    public static final int ITYPE_LOCAL_NAVIGATION_BAR_1 = 22;
+    public static final int ITYPE_LOCAL_NAVIGATION_BAR_2 = 23;
+
+    static final int LAST_TYPE = ITYPE_LOCAL_NAVIGATION_BAR_2;
     public static final int SIZE = LAST_TYPE + 1;
 
     // Derived types
@@ -674,6 +680,8 @@
         if ((types & Type.NAVIGATION_BARS) != 0) {
             result.add(ITYPE_NAVIGATION_BAR);
             result.add(ITYPE_EXTRA_NAVIGATION_BAR);
+            result.add(ITYPE_LOCAL_NAVIGATION_BAR_1);
+            result.add(ITYPE_LOCAL_NAVIGATION_BAR_2);
         }
         if ((types & Type.CAPTION_BAR) != 0) {
             result.add(ITYPE_CAPTION_BAR);
@@ -714,6 +722,8 @@
                 return Type.STATUS_BARS;
             case ITYPE_NAVIGATION_BAR:
             case ITYPE_EXTRA_NAVIGATION_BAR:
+            case ITYPE_LOCAL_NAVIGATION_BAR_1:
+            case ITYPE_LOCAL_NAVIGATION_BAR_2:
                 return Type.NAVIGATION_BARS;
             case ITYPE_CAPTION_BAR:
                 return Type.CAPTION_BAR;
@@ -833,6 +843,10 @@
                 return "ITYPE_CLIMATE_BAR";
             case ITYPE_EXTRA_NAVIGATION_BAR:
                 return "ITYPE_EXTRA_NAVIGATION_BAR";
+            case ITYPE_LOCAL_NAVIGATION_BAR_1:
+                return "ITYPE_LOCAL_NAVIGATION_BAR_1";
+            case ITYPE_LOCAL_NAVIGATION_BAR_2:
+                return "ITYPE_LOCAL_NAVIGATION_BAR_2";
             default:
                 return "ITYPE_UNKNOWN_" + type;
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 68c8143..3b2a248 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1624,7 +1624,7 @@
                 android:permissionGroup="android.permission-group.UNDEFINED"
                 android:label="@string/permlab_postNotification"
                 android:description="@string/permdesc_postNotification"
-                android:protectionLevel="dangerous" />
+                android:protectionLevel="dangerous|instant" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
 
     <!-- ====================================================================== -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 21b2cb0..2d2c03b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -544,6 +544,8 @@
         <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
         <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
         <permission name="android.permission.SEND_LOST_MODE_LOCATION_UPDATES" />
+        <!-- Permission required for CTS test - CtsTelephonyTestCases -->
+        <permission name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index e50ad38..9b41468 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -52,9 +52,6 @@
  */
 public class BackAnimationController implements RemoteCallable<BackAnimationController> {
 
-    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-    public static final boolean IS_ENABLED = SystemProperties
-            .getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
     private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
             "persist.debug.back_predictability_progress_threshold";
     private static final int PROGRESS_THRESHOLD = SystemProperties
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 79a24b7..ee4d5ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -283,9 +283,8 @@
         if (context == null) {
             return;
         }
-        LetterboxEduWindowManager newLayout = new LetterboxEduWindowManager(context, taskInfo,
-                mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
-                this::onLetterboxEduDismissed);
+        LetterboxEduWindowManager newLayout = createLetterboxEduWindowManager(context, taskInfo,
+                taskListener);
         if (newLayout.createLayout(showOnDisplay(taskInfo.displayId))) {
             // The new layout is eligible to be shown, make it the active layout.
             if (mActiveLetterboxEduLayout != null) {
@@ -298,6 +297,14 @@
         }
     }
 
+    @VisibleForTesting
+    LetterboxEduWindowManager createLetterboxEduWindowManager(Context context, TaskInfo taskInfo,
+            ShellTaskOrganizer.TaskListener taskListener) {
+        return new LetterboxEduWindowManager(context, taskInfo,
+                mSyncQueue, taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
+                this::onLetterboxEduDismissed);
+    }
+
     private void onLetterboxEduDismissed() {
         mActiveLetterboxEduLayout = null;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
index 5679bc4..face243 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -22,6 +22,10 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
+
 import android.annotation.Nullable;
 import android.app.TaskInfo;
 import android.content.Context;
@@ -110,7 +114,8 @@
      * @param canShow whether the layout is allowed to be shown by the parent controller.
      * @return whether the layout is eligible to be shown.
      */
-    protected boolean createLayout(boolean canShow) {
+    @VisibleForTesting(visibility = PROTECTED)
+    public boolean createLayout(boolean canShow) {
         if (!eligibleToShowLayout()) {
             return false;
         }
@@ -184,7 +189,8 @@
      * @param canShow whether the layout is allowed to be shown by the parent controller.
      * @return whether the layout is eligible to be shown.
      */
-    protected boolean updateCompatInfo(TaskInfo taskInfo,
+    @VisibleForTesting(visibility = PROTECTED)
+    public boolean updateCompatInfo(TaskInfo taskInfo,
             ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
         final Configuration prevTaskConfig = mTaskConfig;
         final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
@@ -201,7 +207,8 @@
 
         View layout = getLayout();
         if (layout == null || prevTaskListener != taskListener) {
-            // TaskListener changed, recreate the layout for new surface parent.
+            // Layout wasn't created yet or TaskListener changed, recreate the layout for new
+            // surface parent.
             release();
             return createLayout(canShow);
         }
@@ -227,7 +234,8 @@
      *
      * @param canShow whether the layout is allowed to be shown by the parent controller.
      */
-    void updateVisibility(boolean canShow) {
+    @VisibleForTesting(visibility = PACKAGE)
+    public void updateVisibility(boolean canShow) {
         View layout = getLayout();
         if (layout == null) {
             // Layout may not have been created because it was hidden previously.
@@ -242,7 +250,8 @@
     }
 
     /** Called when display layout changed. */
-    void updateDisplayLayout(DisplayLayout displayLayout) {
+    @VisibleForTesting(visibility = PACKAGE)
+    public void updateDisplayLayout(DisplayLayout displayLayout) {
         final Rect prevStableBounds = mStableBounds;
         final Rect curStableBounds = new Rect();
         displayLayout.getStableBounds(curStableBounds);
@@ -255,7 +264,7 @@
     }
 
     /** Called when the surface is ready to be placed under the task surface. */
-    @VisibleForTesting
+    @VisibleForTesting(visibility = PRIVATE)
     void attachToParentSurface(SurfaceControl.Builder b) {
         mTaskListener.attachChildSurfaceToTask(mTaskId, b);
     }
@@ -347,8 +356,9 @@
         return result;
     }
 
-    @VisibleForTesting
-    SurfaceControlViewHost createSurfaceViewHost() {
+    /** Creates a {@link SurfaceControlViewHost} for this window manager. */
+    @VisibleForTesting(visibility = PRIVATE)
+    public SurfaceControlViewHost createSurfaceViewHost() {
         return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
index 3810eca..03986ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java
@@ -39,7 +39,6 @@
 /**
  * Controls the enter/exit animations of the letterbox education.
  */
-// TODO(b/215316431): Add tests
 class LetterboxEduAnimationController {
     private static final String TAG = "LetterboxEduAnimation";
 
@@ -99,15 +98,10 @@
     /**
      * Starts both the background dim fade-out animation and the dialog exit animation.
      */
-    void startExitAnimation(@Nullable LetterboxEduDialogLayout layout, Runnable endCallback) {
+    void startExitAnimation(@NonNull LetterboxEduDialogLayout layout, Runnable endCallback) {
         // Cancel any previous animation if it's still running.
         cancelAnimation();
 
-        if (layout == null) {
-            endCallback.run();
-            return;
-        }
-
         final View dialogContainer = layout.getDialogContainer();
         mDialogAnimation = loadAnimation(WindowAnimation_windowExitAnimation);
         if (mDialogAnimation == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
index fc6fd3f..02197f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogActionLayout.java
@@ -29,18 +29,28 @@
 /**
  * Custom layout for Letterbox Education dialog action.
  */
-// TODO(b/215316431): Add tests
 class LetterboxEduDialogActionLayout extends FrameLayout {
 
-    LetterboxEduDialogActionLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
+    public LetterboxEduDialogActionLayout(Context context) {
+        this(context, null);
+    }
+
+    public LetterboxEduDialogActionLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public LetterboxEduDialogActionLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public LetterboxEduDialogActionLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
 
         TypedArray styledAttributes =
                 context.getTheme().obtainStyledAttributes(
-                        attrs,
-                        R.styleable.LetterboxEduDialogActionLayout,
-                        /* defStyleAttr= */ 0,
-                        /* defStyleRes= */ 0);
+                        attrs, R.styleable.LetterboxEduDialogActionLayout, defStyleAttr,
+                        defStyleRes);
         int iconId = styledAttributes.getResourceId(
                 R.styleable.LetterboxEduDialogActionLayout_icon, 0);
         String text = styledAttributes.getString(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
index bb6fe98..2da6a6b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.compatui.letterboxedu;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
@@ -31,7 +32,6 @@
  * <p>This layout should fill the entire task and the background around the dialog acts as the
  * background dim which dismisses the dialog when clicked.
  */
-// TODO(b/215316431): Add tests
 class LetterboxEduDialogLayout extends ConstraintLayout {
 
     // The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque).
@@ -68,16 +68,16 @@
     /**
      * Register a callback for the dismiss button and background dim.
      *
-     * @param callback The callback to register
+     * @param callback The callback to register or null if all on click listeners should be removed.
      */
-    void setDismissOnClickListener(Runnable callback) {
-        findViewById(R.id.letterbox_education_dialog_dismiss_button).setOnClickListener(
-                view -> callback.run());
+    void setDismissOnClickListener(@Nullable Runnable callback) {
+        final OnClickListener listener = callback == null ? null : view -> callback.run();
+        findViewById(R.id.letterbox_education_dialog_dismiss_button).setOnClickListener(listener);
         // Clicks on the background dim should also dismiss the dialog.
-        setOnClickListener(view -> callback.run());
+        setOnClickListener(listener);
         // We add a no-op on-click listener to the dialog container so that clicks on it won't
         // propagate to the listener of the layout (which represents the background dim).
-        mDialogContainer.setOnClickListener(view -> {});
+        mDialogContainer.setOnClickListener(callback == null ? null : view -> {});
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
index bb4d427..30b9f08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java
@@ -29,6 +29,7 @@
 import android.view.ViewGroup.MarginLayoutParams;
 import android.view.WindowManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayLayout;
@@ -38,7 +39,6 @@
 /**
  * Window manager for the Letterbox Education.
  */
-// TODO(b/215316431): Add tests
 public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
 
     /**
@@ -51,7 +51,8 @@
      * The name of the {@link SharedPreferences} that holds which user has seen the Letterbox
      * Education for specific packages and which user has seen the full dialog for any package.
      */
-    private static final String HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME =
+    @VisibleForTesting
+    static final String HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME =
             "has_seen_letterbox_education";
 
     /**
@@ -65,19 +66,37 @@
     private boolean mEligibleForLetterboxEducation;
 
     @Nullable
-    private LetterboxEduDialogLayout mLayout;
+    @VisibleForTesting
+    LetterboxEduDialogLayout mLayout;
 
     private final Runnable mOnDismissCallback;
 
+    /**
+     * The vertical margin between the dialog container and the task stable bounds (excluding
+     * insets).
+     */
+    private final int mDialogVerticalMargin;
+
     public LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
             SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
             DisplayLayout displayLayout, Runnable onDismissCallback) {
+        this(context, taskInfo, syncQueue, taskListener, displayLayout, onDismissCallback,
+                new LetterboxEduAnimationController(context));
+    }
+
+    @VisibleForTesting
+    LetterboxEduWindowManager(Context context, TaskInfo taskInfo,
+            SyncTransactionQueue syncQueue, ShellTaskOrganizer.TaskListener taskListener,
+            DisplayLayout displayLayout, Runnable onDismissCallback,
+            LetterboxEduAnimationController animationController) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mOnDismissCallback = onDismissCallback;
+        mAnimationController = animationController;
         mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation;
-        mAnimationController = new LetterboxEduAnimationController(context);
         mSharedPreferences = mContext.getSharedPreferences(HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
                 Context.MODE_PRIVATE);
+        mDialogVerticalMargin = (int) mContext.getResources().getDimension(
+                R.dimen.letterbox_education_dialog_margin);
     }
 
     @Override
@@ -124,13 +143,12 @@
         }
         final View dialogContainer = mLayout.getDialogContainer();
         MarginLayoutParams marginParams = (MarginLayoutParams) dialogContainer.getLayoutParams();
-        int verticalMargin = (int) mContext.getResources().getDimension(
-                R.dimen.letterbox_education_dialog_margin);
 
         final Rect taskBounds = getTaskBounds();
         final Rect taskStableBounds = getTaskStableBounds();
-        marginParams.topMargin = taskStableBounds.top - taskBounds.top + verticalMargin;
-        marginParams.bottomMargin = taskBounds.bottom - taskStableBounds.bottom + verticalMargin;
+        marginParams.topMargin = taskStableBounds.top - taskBounds.top + mDialogVerticalMargin;
+        marginParams.bottomMargin =
+                taskBounds.bottom - taskStableBounds.bottom + mDialogVerticalMargin;
         dialogContainer.setLayoutParams(marginParams);
     }
 
@@ -147,6 +165,10 @@
     }
 
     private void onDismiss() {
+        if (mLayout == null) {
+            return;
+        }
+        mLayout.setDismissOnClickListener(null);
         mAnimationController.startExitAnimation(mLayout, () -> {
             release();
             mOnDismissCallback.run();
@@ -204,7 +226,8 @@
         return String.valueOf(mContext.getUserId());
     }
 
-    private boolean isTaskbarEduShowing() {
+    @VisibleForTesting
+    boolean isTaskbarEduShowing() {
         return Settings.Secure.getInt(mContext.getContentResolver(),
                 LAUNCHER_TASKBAR_EDUCATION_SHOWING, /* def= */ 0) == 1;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 0362b3f..c94f3d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -699,10 +699,7 @@
             Context context,
             @ShellMainThread ShellExecutor shellExecutor
     ) {
-        if (BackAnimationController.IS_ENABLED) {
-            return Optional.of(
-                    new BackAnimationController(shellExecutor, context));
-        }
-        return Optional.empty();
+        return Optional.of(
+                new BackAnimationController(shellExecutor, context));
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index c5be485..29e40be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -52,6 +52,7 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -81,7 +82,8 @@
     private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
     private @Mock SyncTransactionQueue mMockSyncQueue;
     private @Mock ShellExecutor mMockExecutor;
-    private @Mock CompatUIWindowManager mMockLayout;
+    private @Mock CompatUIWindowManager mMockCompatLayout;
+    private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
 
     @Captor
     ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -91,16 +93,26 @@
         MockitoAnnotations.initMocks(this);
 
         doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
-        doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
-        doReturn(TASK_ID).when(mMockLayout).getTaskId();
-        doReturn(true).when(mMockLayout).createLayout(anyBoolean());
-        doReturn(true).when(mMockLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(DISPLAY_ID).when(mMockCompatLayout).getDisplayId();
+        doReturn(TASK_ID).when(mMockCompatLayout).getTaskId();
+        doReturn(true).when(mMockCompatLayout).createLayout(anyBoolean());
+        doReturn(true).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(DISPLAY_ID).when(mMockLetterboxEduLayout).getDisplayId();
+        doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
+        doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
+        doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
         mController = new CompatUIController(mContext, mMockDisplayController,
                 mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
             @Override
             CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
                     ShellTaskOrganizer.TaskListener taskListener) {
-                return mMockLayout;
+                return mMockCompatLayout;
+            }
+
+            @Override
+            LetterboxEduWindowManager createLetterboxEduWindowManager(Context context,
+                    TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) {
+                return mMockLetterboxEduLayout;
             }
         };
         spyOn(mController);
@@ -121,68 +133,95 @@
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
-        // Verify that the compat controls are updated with new size compat info.
-        clearInvocations(mMockLayout);
+        // Verify that the compat controls and letterbox education are updated with new size compat
+        // info.
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ true);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                true);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                true);
 
-        // Verify that compat controls are removed with null task listener.
-        clearInvocations(mMockLayout);
+        // Verify that compat controls and letterbox education are removed with null task listener.
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN),
                 /* taskListener= */ null);
 
-        verify(mMockLayout).release();
+        verify(mMockCompatLayout).release();
+        verify(mMockLetterboxEduLayout).release();
     }
 
     @Test
     public void testOnCompatInfoChanged_createLayoutReturnsFalse() {
-        doReturn(false).when(mMockLayout).createLayout(anyBoolean());
+        doReturn(false).when(mMockCompatLayout).createLayout(anyBoolean());
+        doReturn(false).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
 
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
         // Verify that the layout is created again.
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
     }
 
     @Test
     public void testOnCompatInfoChanged_updateCompatInfoReturnsFalse() {
-        doReturn(false).when(mMockLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(false).when(mMockCompatLayout).updateCompatInfo(any(), any(), anyBoolean());
+        doReturn(false).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
 
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
 
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ true);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                true);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                true);
 
         // Verify that the layout is created again.
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         clearInvocations(mController);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockCompatLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
+        verify(mMockLetterboxEduLayout, never()).updateCompatInfo(any(), any(), anyBoolean());
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
+        verify(mController).createLetterboxEduWindowManager(any(), eq(taskInfo),
+                eq(mMockTaskListener));
     }
 
 
@@ -204,14 +243,16 @@
 
         mController.onDisplayRemoved(DISPLAY_ID + 1);
 
-        verify(mMockLayout, never()).release();
+        verify(mMockCompatLayout, never()).release();
+        verify(mMockLetterboxEduLayout, never()).release();
         verify(mMockDisplayInsetsController, never()).removeInsetsChangedListener(eq(DISPLAY_ID),
                 any());
 
         mController.onDisplayRemoved(DISPLAY_ID);
 
         verify(mMockDisplayInsetsController).removeInsetsChangedListener(eq(DISPLAY_ID), any());
-        verify(mMockLayout).release();
+        verify(mMockCompatLayout).release();
+        verify(mMockLetterboxEduLayout).release();
     }
 
     @Test
@@ -221,11 +262,13 @@
 
         mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, new Configuration());
 
-        verify(mMockLayout, never()).updateDisplayLayout(any());
+        verify(mMockCompatLayout, never()).updateDisplayLayout(any());
+        verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(any());
 
         mController.onDisplayConfigurationChanged(DISPLAY_ID, new Configuration());
 
-        verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
     }
 
     @Test
@@ -242,104 +285,125 @@
                 mOnInsetsChangedListenerCaptor.capture());
         mOnInsetsChangedListenerCaptor.getValue().insetsChanged(insetsState);
 
-        verify(mMockLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockCompatLayout).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockLetterboxEduLayout).updateDisplayLayout(mMockDisplayLayout);
 
         // No update if the insets state is the same.
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
         mOnInsetsChangedListenerCaptor.getValue().insetsChanged(new InsetsState(insetsState));
-        verify(mMockLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockCompatLayout, never()).updateDisplayLayout(mMockDisplayLayout);
+        verify(mMockLetterboxEduLayout, never()).updateDisplayLayout(mMockDisplayLayout);
     }
 
     @Test
-    public void testChangeButtonVisibilityOnImeShowHide() {
+    public void testChangeLayoutsVisibilityOnImeShowHide() {
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
 
         // Verify that the restart button is hidden after IME is showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
 
-        verify(mMockLayout).updateVisibility(false);
+        verify(mMockCompatLayout).updateVisibility(false);
+        verify(mMockLetterboxEduLayout).updateVisibility(false);
 
         // Verify button remains hidden while IME is showing.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ false);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                false);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                false);
 
         // Verify button is shown after IME is hidden.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
-        verify(mMockLayout).updateVisibility(true);
+        verify(mMockCompatLayout).updateVisibility(true);
+        verify(mMockLetterboxEduLayout).updateVisibility(true);
     }
 
     @Test
-    public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
+    public void testChangeLayoutsVisibilityOnKeyguardOccludedChanged() {
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
 
         // Verify that the restart button is hidden after keyguard becomes occluded.
         mController.onKeyguardOccludedChanged(true);
 
-        verify(mMockLayout).updateVisibility(false);
+        verify(mMockCompatLayout).updateVisibility(false);
+        verify(mMockLetterboxEduLayout).updateVisibility(false);
 
         // Verify button remains hidden while keyguard is occluded.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
                 CAMERA_COMPAT_CONTROL_HIDDEN);
         mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
 
-        verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */ false);
+        verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                false);
+        verify(mMockLetterboxEduLayout).updateCompatInfo(taskInfo, mMockTaskListener, /* canShow= */
+                false);
 
         // Verify button is shown after keyguard becomes not occluded.
         mController.onKeyguardOccludedChanged(false);
 
-        verify(mMockLayout).updateVisibility(true);
+        verify(mMockCompatLayout).updateVisibility(true);
+        verify(mMockLetterboxEduLayout).updateVisibility(true);
     }
 
     @Test
-    public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
+    public void testLayoutsRemainHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
 
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
         mController.onKeyguardOccludedChanged(true);
 
-        verify(mMockLayout, times(2)).updateVisibility(false);
+        verify(mMockCompatLayout, times(2)).updateVisibility(false);
+        verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
 
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
 
         // Verify button remains hidden after keyguard becomes not occluded since IME is showing.
         mController.onKeyguardOccludedChanged(false);
 
-        verify(mMockLayout).updateVisibility(false);
+        verify(mMockCompatLayout).updateVisibility(false);
+        verify(mMockLetterboxEduLayout).updateVisibility(false);
 
         // Verify button is shown after IME is not showing.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
-        verify(mMockLayout).updateVisibility(true);
+        verify(mMockCompatLayout).updateVisibility(true);
+        verify(mMockLetterboxEduLayout).updateVisibility(true);
     }
 
     @Test
-    public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
+    public void testLayoutsRemainHiddenOnImeHideWhenKeyguardIsOccluded() {
         mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
                 /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
 
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true);
         mController.onKeyguardOccludedChanged(true);
 
-        verify(mMockLayout, times(2)).updateVisibility(false);
+        verify(mMockCompatLayout, times(2)).updateVisibility(false);
+        verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false);
 
-        clearInvocations(mMockLayout);
+        clearInvocations(mMockCompatLayout);
+        clearInvocations(mMockLetterboxEduLayout);
 
         // Verify button remains hidden after IME is hidden since keyguard is occluded.
         mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ false);
 
-        verify(mMockLayout).updateVisibility(false);
+        verify(mMockCompatLayout).updateVisibility(false);
+        verify(mMockLetterboxEduLayout).updateVisibility(false);
 
         // Verify button is shown after keyguard becomes not occluded.
         mController.onKeyguardOccludedChanged(false);
 
-        verify(mMockLayout).updateVisibility(true);
+        verify(mMockCompatLayout).updateVisibility(true);
+        verify(mMockLetterboxEduLayout).updateVisibility(true);
     }
 
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
new file mode 100644
index 0000000..00e4938
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.compatui.letterboxedu;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link LetterboxEduDialogLayout}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:LetterboxEduDialogLayoutTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class LetterboxEduDialogLayoutTest extends ShellTestCase {
+
+    @Mock
+    private Runnable mDismissCallback;
+
+    private LetterboxEduDialogLayout mLayout;
+    private View mDismissButton;
+    private View mDialogContainer;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mLayout = (LetterboxEduDialogLayout)
+                LayoutInflater.from(mContext).inflate(R.layout.letterbox_education_dialog_layout,
+                        null);
+        mDismissButton = mLayout.findViewById(R.id.letterbox_education_dialog_dismiss_button);
+        mDialogContainer = mLayout.findViewById(R.id.letterbox_education_dialog_container);
+        mLayout.setDismissOnClickListener(mDismissCallback);
+    }
+
+    @Test
+    public void testOnFinishInflate() {
+        assertEquals(mLayout.getDialogContainer(),
+                mLayout.findViewById(R.id.letterbox_education_dialog_container));
+        assertEquals(mLayout.getBackgroundDim(), mLayout.getBackground());
+        assertEquals(mLayout.getBackground().getAlpha(), 0);
+    }
+
+    @Test
+    public void testOnDismissButtonClicked() {
+        assertTrue(mDismissButton.performClick());
+
+        verify(mDismissCallback).run();
+    }
+
+    @Test
+    public void testOnBackgroundClicked() {
+        assertTrue(mLayout.performClick());
+
+        verify(mDismissCallback).run();
+    }
+
+    @Test
+    public void testOnDialogContainerClicked() {
+        assertTrue(mDialogContainer.performClick());
+
+        verify(mDismissCallback, never()).run();
+    }
+
+    @Test
+    public void testSetDismissOnClickListenerNull() {
+        mLayout.setDismissOnClickListener(null);
+
+        assertFalse(mDismissButton.performClick());
+        assertFalse(mLayout.performClick());
+        assertFalse(mDialogContainer.performClick());
+
+        verify(mDismissCallback, never()).run();
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
new file mode 100644
index 0000000..0509dd3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.compatui.letterboxedu;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.SurfaceControlViewHost;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link LetterboxEduWindowManager}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:LetterboxEduWindowManagerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class LetterboxEduWindowManagerTest extends ShellTestCase {
+
+    private static final int TASK_ID = 1;
+
+    private static final int TASK_WIDTH = 200;
+    private static final int TASK_HEIGHT = 100;
+    private static final int DISPLAY_CUTOUT_TOP = 5;
+    private static final int DISPLAY_CUTOUT_BOTTOM = 10;
+    private static final int DISPLAY_CUTOUT_HORIZONTAL = 20;
+
+    @Captor
+    private ArgumentCaptor<WindowManager.LayoutParams> mWindowAttrsCaptor;
+    @Captor
+    private ArgumentCaptor<Runnable> mEndCallbackCaptor;
+
+    @Mock private LetterboxEduAnimationController mAnimationController;
+    @Mock private SyncTransactionQueue mSyncTransactionQueue;
+    @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+    @Mock private SurfaceControlViewHost mViewHost;
+    @Mock private Runnable mOnDismissCallback;
+
+    private SharedPreferences mSharedPreferences;
+    private String mPrefKey;
+    @Nullable
+    private Boolean mInitialPrefValue = null;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mSharedPreferences = mContext.getSharedPreferences(
+                LetterboxEduWindowManager.HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
+                Context.MODE_PRIVATE);
+        mPrefKey = String.valueOf(mContext.getUserId());
+        if (mSharedPreferences.contains(mPrefKey)) {
+            mInitialPrefValue = mSharedPreferences.getBoolean(mPrefKey, /* default= */ false);
+            mSharedPreferences.edit().remove(mPrefKey).apply();
+        }
+    }
+
+    @After
+    public void tearDown() {
+        SharedPreferences.Editor editor = mSharedPreferences.edit();
+        if (mInitialPrefValue == null) {
+            editor.remove(mPrefKey);
+        } else {
+            editor.putBoolean(mPrefKey, mInitialPrefValue);
+        }
+        editor.apply();
+    }
+
+    @Test
+    public void testCreateLayout_notEligible_doesNotCreateLayout() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ false);
+
+        assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testCreateLayout_alreadyShownToUser_doesNotCreateLayout() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+        mSharedPreferences.edit().putBoolean(mPrefKey, true).apply();
+
+        assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testCreateLayout_taskBarEducationIsShowing_doesNotCreateLayout() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */
+                true, /* isTaskbarEduShowing= */ true);
+
+        assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testCreateLayout_canShowFalse_returnsTrueButDoesNotCreateLayout() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ false));
+
+        assertFalse(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testCreateLayout_canShowTrue_createsLayoutCorrectly() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ true));
+
+        assertTrue(mSharedPreferences.getBoolean(mPrefKey, /* default= */ false));
+        LetterboxEduDialogLayout layout = windowManager.mLayout;
+        assertNotNull(layout);
+        verify(mViewHost).setView(eq(layout), mWindowAttrsCaptor.capture());
+        verifyLayout(layout, mWindowAttrsCaptor.getValue(), /* expectedWidth= */ TASK_WIDTH,
+                /* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */ DISPLAY_CUTOUT_TOP,
+                /* expectedExtraBottomMargin= */ DISPLAY_CUTOUT_BOTTOM);
+
+        // Clicking the layout does nothing until enter animation is done.
+        layout.performClick();
+        verify(mAnimationController, never()).startExitAnimation(any(), any());
+
+        verifyAndFinishEnterAnimation(layout);
+
+        // Exit animation should start following a click on the layout.
+        layout.performClick();
+
+        // Window manager isn't released until exit animation is done.
+        verify(windowManager, never()).release();
+
+        // Verify multiple clicks are ignored.
+        layout.performClick();
+
+        verifyAndFinishExitAnimation(layout);
+
+        verify(windowManager).release();
+        verify(mOnDismissCallback).run();
+    }
+
+    @Test
+    public void testUpdateCompatInfo_updatesLayoutCorrectly() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ true));
+        LetterboxEduDialogLayout layout = windowManager.mLayout;
+        assertNotNull(layout);
+
+        assertTrue(windowManager.updateCompatInfo(
+                createTaskInfo(/* eligible= */ true, new Rect(50, 25, 150, 75)),
+                mTaskListener, /* canShow= */ true));
+
+        verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ 100,
+                /* expectedHeight= */ 50, /* expectedExtraTopMargin= */ 0,
+                /* expectedExtraBottomMargin= */ 0);
+        verify(mViewHost).relayout(mWindowAttrsCaptor.capture());
+        assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+
+        // Window manager should be released (without animation) when eligible becomes false.
+        assertFalse(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ false),
+                mTaskListener, /* canShow= */ true));
+
+        verify(windowManager).release();
+        verify(mOnDismissCallback, never()).run();
+        verify(mAnimationController, never()).startExitAnimation(any(), any());
+        assertNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testUpdateCompatInfo_notEligibleUntilUpdate_createsLayoutAfterUpdate() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ false);
+
+        assertFalse(windowManager.createLayout(/* canShow= */ true));
+        assertNull(windowManager.mLayout);
+
+        assertTrue(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ true),
+                mTaskListener, /* canShow= */ true));
+
+        assertNotNull(windowManager.mLayout);
+    }
+
+    @Test
+    public void testUpdateCompatInfo_canShowFalse_doesNothing() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ false));
+        assertNull(windowManager.mLayout);
+
+        assertTrue(windowManager.updateCompatInfo(createTaskInfo(/* eligible= */ true),
+                mTaskListener, /* canShow= */ false));
+
+        assertNull(windowManager.mLayout);
+        verify(mViewHost, never()).relayout(any());
+    }
+
+    @Test
+    public void testUpdateDisplayLayout_updatesLayoutCorrectly() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ true));
+        LetterboxEduDialogLayout layout = windowManager.mLayout;
+        assertNotNull(layout);
+
+        int newDisplayCutoutTop = DISPLAY_CUTOUT_TOP + 7;
+        int newDisplayCutoutBottom = DISPLAY_CUTOUT_BOTTOM + 9;
+        windowManager.updateDisplayLayout(createDisplayLayout(
+                Insets.of(DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutTop,
+                        DISPLAY_CUTOUT_HORIZONTAL, newDisplayCutoutBottom)));
+
+        verifyLayout(layout, layout.getLayoutParams(), /* expectedWidth= */ TASK_WIDTH,
+                /* expectedHeight= */ TASK_HEIGHT, /* expectedExtraTopMargin= */
+                newDisplayCutoutTop, /* expectedExtraBottomMargin= */ newDisplayCutoutBottom);
+        verify(mViewHost).relayout(mWindowAttrsCaptor.capture());
+        assertThat(mWindowAttrsCaptor.getValue()).isEqualTo(layout.getLayoutParams());
+    }
+
+    @Test
+    public void testRelease_animationIsCancelled() {
+        LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */ true);
+
+        assertTrue(windowManager.createLayout(/* canShow= */ true));
+        windowManager.release();
+
+        verify(mAnimationController).cancelAnimation();
+    }
+
+    private void verifyLayout(LetterboxEduDialogLayout layout, ViewGroup.LayoutParams params,
+            int expectedWidth, int expectedHeight, int expectedExtraTopMargin,
+            int expectedExtraBottomMargin) {
+        assertThat(params.width).isEqualTo(expectedWidth);
+        assertThat(params.height).isEqualTo(expectedHeight);
+        MarginLayoutParams dialogParams =
+                (MarginLayoutParams) layout.getDialogContainer().getLayoutParams();
+        int verticalMargin = (int) mContext.getResources().getDimension(
+                R.dimen.letterbox_education_dialog_margin);
+        assertThat(dialogParams.topMargin).isEqualTo(verticalMargin + expectedExtraTopMargin);
+        assertThat(dialogParams.bottomMargin).isEqualTo(verticalMargin + expectedExtraBottomMargin);
+    }
+
+    private void verifyAndFinishEnterAnimation(LetterboxEduDialogLayout layout) {
+        verify(mAnimationController).startEnterAnimation(eq(layout), mEndCallbackCaptor.capture());
+        mEndCallbackCaptor.getValue().run();
+    }
+
+    private void verifyAndFinishExitAnimation(LetterboxEduDialogLayout layout) {
+        verify(mAnimationController).startExitAnimation(eq(layout), mEndCallbackCaptor.capture());
+        mEndCallbackCaptor.getValue().run();
+    }
+
+    private LetterboxEduWindowManager createWindowManager(boolean eligible) {
+        return createWindowManager(eligible, /* isTaskbarEduShowing= */ false);
+    }
+
+    private LetterboxEduWindowManager createWindowManager(boolean eligible,
+            boolean isTaskbarEduShowing) {
+        LetterboxEduWindowManager windowManager = new LetterboxEduWindowManager(mContext,
+                createTaskInfo(eligible), mSyncTransactionQueue, mTaskListener,
+                createDisplayLayout(), mOnDismissCallback, mAnimationController);
+
+        spyOn(windowManager);
+        doReturn(mViewHost).when(windowManager).createSurfaceViewHost();
+        doReturn(isTaskbarEduShowing).when(windowManager).isTaskbarEduShowing();
+
+        return windowManager;
+    }
+
+    private DisplayLayout createDisplayLayout() {
+        return createDisplayLayout(
+                Insets.of(DISPLAY_CUTOUT_HORIZONTAL, DISPLAY_CUTOUT_TOP, DISPLAY_CUTOUT_HORIZONTAL,
+                        DISPLAY_CUTOUT_BOTTOM));
+    }
+
+    private DisplayLayout createDisplayLayout(Insets insets) {
+        DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = TASK_WIDTH;
+        displayInfo.logicalHeight = TASK_HEIGHT;
+        displayInfo.displayCutout = new DisplayCutout(
+                insets, null, null, null, null);
+        return new DisplayLayout(displayInfo,
+                mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
+    }
+
+    private static TaskInfo createTaskInfo(boolean eligible) {
+        return createTaskInfo(eligible, new Rect(0, 0, TASK_WIDTH, TASK_HEIGHT));
+    }
+
+    private static TaskInfo createTaskInfo(boolean eligible, Rect bounds) {
+        ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+        taskInfo.taskId = TASK_ID;
+        taskInfo.topActivityEligibleForLetterboxEducation = eligible;
+        taskInfo.configuration.windowConfiguration.setBounds(bounds);
+        return taskInfo;
+    }
+}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 63b831d..c8cc3526 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -33,6 +33,7 @@
 
 cc_defaults {
     name: "libandroidfw_defaults",
+    cpp_std: "gnu++2b",
     cflags: [
         "-Werror",
         "-Wunreachable-code",
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index 921877dc..fac2fa4 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -288,12 +288,12 @@
 
 template <typename TChar>
 inline bool operator==(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
-  return rhs == lhs;
+  return BasicStringPiece<TChar>(lhs) == rhs;
 }
 
 template <typename TChar>
 inline bool operator!=(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
-  return rhs != lhs;
+  return BasicStringPiece<TChar>(lhs) != rhs;
 }
 
 }  // namespace android
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index f627a3c..01b956c 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -152,8 +152,8 @@
     ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
 
     virtual void requestNextVsync() override {
-        AChoreographer_postExtendedFrameCallback(
-                mRenderThread->mChoreographer, RenderThread::extendedFrameCallback, mRenderThread);
+        AChoreographer_postVsyncCallback(mRenderThread->mChoreographer,
+                                         RenderThread::extendedFrameCallback, mRenderThread);
     }
 
     virtual void drainPendingEvents() override {
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index 9039011..858f85f 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -20,7 +20,7 @@
         xmlns:tools="http://schemas.android.com/tools"
         package="com.android.bluetoothmidiservice"
         >
-    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+    <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
 
     <uses-feature android:name="android.hardware.bluetooth_le"
          android:required="true"/>
diff --git a/media/packages/BluetoothMidiService/AndroidManifestBase.xml b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
index 5a900c7..99dcaaa 100644
--- a/media/packages/BluetoothMidiService/AndroidManifestBase.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
@@ -19,7 +19,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.bluetoothmidiservice"
           >
-    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+    <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
     <application
         android:label="BluetoothMidi"
         android:defaultToDeviceProtectedStorage="true"
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index e22580d..21c5029 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -40,10 +40,9 @@
     return AChoreographer_routePostFrameCallbackDelayed64(choreographer, callback, data,
                                                           delayMillis);
 }
-void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
-                                              AChoreographer_extendedFrameCallback callback,
-                                              void* data) {
-    return AChoreographer_routePostExtendedFrameCallback(choreographer, callback, data);
+void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
+                                      AChoreographer_vsyncCallback callback, void* data) {
+    return AChoreographer_routePostVsyncCallback(choreographer, callback, data);
 }
 void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
                                                 AChoreographer_refreshRateCallback callback,
@@ -71,10 +70,10 @@
         const AChoreographerFrameCallbackData* data, size_t index) {
     return AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(data, index);
 }
-int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos(
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
-    return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTimeNanos(data,
-                                                                                         index);
+    return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentationTimeNanos(
+            data, index);
 }
 int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
         const AChoreographerFrameCallbackData* data, size_t index) {
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 67a98a9..4aa5bbf 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -29,12 +29,12 @@
     AChoreographer_postFrameCallbackDelayed64; # introduced=29
     AChoreographer_registerRefreshRateCallback; # introduced=30
     AChoreographer_unregisterRefreshRateCallback; # introduced=30
-    AChoreographer_postExtendedFrameCallback;  # introduced=33
+    AChoreographer_postVsyncCallback;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimeNanos;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelinesLength;  # introduced=33
     AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelineVsyncId;  # introduced=33
-    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTimeNanos;  # introduced=33
+    AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos;  # introduced=33
     AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos;  # introduced=33
     AConfiguration_copy;
     AConfiguration_delete;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1df326a..f0b180e 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -658,6 +658,9 @@
     <!-- Permission required for CTS test - CaptioningManagerTest -->
     <uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" />
 
+    <!-- Permission required for CTS test - CtsTelephonyTestCases -->
+    <uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b1cfb11..8cc9e00 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -112,6 +112,7 @@
         "androidx.dynamicanimation_dynamicanimation",
         "androidx-constraintlayout_constraintlayout",
         "androidx.exifinterface_exifinterface",
+        "com.google.android.material_material",
         "kotlinx_coroutines_android",
         "kotlinx_coroutines",
         "iconloader_base",
diff --git a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
index 9891156..a751f58 100644
--- a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
+++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
@@ -15,6 +15,6 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="@android:color/system_neutral1_800" />
+    <solid android:color="@color/material_dynamic_neutral20" />
     <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
 </shape>
diff --git a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
index ff7cbae..f7b0982 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
@@ -14,5 +14,5 @@
     limitations under the License.
 -->
 <color xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@android:color/system_accent1_500" />
+    android:color="@color/material_dynamic_primary50" />
 
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
index 3a228d5..e1b99ce 100644
--- a/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
+++ b/packages/SystemUI/res/drawable/media_output_dialog_seekbar_background.xml
@@ -30,7 +30,7 @@
                     android:radius="28dp"/>
                 <size
                     android:height="64dp"/>
-                <solid android:color="@*android:color/system_accent1_200" />
+                <solid android:color="@color/material_dynamic_primary80" />
             </shape>
         </clip>
     </item>
diff --git a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
index ab600b3..2567176 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog_audio_source.xml
@@ -27,11 +27,11 @@
         android:layout_gravity="center_vertical"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textSize="14sp"
-        android:textColor="@android:color/system_neutral1_900"/>
+        android:textColor="@color/material_dynamic_neutral10"/>
     <TextView
         android:id="@+id/screen_recording_dialog_source_description"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="@android:color/system_neutral2_700"/>
+        android:textColor="@color/material_dynamic_neutral_variant30"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index f1e93c2..4b96d5d 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -33,7 +33,7 @@
     <!-- The color of the text inside a notification -->
     <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
 
-    <color name="notif_pill_text">@android:color/system_neutral1_50</color>
+    <color name="notif_pill_text">@color/material_dynamic_neutral95</color>
 
     <color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
     <color name="notification_guts_sub_text_color">@color/GM2_grey_300</color>
@@ -66,15 +66,15 @@
     <color name="media_divider">#85ffffff</color>
 
     <!-- media output dialog-->
-    <color name="media_dialog_background">@android:color/system_neutral1_900</color>
-    <color name="media_dialog_active_item_main_content">@android:color/system_neutral1_900</color>
-    <color name="media_dialog_inactive_item_main_content">@android:color/system_neutral1_900</color>
-    <color name="media_dialog_item_status">@android:color/system_neutral1_900</color>
-    <color name="media_dialog_item_background">@android:color/system_accent2_50</color>
+    <color name="media_dialog_background">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_active_item_main_content">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_item_status">@color/material_dynamic_neutral10</color>
+    <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
 
     <!-- Biometric dialog colors -->
     <color name="biometric_dialog_gray">#ffcccccc</color>
-    <color name="biometric_dialog_accent">@android:color/system_accent1_300</color>
+    <color name="biometric_dialog_accent">@color/material_dynamic_primary70</color>
     <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
 
     <!-- UDFPS colors -->
@@ -99,5 +99,5 @@
     <!-- Accessibility floating menu -->
     <color name="accessibility_floating_menu_background">#B3000000</color> <!-- 70% -->
 
-    <color name="people_tile_background">@android:color/system_accent2_800</color>
+    <color name="people_tile_background">@color/material_dynamic_secondary20</color>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index faf518e..f4e7cf3 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -87,7 +87,7 @@
     <color name="notification_section_clear_all_btn_color">@color/GM2_grey_700</color>
 
     <color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
-    <color name="user_switcher_fullscreen_bg">@android:color/system_neutral1_900</color>
+    <color name="user_switcher_fullscreen_bg">@color/material_dynamic_neutral10</color>
     <color name="user_switcher_fullscreen_popup_item_tint">@*android:color/text_color_primary_device_default_dark</color>
 
     <!-- The color of the navigation bar icons. Need to be in sync with ic_sysbar_* -->
@@ -112,7 +112,7 @@
     <!-- Chosen so fill over background matches single tone -->
     <color name="dark_mode_qs_icon_color_dual_tone_fill">#99000000</color>
 
-    <color name="notif_pill_text">@android:color/system_neutral1_900</color>
+    <color name="notif_pill_text">@color/material_dynamic_neutral10</color>
 
     <!-- Keyboard shortcuts colors -->
     <color name="ksh_application_group_color">#fff44336</color>
@@ -131,7 +131,7 @@
     <!-- Biometric dialog colors -->
     <color name="biometric_dialog_dim_color">#80000000</color>              <!-- 50% black -->
     <color name="biometric_dialog_gray">#ff757575</color>
-    <color name="biometric_dialog_accent">@android:color/system_accent1_600</color>
+    <color name="biometric_dialog_accent">@color/material_dynamic_primary40</color>
     <color name="biometric_dialog_error">#ffd93025</color>                  <!-- red 600 -->
 
     <!-- UDFPS colors -->
@@ -155,6 +155,7 @@
     <color name="GM2_grey_900">#202124</color>
 
     <color name="GM2_red_300">#F28B82</color>
+    <color name="GM2_red_500">#EA4335</color>
     <color name="GM2_red_700">#C5221F</color>
 
     <color name="GM2_blue_300">#8AB4F8</color>
@@ -174,11 +175,11 @@
     <color name="media_seamless_border">?android:attr/colorAccent</color>
 
     <!-- media output dialog-->
-    <color name="media_dialog_background" android:lstar="98">@android:color/system_neutral1_100</color>
-    <color name="media_dialog_active_item_main_content">@android:color/system_accent1_900</color>
-    <color name="media_dialog_inactive_item_main_content">@android:color/system_accent1_600</color>
-    <color name="media_dialog_item_status">@android:color/system_accent1_900</color>
-    <color name="media_dialog_item_background">@android:color/system_accent2_50</color>
+    <color name="media_dialog_background" android:lstar="98">@color/material_dynamic_neutral90</color>
+    <color name="media_dialog_active_item_main_content">@color/material_dynamic_primary10</color>
+    <color name="media_dialog_inactive_item_main_content">@color/material_dynamic_primary40</color>
+    <color name="media_dialog_item_status">@color/material_dynamic_primary10</color>
+    <color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
 
     <!-- controls -->
     <color name="control_primary_text">#E6FFFFFF</color>
@@ -213,7 +214,7 @@
     <!-- Wallet screen -->
     <color name="wallet_card_border">#33FFFFFF</color>
 
-    <color name="people_tile_background">@android:color/system_accent2_50</color>
+    <color name="people_tile_background">@color/material_dynamic_secondary95</color>
 
     <!-- Internet Dialog -->
     <!-- Material next state on color-->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d4cc718..3f80647 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1982,6 +1982,12 @@
     <!-- Text for privacy dialog, indicating that an app (or multiple) is using an op on behalf of another [CHAR LIMIT=NONE] -->
     <string name="ongoing_privacy_dialog_attribution_text">(through <xliff:g id="application name(s)" example="Maps, and Assistant">%s</xliff:g>)</string>
 
+    <!-- Text for privacy dialog, displaying just the subattribution label [CHAR LIMIT=NONE] -->
+    <string name="ongoing_privacy_dialog_attribution_label">(<xliff:g id="attribution_label" example="For Wallet">%s</xliff:g>)</string>
+
+    <!-- Text for privacy dialog, displaying both subattribution and proxy label [CHAR LIMIT=NONE] -->
+    <string name="ongoing_privacy_dialog_attribution_proxy_label">(<xliff:g id="attribution_label" example="For Wallet">%1$s</xliff:g>  \u2022 <xliff:g id="proxy_label" example="Maps, and Assistant">%2$s</xliff:g>)</string>
+
     <!-- Text for camera app op [CHAR LIMIT=20]-->
     <string name="privacy_type_camera">camera</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 09ae3f9..9d65c38 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -296,7 +296,7 @@
         <item name="darkIconTheme">@style/DualToneDarkTheme</item>
         <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
         <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_dark</item>
-        <item name="wallpaperTextColorAccent">@*android:color/system_accent1_100</item>
+        <item name="wallpaperTextColorAccent">@color/material_dynamic_primary90</item>
         <item name="android:colorError">@*android:color/error_color_material_dark</item>
         <item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
         <item name="passwordStyle">@style/PasswordTheme</item>
@@ -312,7 +312,7 @@
     <style name="Theme.SystemUI.LightWallpaper">
         <item name="wallpaperTextColor">@*android:color/primary_text_material_light</item>
         <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item>
-        <item name="wallpaperTextColorAccent">@*android:color/system_accent2_600</item>
+        <item name="wallpaperTextColorAccent">@color/material_dynamic_secondary40</item>
         <item name="android:colorError">@*android:color/error_color_material_light</item>
         <item name="shadowRadius">0</item>
 
@@ -353,9 +353,9 @@
         <item name="android:colorError">@*android:color/error_color_material_dark</item>
         <item name="android:windowIsFloating">true</item>
         <item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
-        <item name="offStateColor">@android:color/system_neutral1_800</item>
-        <item name="underSurfaceColor">@android:color/system_neutral1_1000</item>
-        <item name="android:colorBackground">@android:color/system_neutral1_900</item>
+        <item name="offStateColor">@color/material_dynamic_neutral20</item>
+        <item name="underSurfaceColor">@color/material_dynamic_neutral0</item>
+        <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
         <item name="android:itemTextAppearance">@style/Control.MenuItem</item>
     </style>
 
@@ -578,7 +578,7 @@
     <!-- Media controls always have light background -->
     <style name="MediaPlayer" parent="@*android:style/Theme.DeviceDefault.Light">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:backgroundTint">@android:color/system_accent2_50</item>
+        <item name="android:backgroundTint">@color/material_dynamic_secondary95</item>
     </style>
 
     <style name="MediaPlayer.ProgressBar" parent="@android:style/Widget.ProgressBar.Horizontal">
@@ -887,8 +887,8 @@
     </style>
 
     <style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault">
-      <item name="android:colorBackground">@android:color/system_neutral1_900</item>
-      <item name="android:itemBackground">@android:color/system_neutral1_800</item>
+      <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
+      <item name="android:itemBackground">@color/material_dynamic_neutral20</item>
       <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen.  -->
       <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
     </style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
index 8bcb7c9..ccbb0c5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardEsimArea.java
@@ -43,6 +43,7 @@
     private static final String TAG = "KeyguardEsimArea";
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
+    private int mSubscriptionId;
     private EuiccManager mEuiccManager;
 
     private BroadcastReceiver mReceiver =
@@ -87,6 +88,10 @@
         setOnClickListener(this);
     }
 
+    public void setSubscriptionId(int subscriptionId) {
+        mSubscriptionId = subscriptionId;
+    }
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -113,6 +118,12 @@
 
     @Override
     public void onClick(View v) {
+        SubscriptionInfo sub = SubscriptionManager.from(mContext)
+                .getActiveSubscriptionInfo(mSubscriptionId);
+        if (sub == null) {
+            Log.e(TAG, "No active subscription with subscriptionId: " + mSubscriptionId);
+            return;
+        }
         Intent intent = new Intent(ACTION_DISABLE_ESIM);
         intent.setPackage(mContext.getPackageName());
         PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
@@ -120,7 +131,7 @@
             0 /* requestCode */,
             intent,
             PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED, UserHandle.SYSTEM);
-        mEuiccManager
-                .switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, callbackIntent);
+        mEuiccManager.switchToSubscription(
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID, sub.getPortIndex(), callbackIntent);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index c0f9ce7..736e34e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -37,8 +37,9 @@
         super(context, attrs);
     }
 
-    public void setEsimLocked(boolean locked) {
+    public void setEsimLocked(boolean locked, int subscriptionId) {
         KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
+        esimButton.setSubscriptionId(subscriptionId);
         esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index e04bfdc..0394e76 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -103,7 +103,7 @@
             showDefaultMessage();
         }
 
-        mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
+        mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId), mSubId);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 0730922..ea94b19 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -169,6 +169,7 @@
             boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
 
             KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
+            esimButton.setSubscriptionId(mSubId);
             esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
             mPasswordEntry.requestFocus();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 23ca923..ec11065 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -192,6 +192,7 @@
         Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
                 Comparator.comparing(Class::getName));
         sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponents());
+        sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser());
         startServicesIfNeeded(
                 sortedStartables, "StartServices", vendorComponent);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 54664f2..b60e266 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -18,15 +18,13 @@
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
 
-import static java.util.Objects.requireNonNull;
-
 import android.content.ClipboardManager;
 import android.content.Context;
 import android.provider.DeviceConfig;
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.screenshot.TimeoutHandler;
+import com.android.systemui.util.DeviceConfigProxy;
 
 import javax.inject.Inject;
 
@@ -37,19 +35,24 @@
 public class ClipboardListener extends CoreStartable
         implements ClipboardManager.OnPrimaryClipChangedListener {
 
+    private final DeviceConfigProxy mDeviceConfig;
+    private final ClipboardOverlayControllerFactory mOverlayFactory;
+    private final ClipboardManager mClipboardManager;
     private ClipboardOverlayController mClipboardOverlayController;
-    private ClipboardManager mClipboardManager;
 
     @Inject
-    public ClipboardListener(Context context) {
+    public ClipboardListener(Context context, DeviceConfigProxy deviceConfigProxy,
+            ClipboardOverlayControllerFactory overlayFactory, ClipboardManager clipboardManager) {
         super(context);
+        mDeviceConfig = deviceConfigProxy;
+        mOverlayFactory = overlayFactory;
+        mClipboardManager = clipboardManager;
     }
 
     @Override
     public void start() {
-        if (DeviceConfig.getBoolean(
+        if (mDeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) {
-            mClipboardManager = requireNonNull(mContext.getSystemService(ClipboardManager.class));
             mClipboardManager.addPrimaryClipChangedListener(this);
         }
     }
@@ -60,8 +63,7 @@
             return;
         }
         if (mClipboardOverlayController == null) {
-            mClipboardOverlayController =
-                    new ClipboardOverlayController(mContext, new TimeoutHandler(mContext));
+            mClipboardOverlayController = mOverlayFactory.create(mContext);
         }
         mClipboardOverlayController.setClipData(
                 mClipboardManager.getPrimaryClip(), mClipboardManager.getPrimaryClipSource());
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerFactory.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerFactory.java
new file mode 100644
index 0000000..e1c11c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerFactory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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.clipboardoverlay;
+
+import android.content.Context;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.screenshot.TimeoutHandler;
+
+import javax.inject.Inject;
+
+/**
+ * A factory that churns out ClipboardOverlayControllers on demand.
+ */
+@SysUISingleton
+public class ClipboardOverlayControllerFactory {
+
+    @Inject
+    public ClipboardOverlayControllerFactory() {}
+
+    /**
+     * One new ClipboardOverlayController, coming right up!
+     */
+    public ClipboardOverlayController create(Context context) {
+        return new ClipboardOverlayController(context, new TimeoutHandler(context));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index fa23842..f6aeb2a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -49,6 +49,7 @@
 import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
+import com.android.systemui.clipboardoverlay.ClipboardOverlayControllerFactory;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
@@ -291,4 +292,11 @@
     public ModeSwitchesController providesModeSwitchesController(Context context) {
         return new ModeSwitchesController(context);
     }
+
+    /***/
+    @Provides
+    @SysUISingleton
+    public ClipboardOverlayControllerFactory provideClipboardOverlayControllerFactory() {
+        return new ClipboardOverlayControllerFactory();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index b51eb07..bd472a4 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -31,6 +31,7 @@
 import android.app.role.RoleManager;
 import android.app.smartspace.SmartspaceManager;
 import android.app.trust.TrustManager;
+import android.content.ClipboardManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.om.OverlayManager;
@@ -451,6 +452,12 @@
 
     @Provides
     @Singleton
+    static ClipboardManager provideClipboardManager(Context context) {
+        return context.getSystemService(ClipboardManager.class);
+    }
+
+    @Provides
+    @Singleton
     static SmartspaceManager provideSmartspaceManager(Context context) {
         return context.getSystemService(SmartspaceManager.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index dd36fd6..5487c42 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -145,7 +145,7 @@
     // 900 - media
     public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true);
     public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true);
-    public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, false);
+    public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, true);
     public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true);
     public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index ecaa142..83ad027 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -540,14 +540,14 @@
     }
 
     private fun getBackgroundColor(): Int {
-        return context.getColor(android.R.color.system_accent2_50)
+        return context.getColor(R.color.material_dynamic_secondary95)
     }
 
     private fun getForegroundColor(): Int {
         return if (mediaFlags.useMediaSessionLayout()) {
-            context.getColor(android.R.color.system_neutral2_200)
+            context.getColor(R.color.material_dynamic_neutral_variant80)
         } else {
-            context.getColor(android.R.color.system_accent2_900)
+            context.getColor(R.color.material_dynamic_secondary10)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index ce7a697..831a606 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -476,7 +476,7 @@
         appIconView.clearColorFilter();
         if (data.getAppIcon() != null && !data.getResumption()) {
             appIconView.setImageIcon(data.getAppIcon());
-            int color = mContext.getColor(android.R.color.system_accent2_900);
+            int color = mContext.getColor(R.color.material_dynamic_secondary10);
             appIconView.setColorFilter(color);
         } else {
             // Resume players use launcher icon
@@ -590,7 +590,7 @@
             } else {
                 appIconView.setImageResource(R.drawable.ic_music_note);
             }
-            int color = mContext.getColor(android.R.color.system_accent2_900);
+            int color = mContext.getColor(R.color.material_dynamic_secondary10);
             appIconView.setColorFilter(color);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index e1ff110..1d1a230 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -150,7 +150,7 @@
 
     private val themeText = com.android.settingslib.Utils.getColorAttr(context,
             com.android.internal.R.attr.textColorPrimary).defaultColor
-    private val bgColor = context.getColor(android.R.color.system_accent2_50)
+    private val bgColor = context.getColor(R.color.material_dynamic_secondary95)
 
     // Internal listeners are part of the internal pipeline. External listeners (those registered
     // with [MediaDeviceManager.addListener]) receive events after they have propagated through
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 2dff947..c3b4354 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -16,12 +16,7 @@
 
 package com.android.systemui.media.dagger;
 
-import android.content.Context;
-import android.os.Handler;
-import android.view.WindowManager;
-
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaFlags;
 import com.android.systemui.media.MediaHierarchyManager;
@@ -34,11 +29,8 @@
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
 import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
 import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.commandline.CommandRegistry;
 
 import java.util.Optional;
-import java.util.concurrent.Executor;
 
 import javax.inject.Named;
 
@@ -101,13 +93,11 @@
     @SysUISingleton
     static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
             MediaTttFlags mediaTttFlags,
-            CommandQueue commandQueue,
-            Context context,
-            WindowManager windowManager) {
+            Lazy<MediaTttChipControllerSender> controllerSenderLazy) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(new MediaTttChipControllerSender(commandQueue, context, windowManager));
+        return Optional.of(controllerSenderLazy.get());
     }
 
     /** */
@@ -115,16 +105,11 @@
     @SysUISingleton
     static Optional<MediaTttChipControllerReceiver> providesMediaTttChipControllerReceiver(
             MediaTttFlags mediaTttFlags,
-            CommandQueue commandQueue,
-            Context context,
-            WindowManager windowManager,
-            @Main Handler mainHandler) {
+            Lazy<MediaTttChipControllerReceiver> controllerReceiverLazy) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(
-                new MediaTttChipControllerReceiver(
-                        commandQueue, context, windowManager, mainHandler));
+        return Optional.of(controllerReceiverLazy.get());
     }
 
     /** */
@@ -132,14 +117,11 @@
     @SysUISingleton
     static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
             MediaTttFlags mediaTttFlags,
-            CommandRegistry commandRegistry,
-            Context context,
-            @Main Executor mainExecutor) {
+            Lazy<MediaTttCommandLineHelper> helperLazy) {
         if (!mediaTttFlags.isMediaTttEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(
-                new MediaTttCommandLineHelper(commandRegistry, context, mainExecutor));
+        return Optional.of(helperLazy.get());
     }
 
     /** */
@@ -147,13 +129,12 @@
     @SysUISingleton
     static Optional<MediaMuteAwaitConnectionCli> providesMediaMuteAwaitConnectionCli(
             MediaFlags mediaFlags,
-            CommandRegistry commandRegistry,
-            Context context
+            Lazy<MediaMuteAwaitConnectionCli> muteAwaitConnectionCliLazy
     ) {
         if (!mediaFlags.areMuteAwaitConnectionsEnabled()) {
             return Optional.empty();
         }
-        return Optional.of(new MediaMuteAwaitConnectionCli(commandRegistry, context));
+        return Optional.of(muteAwaitConnectionCliLazy.get());
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 4993105..ee2fba0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -25,8 +25,11 @@
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
+import androidx.annotation.VisibleForTesting
 import com.android.internal.widget.CachingIconView
 import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
 
 /**
  * A superclass controller that provides common functionality for showing chips on the sender device
@@ -38,6 +41,7 @@
 abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
     internal val context: Context,
     private val windowManager: WindowManager,
+    @Main private val mainExecutor: DelayableExecutor,
     @LayoutRes private val chipLayoutRes: Int
 ) {
     /** The window layout parameters we'll use when attaching the view to a window. */
@@ -56,6 +60,9 @@
     /** The chip view currently being displayed. Null if the chip is not being displayed. */
     var chipView: ViewGroup? = null
 
+    /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
+    var cancelChipViewTimeout: Runnable? = null
+
     /**
      * Displays the chip with the current state.
      *
@@ -77,8 +84,11 @@
         if (oldChipView == null) {
             windowManager.addView(chipView, windowLayoutParams)
         }
-    }
 
+        // Cancel and re-set the chip timeout each time we get a new state.
+        cancelChipViewTimeout?.run()
+        cancelChipViewTimeout = mainExecutor.executeDelayed(this::removeChip, TIMEOUT_MILLIS)
+    }
 
     /** Hides the chip. */
     fun removeChip() {
@@ -118,3 +128,5 @@
 // Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
 // UpdateMediaTapToTransferReceiverDisplayTest
 private const val WINDOW_TITLE = "Media Transfer Chip View"
+@VisibleForTesting
+const val TIMEOUT_MILLIS = 3000L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 18623c0..214a888 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -18,7 +18,6 @@
 
 import android.app.StatusBarManager
 import android.content.Context
-import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.media.MediaRoute2Info
 import android.os.Handler
@@ -27,10 +26,10 @@
 import android.view.WindowManager
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.DelayableExecutor
 import javax.inject.Inject
 
 /**
@@ -43,9 +42,10 @@
     commandQueue: CommandQueue,
     context: Context,
     windowManager: WindowManager,
+    mainExecutor: DelayableExecutor,
     @Main private val mainHandler: Handler,
 ) : MediaTttChipControllerCommon<ChipStateReceiver>(
-    context, windowManager, R.layout.media_ttt_chip_receiver
+    context, windowManager, mainExecutor, R.layout.media_ttt_chip_receiver
 ) {
     private val commandQueueCallbacks = object : CommandQueue.Callbacks {
         override fun updateMediaTapToTransferReceiverDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index da767ea..482e604 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -27,8 +27,10 @@
 import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.DelayableExecutor
 import javax.inject.Inject
 
 /**
@@ -40,8 +42,9 @@
     commandQueue: CommandQueue,
     context: Context,
     windowManager: WindowManager,
+    @Main private val mainExecutor: DelayableExecutor,
 ) : MediaTttChipControllerCommon<ChipStateSender>(
-    context, windowManager, R.layout.media_ttt_chip
+    context, windowManager, mainExecutor,  R.layout.media_ttt_chip
 ) {
     private val commandQueueCallbacks = object : CommandQueue.Callbacks {
         override fun updateMediaTapToTransferSenderDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 9199911..9ea2763 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -263,15 +263,6 @@
                     // Notify FalsingManager that an intentional gesture has occurred.
                     // TODO(b/186519446): use a different method than isFalseTouch
                     mFalsingManager.isFalseTouch(BACK_GESTURE);
-                    // Only inject back keycodes when ahead-of-time back dispatching is disabled.
-                    if (mBackAnimation == null) {
-                        boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
-                        boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
-                        if (DEBUG_MISSING_GESTURE) {
-                            Log.d(DEBUG_MISSING_GESTURE_TAG, "Triggered back: down="
-                                    + sendDown + ", up=" + sendUp);
-                        }
-                    }
 
                     mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
                             (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 23482677..fe4cb71 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.privacy
 
 import android.content.Context
+import android.content.Intent
 import android.graphics.drawable.LayerDrawable
 import android.os.Bundle
 import android.text.TextUtils
@@ -40,12 +41,12 @@
  * @param context A context to create the dialog
  * @param list list of elements to show in the dialog. The elements will show in the same order they
  *             appear in the list
- * @param activityStarter a callback to start an activity for a given package name and user id
+ * @param activityStarter a callback to start an activity for a given package name, user id, attributionTag and intent
  */
 class PrivacyDialog(
     context: Context,
     private val list: List<PrivacyElement>,
-    activityStarter: (String, Int) -> Unit
+    activityStarter: (String, Int, CharSequence?, Intent?) -> Unit
 ) : SystemUIDialog(context, R.style.PrivacyDialog) {
 
     private val dismissListeners = mutableListOf<WeakReference<OnDialogDismissed>>()
@@ -118,13 +119,7 @@
             app
         }
         val firstLine = context.getString(stringId, appName)
-        val finalText = element.attribution?.let {
-            TextUtils.concat(
-                    firstLine,
-                    " ",
-                    context.getString(R.string.ongoing_privacy_dialog_attribution_text, it)
-            )
-        } ?: firstLine
+        val finalText = getFinalText(firstLine, element.attributionLabel, element.proxyLabel)
         newView.requireViewById<TextView>(R.id.text).text = finalText
         if (element.phoneCall) {
             newView.requireViewById<View>(R.id.chevron).visibility = View.GONE
@@ -138,6 +133,25 @@
         return newView
     }
 
+    private fun getFinalText(
+        firstLine: CharSequence,
+        attributionLabel: CharSequence?,
+        proxyLabel: CharSequence?
+    ): CharSequence {
+        var dialogText: CharSequence? = null
+        if (attributionLabel != null && proxyLabel != null) {
+            dialogText = context.getString(R.string.ongoing_privacy_dialog_attribution_proxy_label,
+                attributionLabel, proxyLabel)
+        } else if (attributionLabel != null) {
+            dialogText = context.getString(R.string.ongoing_privacy_dialog_attribution_label,
+                attributionLabel)
+        } else if (proxyLabel != null) {
+            dialogText = context.getString(R.string.ongoing_privacy_dialog_attribution_text,
+                proxyLabel)
+        }
+        return if (dialogText != null) TextUtils.concat(firstLine, " ", dialogText) else firstLine
+    }
+
     private fun getStringIdForState(active: Boolean): Int {
         return if (active) {
             R.string.ongoing_privacy_dialog_using_op
@@ -157,7 +171,8 @@
     private val clickListener = View.OnClickListener { v ->
         v.tag?.let {
             val element = it as PrivacyElement
-            activityStarter(element.packageName, element.userId)
+            activityStarter(element.packageName, element.userId,
+                element.attributionTag, element.navigationIntent)
         }
     }
 
@@ -167,11 +182,15 @@
         val packageName: String,
         val userId: Int,
         val applicationName: CharSequence,
-        val attribution: CharSequence?,
+        val attributionTag: CharSequence?,
+        val attributionLabel: CharSequence?,
+        val proxyLabel: CharSequence?,
         val lastActiveTimestamp: Long,
         val active: Boolean,
         val enterprise: Boolean,
-        val phoneCall: Boolean
+        val phoneCall: Boolean,
+        val permGroupName: CharSequence,
+        val navigationIntent: Intent?
     ) {
         private val builder = StringBuilder("PrivacyElement(")
 
@@ -180,8 +199,14 @@
             builder.append(", packageName=$packageName")
             builder.append(", userId=$userId")
             builder.append(", appName=$applicationName")
-            if (attribution != null) {
-                builder.append(", attribution=$attribution")
+            if (attributionTag != null) {
+                builder.append(", attributionTag=$attributionTag")
+            }
+            if (attributionLabel != null) {
+                builder.append(", attributionLabel=$attributionLabel")
+            }
+            if (proxyLabel != null) {
+                builder.append(", proxyLabel=$proxyLabel")
             }
             builder.append(", lastActive=$lastActiveTimestamp")
             if (active) {
@@ -193,7 +218,10 @@
             if (phoneCall) {
                 builder.append(", phoneCall")
             }
-            builder.append(")")
+            builder.append(", permGroupName=$permGroupName)")
+            if (navigationIntent != null) {
+                builder.append(", navigationIntent=$navigationIntent")
+            }
         }
 
         override fun toString(): String = builder.toString()
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index d0aa710..6107123 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -19,11 +19,12 @@
 import android.Manifest
 import android.app.ActivityManager
 import android.app.Dialog
+import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.os.UserHandle
-import android.permission.PermGroupUsage
+import android.permission.PermissionGroupUsage
 import android.permission.PermissionManager
 import android.util.Log
 import androidx.annotation.MainThread
@@ -45,11 +46,12 @@
     override fun makeDialog(
         context: Context,
         list: List<PrivacyDialog.PrivacyElement>,
-        starter: (String, Int) -> Unit
+        starter: (String, Int, CharSequence?, Intent?) -> Unit
     ): PrivacyDialog {
         return PrivacyDialog(context, list, starter)
     }
 }
+
 /**
  * Controller for [PrivacyDialog].
  *
@@ -115,12 +117,19 @@
     }
 
     @MainThread
-    private fun startActivity(packageName: String, userId: Int) {
-        val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
-        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
-        intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+    private fun startActivity(
+        packageName: String,
+        userId: Int,
+        attributionTag: CharSequence?,
+        navigationIntent: Intent?
+    ) {
+        val intent = if (navigationIntent == null) {
+            getDefaultManageAppPermissionsIntent(packageName, userId)
+        } else {
+            navigationIntent
+        }
         uiEventLogger.log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
-                userId, packageName)
+            userId, packageName)
         privacyLogger.logStartSettingsActivityFromDialog(packageName, userId)
         if (!keyguardStateController.isUnlocked) {
             // If we are locked, hide the dialog so the user can unlock
@@ -137,7 +146,39 @@
     }
 
     @WorkerThread
-    private fun permGroupUsage(): List<PermGroupUsage> {
+    private fun getManagePermissionIntent(
+        packageName: String,
+        userId: Int,
+        permGroupName: CharSequence,
+        attributionTag: CharSequence?
+    ): Intent
+    {
+        lateinit var intent: Intent
+        if (attributionTag != null) {
+            intent = Intent(Intent.ACTION_MANAGE_PERMISSION_USAGE)
+            intent.setPackage(packageName)
+            intent.putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, permGroupName.toString())
+            intent.putExtra(Intent.EXTRA_ATTRIBUTION_TAGS, arrayOf(attributionTag.toString()))
+            intent.putExtra(Intent.EXTRA_SHOWING_ATTRIBUTION, true)
+            val resolveInfo = packageManager.resolveActivity(
+                intent, PackageManager.ResolveInfoFlags.of(0))
+                ?: return getDefaultManageAppPermissionsIntent(packageName, userId)
+            intent.component = ComponentName(packageName, resolveInfo.activityInfo.name)
+            return intent
+        } else {
+            return getDefaultManageAppPermissionsIntent(packageName, userId)
+        }
+    }
+
+    fun getDefaultManageAppPermissionsIntent(packageName: String, userId: Int): Intent {
+        val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
+        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
+        intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+        return intent
+    }
+
+    @WorkerThread
+    private fun permGroupUsage(): List<PermissionGroupUsage> {
         return permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted)
     }
 
@@ -160,7 +201,7 @@
             val userInfos = userTracker.userProfiles
             privacyLogger.logUnfilteredPermGroupUsage(usage)
             val items = usage.mapNotNull {
-                val type = filterType(permGroupToPrivacyType(it.permGroupName))
+                val type = filterType(permGroupToPrivacyType(it.permissionGroupName))
                 val userInfo = userInfos.firstOrNull { ui -> ui.id == UserHandle.getUserId(it.uid) }
                 if (userInfo != null || it.isPhoneCall) {
                     type?.let { t ->
@@ -170,17 +211,24 @@
                         } else {
                             getLabelForPackage(it.packageName, it.uid)
                         }
+                        val userId = UserHandle.getUserId(it.uid)
                         PrivacyDialog.PrivacyElement(
                                 t,
                                 it.packageName,
-                                UserHandle.getUserId(it.uid),
+                                userId,
                                 appName,
-                                it.attribution,
-                                it.lastAccess,
+                                it.attributionTag,
+                                it.attributionLabel,
+                                it.proxyLabel,
+                                it.lastAccessTimeMillis,
                                 it.isActive,
                                 // If there's no user info, we're in a phoneCall in secondary user
                                 userInfo?.isManagedProfile ?: false,
-                                it.isPhoneCall
+                                it.isPhoneCall,
+                                it.permissionGroupName,
+                                getManagePermissionIntent(it.packageName, userId,
+                                    it.permissionGroupName,
+                                it.attributionTag)
                         )
                     }
                 } else {
@@ -215,8 +263,8 @@
     private fun getLabelForPackage(packageName: String, uid: Int): CharSequence {
         return try {
             packageManager
-                    .getApplicationInfoAsUser(packageName, 0, UserHandle.getUserId(uid))
-                    .loadLabel(packageManager)
+                .getApplicationInfoAsUser(packageName, 0, UserHandle.getUserId(uid))
+                .loadLabel(packageManager)
         } catch (_: PackageManager.NameNotFoundException) {
             Log.w(TAG, "Label not found for: $packageName")
             packageName
@@ -235,7 +283,7 @@
     private fun filterType(type: PrivacyType?): PrivacyType? {
         return type?.let {
             if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) &&
-                    privacyItemController.micCameraAvailable) {
+                privacyItemController.micCameraAvailable) {
                 it
             } else if (it == PrivacyType.TYPE_LOCATION && privacyItemController.locationAvailable) {
                 it
@@ -278,7 +326,7 @@
         fun makeDialog(
             context: Context,
             list: List<PrivacyDialog.PrivacyElement>,
-            starter: (String, Int) -> Unit
+            starter: (String, Int, CharSequence?, Intent?) -> Unit
         ): PrivacyDialog
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
index 7c82a82..1a268b5 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.privacy.logging
 
-import android.permission.PermGroupUsage
+import android.permission.PermissionGroupUsage
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogLevel
 import com.android.systemui.log.LogMessage
@@ -100,7 +100,7 @@
         })
     }
 
-    fun logUnfilteredPermGroupUsage(contents: List<PermGroupUsage>) {
+    fun logUnfilteredPermGroupUsage(contents: List<PermissionGroupUsage>) {
         log(LogLevel.DEBUG, {
             str1 = contents.toString()
         }, {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 3b305bb..8afb793 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -1,8 +1,16 @@
 package com.android.systemui.qs
 
+import android.content.Intent
+import android.permission.PermissionGroupUsage
+import android.permission.PermissionManager
+import android.provider.DeviceConfig
 import android.view.View
+import androidx.annotation.WorkerThread
 import com.android.internal.R
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.privacy.OngoingPrivacyChip
 import com.android.systemui.privacy.PrivacyChipEvent
 import com.android.systemui.privacy.PrivacyDialogController
@@ -10,7 +18,11 @@
 import com.android.systemui.privacy.PrivacyItemController
 import com.android.systemui.privacy.logging.PrivacyLogger
 import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.util.DeviceConfigProxy
+import java.util.concurrent.Executor
 import javax.inject.Inject
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 
 interface ChipVisibilityListener {
     fun onChipVisibilityRefreshed(visible: Boolean)
@@ -32,18 +44,46 @@
     private val privacyChip: OngoingPrivacyChip,
     private val privacyDialogController: PrivacyDialogController,
     private val privacyLogger: PrivacyLogger,
-    private val iconContainer: StatusIconContainer
+    private val iconContainer: StatusIconContainer,
+    private val permissionManager: PermissionManager,
+    @Background private val backgroundExecutor: Executor,
+    @Main private val uiExecutor: Executor,
+    private val activityStarter: ActivityStarter,
+    private val appOpsController: AppOpsController,
+    private val deviceConfigProxy: DeviceConfigProxy
 ) {
 
+    companion object {
+        const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+    }
+
     var chipVisibilityListener: ChipVisibilityListener? = null
+
     private var listening = false
     private var micCameraIndicatorsEnabled = false
     private var locationIndicatorsEnabled = false
     private var privacyChipLogged = false
+    private var safetyCenterEnabled = false
     private val cameraSlot = privacyChip.resources.getString(R.string.status_bar_camera)
     private val micSlot = privacyChip.resources.getString(R.string.status_bar_microphone)
     private val locationSlot = privacyChip.resources.getString(R.string.status_bar_location)
 
+    private val devicePropertiesChangedListener =
+        object : DeviceConfig.OnPropertiesChangedListener {
+            override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
+                safetyCenterEnabled = properties.getBoolean(SAFETY_CENTER_ENABLED, false)
+            }
+        }
+
+    init {
+        safetyCenterEnabled = deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+            SAFETY_CENTER_ENABLED, false)
+        deviceConfigProxy.addOnPropertiesChangedListener(
+            DeviceConfig.NAMESPACE_PRIVACY,
+            uiExecutor,
+            devicePropertiesChangedListener)
+    }
+
     private val picCallback: PrivacyItemController.Callback =
             object : PrivacyItemController.Callback {
         override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
@@ -77,7 +117,11 @@
         privacyChip.setOnClickListener {
             // If the privacy chip is visible, it means there were some indicators
             uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
-            privacyDialogController.showDialog(privacyChip.context)
+            if (safetyCenterEnabled) {
+                showSafetyHub()
+            } else {
+                privacyDialogController.showDialog(privacyChip.context)
+            }
         }
         setChipVisibility(privacyChip.visibility == View.VISIBLE)
         micCameraIndicatorsEnabled = privacyItemController.micCameraAvailable
@@ -87,6 +131,26 @@
         updatePrivacyIconSlots()
     }
 
+    private fun showSafetyHub() {
+        backgroundExecutor.execute {
+            val usage = ArrayList(permGroupUsage())
+            privacyLogger.logUnfilteredPermGroupUsage(usage)
+            val startSafetyHub = Intent(Intent.ACTION_VIEW_SAFETY_HUB)
+            startSafetyHub.putParcelableArrayListExtra(PermissionManager.EXTRA_PERMISSION_USAGES,
+                usage)
+            startSafetyHub.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+            uiExecutor.execute {
+                activityStarter.startActivity(startSafetyHub, true,
+                    ActivityLaunchAnimator.Controller.fromView(privacyChip))
+            }
+        }
+    }
+
+    @WorkerThread
+    private fun permGroupUsage(): List<PermissionGroupUsage> {
+        return permissionManager.getIndicatorAppOpUsageData(appOpsController.isMicMuted)
+    }
+
     fun onParentInvisible() {
         chipVisibilityListener = null
         privacyChip.setOnClickListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 17f85ee..3c7933f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -381,14 +381,17 @@
                 : View.INVISIBLE);
         mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
-        boolean footerVisible = !mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
+        boolean qsPanelVisible = !mQsDisabled && expandVisually;
+        boolean footerVisible = qsPanelVisible &&  (expanded || !keyguardShowing || mHeaderAnimating
                 || mShowCollapsedOnKeyguard);
         mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
         mQSFooterActionController.setVisible(footerVisible);
         mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
                 || (expanded && !mStackScrollerOverscrolling));
-        mQSPanelController.setVisibility(
-                !mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
+        mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE);
+        if (DEBUG) {
+            Log.d(TAG, "Footer: " + footerVisible + ", QS Panel: " + qsPanelVisible);
+        }
     }
 
     private boolean isKeyguardState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 3a3f581..952cd9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -202,10 +202,12 @@
             float newHeight = state.height;
             float newNotificationEnd = newYTranslation + newHeight;
             boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned();
+            final boolean shadeClosedWithHUN =
+                    ambientState.isShadeOpening() && !ambientState.isShadeExpanded();
             if (mClipNotificationScrollToTop
                     && (!state.inShelf || (isHeadsUp && !firstHeadsUp))
                     && newYTranslation < clipStart
-                    && !ambientState.isShadeOpening()) {
+                    && shadeClosedWithHUN) {
                 // The previous view is overlapping on top, clip!
                 float overlapAmount = clipStart - newYTranslation;
                 state.clipTopAmount = (int) overlapAmount;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
index 5e9bae9..d2142dc 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
@@ -48,8 +48,8 @@
                         R.dimen.card_carousel_dot_selected_radius);
         mDotMargin = context.getResources().getDimensionPixelSize(R.dimen.card_carousel_dot_margin);
 
-        mUnselectedColor = context.getColor(com.android.internal.R.color.system_neutral1_300);
-        mSelectedColor = context.getColor(com.android.internal.R.color.system_neutral1_0);
+        mUnselectedColor = context.getColor(R.color.material_dynamic_neutral70);
+        mSelectedColor = context.getColor(R.color.material_dynamic_neutral100);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 9917777..92e7f2d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -278,7 +278,7 @@
 
     private Drawable getHomeIndicatorDrawable() {
         Drawable drawable = getDrawable(R.drawable.ic_close);
-        drawable.setTint(getColor(com.android.internal.R.color.system_neutral1_300));
+        drawable.setTint(getColor(R.color.material_dynamic_neutral70));
         return drawable;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
new file mode 100644
index 0000000..e15b6cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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.clipboardoverlay;
+
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_ENABLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.provider.DeviceConfig;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.DeviceConfigProxyFake;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ClipboardListenerTest extends SysuiTestCase {
+
+    @Mock
+    private ClipboardManager mClipboardManager;
+    @Mock
+    private ClipboardOverlayControllerFactory mClipboardOverlayControllerFactory;
+    @Mock
+    private ClipboardOverlayController mOverlayController;
+    private DeviceConfigProxyFake mDeviceConfigProxy;
+
+    private ClipData mSampleClipData;
+    private String mSampleSource = "Example source";
+
+    @Captor
+    private ArgumentCaptor<Runnable> mRunnableCaptor;
+    @Captor
+    private ArgumentCaptor<ClipData> mClipDataCaptor;
+    @Captor
+    private ArgumentCaptor<String> mStringCaptor;
+
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mClipboardOverlayControllerFactory.create(any())).thenReturn(
+                mOverlayController);
+        when(mClipboardManager.hasPrimaryClip()).thenReturn(true);
+
+
+        mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
+                new ClipData.Item("Test Item"));
+        when(mClipboardManager.getPrimaryClip()).thenReturn(mSampleClipData);
+        when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource);
+
+        mDeviceConfigProxy = new DeviceConfigProxyFake();
+    }
+
+    @Test
+    public void test_disabled() {
+        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
+                "false", false);
+        ClipboardListener listener = new ClipboardListener(getContext(), mDeviceConfigProxy,
+                mClipboardOverlayControllerFactory, mClipboardManager);
+        listener.start();
+        verifyZeroInteractions(mClipboardManager);
+    }
+
+    @Test
+    public void test_enabled() {
+        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
+                "true", false);
+        ClipboardListener listener = new ClipboardListener(getContext(), mDeviceConfigProxy,
+                mClipboardOverlayControllerFactory, mClipboardManager);
+        listener.start();
+        verify(mClipboardManager).addPrimaryClipChangedListener(any());
+    }
+
+    @Test
+    public void test_consecutiveCopies() {
+        mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED,
+                "true", false);
+        ClipboardListener listener = new ClipboardListener(getContext(), mDeviceConfigProxy,
+                mClipboardOverlayControllerFactory, mClipboardManager);
+        listener.start();
+        listener.onPrimaryClipChanged();
+
+        verify(mClipboardOverlayControllerFactory).create(any());
+
+        verify(mOverlayController).setClipData(mClipDataCaptor.capture(), mStringCaptor.capture());
+
+        assertEquals(mSampleClipData, mClipDataCaptor.getValue());
+        assertEquals(mSampleSource, mStringCaptor.getValue());
+
+        verify(mOverlayController).setOnSessionCompleteListener(mRunnableCaptor.capture());
+
+        // Should clear the overlay controller
+        mRunnableCaptor.getValue().run();
+
+        listener.onPrimaryClipChanged();
+
+        verify(mClipboardOverlayControllerFactory, times(2)).create(any());
+
+        // Not calling the runnable here, just change the clip again and verify that the overlay is
+        // NOT recreated.
+
+        listener.onPrimaryClipChanged();
+
+        verify(mClipboardOverlayControllerFactory, times(2)).create(any());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 14afece..794bc09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -39,7 +39,6 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.Mock
 import org.mockito.Mockito.verify
@@ -49,7 +48,6 @@
 import java.util.concurrent.Executor
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttCommandLineHelperTest : SysuiTestCase() {
 
     private val inlineExecutor = Executor { command -> command.run() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index f05d621..ea0a5a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.graphics.drawable.Drawable
-import android.graphics.drawable.Icon
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
@@ -26,10 +25,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
@@ -39,10 +41,12 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttChipControllerCommonTest : SysuiTestCase() {
     private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState>
 
+    private lateinit var fakeClock: FakeSystemClock
+    private lateinit var fakeExecutor: FakeExecutor
+
     private lateinit var appIconDrawable: Drawable
     @Mock
     private lateinit var windowManager: WindowManager
@@ -50,8 +54,11 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
-        controllerCommon = TestControllerCommon(context, windowManager)
+        appIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+        fakeClock = FakeSystemClock()
+        fakeExecutor = FakeExecutor(fakeClock)
+
+        controllerCommon = TestControllerCommon(context, windowManager, fakeExecutor)
     }
 
     @Test
@@ -71,6 +78,58 @@
     }
 
     @Test
+    fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
+        controllerCommon.displayChip(getState())
+        reset(windowManager)
+
+        fakeClock.advanceTime(TIMEOUT_MILLIS - 1)
+
+        verify(windowManager, never()).removeView(any())
+    }
+
+    @Test
+    fun displayChip_chipDisappearsAfterTimeout() {
+        controllerCommon.displayChip(getState())
+        reset(windowManager)
+
+        fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+
+        verify(windowManager).removeView(any())
+    }
+
+    @Test
+    fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
+        // First, display the chip
+        controllerCommon.displayChip(getState())
+
+        // After some time, re-display the chip
+        val waitTime = 1000L
+        fakeClock.advanceTime(waitTime)
+        controllerCommon.displayChip(getState())
+
+        // Wait until the timeout for the first display would've happened
+        fakeClock.advanceTime(TIMEOUT_MILLIS - waitTime + 1)
+
+        // Verify we didn't hide the chip
+        verify(windowManager, never()).removeView(any())
+    }
+
+    @Test
+    fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
+        // First, display the chip
+        controllerCommon.displayChip(getState())
+
+        // After some time, re-display the chip
+        fakeClock.advanceTime(1000L)
+        controllerCommon.displayChip(getState())
+
+        // Ensure we still hide the chip eventually
+        fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
+
+        verify(windowManager).removeView(any())
+    }
+
+    @Test
     fun removeChip_chipRemoved() {
         // First, add the chip
         controllerCommon.displayChip(getState())
@@ -93,10 +152,10 @@
         controllerCommon.displayChip(getState())
         val chipView = getChipView()
 
-        val state = MediaTttChipState(PACKAGE_NAME)
+        val state = TestChipState(PACKAGE_NAME)
         controllerCommon.setIcon(state, chipView)
 
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
         assertThat(chipView.getAppIconView().contentDescription)
                 .isEqualTo(state.getAppName(context))
     }
@@ -113,13 +172,18 @@
 
     inner class TestControllerCommon(
         context: Context,
-        windowManager: WindowManager
-    ) : MediaTttChipControllerCommon<MediaTttChipState>(
-        context, windowManager, R.layout.media_ttt_chip
+        windowManager: WindowManager,
+        @Main mainExecutor: DelayableExecutor,
+        ) : MediaTttChipControllerCommon<MediaTttChipState>(
+        context, windowManager, mainExecutor, R.layout.media_ttt_chip
     ) {
         override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) {
         }
     }
+
+    inner class TestChipState(appPackageName: String?) : MediaTttChipState(appPackageName) {
+        override fun getAppIcon(context: Context) = appIconDrawable
+    }
 }
 
 private const val PACKAGE_NAME = "com.android.systemui"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 44f691c..117a6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -17,7 +17,9 @@
 package com.android.systemui.media.taptotransfer.receiver
 
 import android.app.StatusBarManager
-import android.graphics.drawable.Icon
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
 import android.media.MediaRoute2Info
 import android.os.Handler
 import android.view.View
@@ -28,33 +30,54 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttChipControllerReceiverTest : SysuiTestCase() {
     private lateinit var controllerReceiver: MediaTttChipControllerReceiver
 
     @Mock
+    private lateinit var packageManager: PackageManager
+    @Mock
+    private lateinit var applicationInfo: ApplicationInfo
+    @Mock
     private lateinit var windowManager: WindowManager
     @Mock
     private lateinit var commandQueue: CommandQueue
     private lateinit var commandQueueCallback: CommandQueue.Callbacks
+    private lateinit var fakeAppIconDrawable: Drawable
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+
+        fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+        whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
+        whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+        whenever(packageManager.getApplicationInfo(
+            eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
+        )).thenReturn(applicationInfo)
+        context.setMockPackageManager(packageManager)
+
         controllerReceiver = MediaTttChipControllerReceiver(
-                commandQueue, context, windowManager, Handler.getMain())
+            commandQueue,
+            context,
+            windowManager,
+            FakeExecutor(FakeSystemClock()),
+            Handler.getMain()
+        )
 
         val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
         verify(commandQueue).addCallback(callbackCaptor.capture())
@@ -113,13 +136,12 @@
 
         controllerReceiver.displayChip(state)
 
-        assertThat(getChipView().getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-
+        assertThat(getChipView().getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
     }
 
     @Test
     fun displayChip_hasAppIconDrawable_iconIsDrawable() {
-        val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
+        val drawable = context.getDrawable(R.drawable.ic_alarm)!!
         val state = ChipStateReceiver(PACKAGE_NAME, drawable, "appName")
 
         controllerReceiver.displayChip(state)
@@ -133,13 +155,12 @@
 
         controllerReceiver.displayChip(state)
 
-        assertThat(getChipView().getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(APP_NAME)
     }
 
     @Test
     fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() {
-        val appName = "FakeAppName"
+        val appName = "Override App Name"
         val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName)
 
         controllerReceiver.displayChip(state)
@@ -156,6 +177,7 @@
     private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
 }
 
+private const val APP_NAME = "Fake app name"
 private const val PACKAGE_NAME = "com.android.systemui"
 
 private val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index dc39893..b440064 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.media.taptotransfer.sender
 
 import android.app.StatusBarManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
 import android.media.MediaRoute2Info
 import android.view.View
 import android.view.WindowManager
@@ -28,32 +31,50 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
-import org.junit.Ignore
 import org.junit.Test
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@Ignore("b/216286227")
 class MediaTttChipControllerSenderTest : SysuiTestCase() {
     private lateinit var controllerSender: MediaTttChipControllerSender
 
     @Mock
+    private lateinit var packageManager: PackageManager
+    @Mock
+    private lateinit var applicationInfo: ApplicationInfo
+    @Mock
     private lateinit var windowManager: WindowManager
     @Mock
     private lateinit var commandQueue: CommandQueue
     private lateinit var commandQueueCallback: CommandQueue.Callbacks
+    private lateinit var fakeAppIconDrawable: Drawable
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        controllerSender = MediaTttChipControllerSender(commandQueue, context, windowManager)
+
+        fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
+        whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+        whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
+        whenever(packageManager.getApplicationInfo(
+            eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
+        )).thenReturn(applicationInfo)
+        context.setMockPackageManager(packageManager)
+
+        controllerSender = MediaTttChipControllerSender(
+            commandQueue, context, windowManager, FakeExecutor(FakeSystemClock())
+        )
 
         val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
         verify(commandQueue).addCallback(callbackCaptor.capture())
@@ -192,9 +213,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -207,9 +227,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -222,9 +241,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -237,9 +255,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -252,9 +269,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -314,9 +330,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -376,9 +391,8 @@
         controllerSender.displayChip(state)
 
         val chipView = getChipView()
-        assertThat(chipView.getAppIconView().drawable).isEqualTo(state.getAppIcon(context))
-        assertThat(chipView.getAppIconView().contentDescription)
-                .isEqualTo(state.getAppName(context))
+        assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+        assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
         assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context))
         assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
         assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -451,15 +465,15 @@
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun almostCloseToStartCast() =
-        AlmostCloseToStartCast(PACKAGE_NAME, DEVICE_NAME)
+        AlmostCloseToStartCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun almostCloseToEndCast() =
-        AlmostCloseToEndCast(PACKAGE_NAME, DEVICE_NAME)
+        AlmostCloseToEndCast(PACKAGE_NAME, OTHER_DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToReceiverTriggered() =
-        TransferToReceiverTriggered(PACKAGE_NAME, DEVICE_NAME)
+        TransferToReceiverTriggered(PACKAGE_NAME, OTHER_DEVICE_NAME)
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToThisDeviceTriggered() =
@@ -468,23 +482,24 @@
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
         TransferToReceiverSucceeded(
-            PACKAGE_NAME, DEVICE_NAME, undoCallback
+            PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
         )
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
         TransferToThisDeviceSucceeded(
-            PACKAGE_NAME, DEVICE_NAME, undoCallback
+            PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
         )
 
     /** Helper method providing default parameters to not clutter up the tests. */
     private fun transferFailed() = TransferFailed(PACKAGE_NAME)
 }
 
-private const val DEVICE_NAME = "My Tablet"
+private const val APP_NAME = "Fake app name"
+private const val OTHER_DEVICE_NAME = "My Tablet"
 private const val PACKAGE_NAME = "com.android.systemui"
 
-private val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
+private val routeInfo = MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
     .addFeature("feature")
     .setPackageName(PACKAGE_NAME)
     .build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 511848d..3508226 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -24,7 +24,7 @@
 import android.content.pm.UserInfo
 import android.os.Process.SYSTEM_UID
 import android.os.UserHandle
-import android.permission.PermGroupUsage
+import android.permission.PermissionGroupUsage
 import android.permission.PermissionManager
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
@@ -68,7 +68,8 @@
         private const val ENT_USER_ID = 10
 
         private const val TEST_PACKAGE_NAME = "test package name"
-        private const val TEST_ATTRIBUTION = "test attribution"
+        private const val TEST_ATTRIBUTION_TAG = "test attribution tag"
+        private const val TEST_PROXY_LABEL = "test proxy label"
 
         private const val PERM_CAMERA = android.Manifest.permission_group.CAMERA
         private const val PERM_MICROPHONE = android.Manifest.permission_group.MICROPHONE
@@ -109,12 +110,12 @@
 
     private val dialogProvider = object : PrivacyDialogController.DialogProvider {
         var list: List<PrivacyDialog.PrivacyElement>? = null
-        var starter: ((String, Int) -> Unit)? = null
+        var starter: ((String, Int, CharSequence?, Intent?) -> Unit)? = null
 
         override fun makeDialog(
             context: Context,
             list: List<PrivacyDialog.PrivacyElement>,
-            starter: (String, Int) -> Unit
+            starter: (String, Int, CharSequence?, Intent?) -> Unit
         ): PrivacyDialog {
             this.list = list
             this.starter = starter
@@ -125,13 +126,11 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
         nextUid = 0
-
         setUpDefaultMockResponses()
 
         controller = PrivacyDialogController(
-            permissionManager,
+                permissionManager,
                 packageManager,
                 privacyItemController,
                 userTracker,
@@ -248,40 +247,54 @@
         val usage = createMockPermGroupUsage(
                 packageName = TEST_PACKAGE_NAME,
                 uid = generateUidForUser(USER_ID),
-                permGroupName = PERM_CAMERA,
-                lastAccess = 5L,
+                permissionGroupName = PERM_CAMERA,
+                lastAccessTimeMillis = 5L,
                 isActive = true,
                 isPhoneCall = false,
-                attribution = TEST_ATTRIBUTION
+                attributionTag = null,
+                proxyLabel = TEST_PROXY_LABEL
         )
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
 
         controller.showDialog(context)
         exhaustExecutors()
 
-        val expected = PrivacyDialog.PrivacyElement(
-                type = PrivacyType.TYPE_CAMERA,
-                packageName = TEST_PACKAGE_NAME,
-                userId = USER_ID,
-                applicationName = TEST_PACKAGE_NAME,
-                attribution = TEST_ATTRIBUTION,
-                lastActiveTimestamp = 5L,
-                active = true,
-                phoneCall = false,
-                enterprise = false
-        )
-        assertThat(dialogProvider.list).containsExactly(expected)
+        dialogProvider.list?.let { list ->
+            assertThat(list.get(0).type).isEqualTo(PrivacyType.TYPE_CAMERA)
+            assertThat(list.get(0).packageName).isEqualTo(TEST_PACKAGE_NAME)
+            assertThat(list.get(0).userId).isEqualTo(USER_ID)
+            assertThat(list.get(0).applicationName).isEqualTo(TEST_PACKAGE_NAME)
+            assertThat(list.get(0).attributionTag).isNull()
+            assertThat(list.get(0).attributionLabel).isNull()
+            assertThat(list.get(0).proxyLabel).isEqualTo(TEST_PROXY_LABEL)
+            assertThat(list.get(0).lastActiveTimestamp).isEqualTo(5L)
+            assertThat(list.get(0).active).isTrue()
+            assertThat(list.get(0).phoneCall).isFalse()
+            assertThat(list.get(0).enterprise).isFalse()
+            assertThat(list.get(0).permGroupName).isEqualTo(PERM_CAMERA)
+            assertThat(isIntentEqual(list.get(0).navigationIntent!!,
+                    controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
+                    .isTrue()
+        }
+    }
+
+    private fun isIntentEqual(actual: Intent, expected: Intent): Boolean {
+        return actual.action == expected.action &&
+                actual.getStringExtra(Intent.EXTRA_PACKAGE_NAME) ==
+                expected.getStringExtra(Intent.EXTRA_PACKAGE_NAME) &&
+                actual.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle ==
+                expected.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle
     }
 
     @Test
     fun testTwoElementsDifferentType_sorted() {
         val usage_camera = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_camera",
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_microphone",
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_microphone, usage_camera)
@@ -322,12 +335,12 @@
         val usage_active = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_active",
                 isActive = true,
-                lastAccess = 0L
+                lastAccessTimeMillis = 0L
         )
         val usage_active_moreRecent = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_active_recent",
                 isActive = true,
-                lastAccess = 1L
+                lastAccessTimeMillis = 1L
         )
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_active, usage_active_moreRecent)
@@ -344,17 +357,17 @@
         val usage_recent = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_recent",
                 isActive = false,
-                lastAccess = 0L
+                lastAccessTimeMillis = 0L
         )
         val usage_moreRecent = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_moreRecent",
                 isActive = false,
-                lastAccess = 1L
+                lastAccessTimeMillis = 1L
         )
         val usage_mostRecent = createMockPermGroupUsage(
                 packageName = "${TEST_PACKAGE_NAME}_mostRecent",
                 isActive = false,
-                lastAccess = 2L
+                lastAccessTimeMillis = 2L
         )
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
                 listOf(usage_recent, usage_mostRecent, usage_moreRecent)
@@ -370,13 +383,13 @@
     @Test
     fun testMicAndCameraDisabled() {
         val usage_camera = createMockPermGroupUsage(
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         val usage_location = createMockPermGroupUsage(
-                permGroupName = PERM_LOCATION
+                permissionGroupName = PERM_LOCATION
         )
 
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -394,13 +407,13 @@
     @Test
     fun testLocationDisabled() {
         val usage_camera = createMockPermGroupUsage(
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         val usage_location = createMockPermGroupUsage(
-                permGroupName = PERM_LOCATION
+                permissionGroupName = PERM_LOCATION
         )
 
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -420,13 +433,13 @@
     @Test
     fun testAllIndicatorsAvailable() {
         val usage_camera = createMockPermGroupUsage(
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         val usage_location = createMockPermGroupUsage(
-                permGroupName = PERM_LOCATION
+                permissionGroupName = PERM_LOCATION
         )
 
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -444,13 +457,13 @@
     @Test
     fun testNoIndicatorsAvailable() {
         val usage_camera = createMockPermGroupUsage(
-                permGroupName = PERM_CAMERA
+                permissionGroupName = PERM_CAMERA
         )
         val usage_microphone = createMockPermGroupUsage(
-                permGroupName = PERM_MICROPHONE
+                permissionGroupName = PERM_MICROPHONE
         )
         val usage_location = createMockPermGroupUsage(
-                permGroupName = PERM_LOCATION
+                permissionGroupName = PERM_LOCATION
         )
 
         `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(
@@ -500,7 +513,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
         verify(activityStarter)
                 .startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
 
@@ -518,7 +531,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, ENT_USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, ENT_USER_ID, null, null)
         verify(activityStarter)
                 .startActivity(capture(intentCaptor), eq(true), any<ActivityStarter.Callback>())
 
@@ -533,7 +546,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
         verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
 
         activityStartedCaptor.value.onActivityStarted(ActivityManager.START_DELIVERED_TO_TOP)
@@ -548,7 +561,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
         verify(activityStarter).startActivity(any(), eq(true), capture(activityStartedCaptor))
 
         activityStartedCaptor.value.onActivityStarted(ActivityManager.START_ABORTED)
@@ -578,7 +591,7 @@
         controller.showDialog(context)
         exhaustExecutors()
 
-        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+        dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID, null, null)
         verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
                 USER_ID, TEST_PACKAGE_NAME)
     }
@@ -599,6 +612,42 @@
         verify(uiEventLogger, times(1)).log(PrivacyDialogEvent.PRIVACY_DIALOG_DISMISSED)
     }
 
+    @Test
+    fun testInvalidAttributionTag() {
+        val usage = createMockPermGroupUsage(
+                packageName = TEST_PACKAGE_NAME,
+                uid = generateUidForUser(USER_ID),
+                permissionGroupName = PERM_CAMERA,
+                lastAccessTimeMillis = 5L,
+                isActive = true,
+                isPhoneCall = false,
+                attributionTag = "INVALID_ATTRIBUTION_TAG",
+                proxyLabel = TEST_PROXY_LABEL
+        )
+        `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
+
+        controller.showDialog(context)
+        exhaustExecutors()
+
+        dialogProvider.list?.let { list ->
+            assertThat(list.get(0).type).isEqualTo(PrivacyType.TYPE_CAMERA)
+            assertThat(list.get(0).packageName).isEqualTo(TEST_PACKAGE_NAME)
+            assertThat(list.get(0).userId).isEqualTo(USER_ID)
+            assertThat(list.get(0).applicationName).isEqualTo(TEST_PACKAGE_NAME)
+            assertThat(list.get(0).attributionTag).isEqualTo("INVALID_ATTRIBUTION_TAG")
+            assertThat(list.get(0).attributionLabel).isNull()
+            assertThat(list.get(0).proxyLabel).isEqualTo(TEST_PROXY_LABEL)
+            assertThat(list.get(0).lastActiveTimestamp).isEqualTo(5L)
+            assertThat(list.get(0).active).isTrue()
+            assertThat(list.get(0).phoneCall).isFalse()
+            assertThat(list.get(0).enterprise).isFalse()
+            assertThat(list.get(0).permGroupName).isEqualTo(PERM_CAMERA)
+            assertThat(isIntentEqual(list.get(0).navigationIntent!!,
+                    controller.getDefaultManageAppPermissionsIntent(TEST_PACKAGE_NAME, USER_ID)))
+                    .isTrue()
+        }
+    }
+
     private fun exhaustExecutors() {
         FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
     }
@@ -608,9 +657,7 @@
         `when`(appOpsController.isMicMuted).thenReturn(false)
 
         `when`(packageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
-                .thenAnswer {
-                    FakeApplicationInfo(it.getArgument(0))
-                }
+                .thenAnswer { FakeApplicationInfo(it.getArgument(0)) }
 
         `when`(privacyItemController.locationAvailable).thenReturn(true)
         `when`(privacyItemController.micCameraAvailable).thenReturn(true)
@@ -636,21 +683,24 @@
     private fun createMockPermGroupUsage(
         packageName: String = TEST_PACKAGE_NAME,
         uid: Int = generateUidForUser(USER_ID),
-        permGroupName: String = PERM_CAMERA,
-        lastAccess: Long = 0L,
+        permissionGroupName: String = PERM_CAMERA,
+        lastAccessTimeMillis: Long = 0L,
         isActive: Boolean = false,
         isPhoneCall: Boolean = false,
-        attribution: CharSequence? = null
-    ): PermGroupUsage {
-        val usage = mock(PermGroupUsage::class.java)
+        attributionTag: CharSequence? = null,
+        attributionLabel: CharSequence? = null,
+        proxyLabel: CharSequence? = null
+    ): PermissionGroupUsage {
+        val usage = mock(PermissionGroupUsage::class.java)
         `when`(usage.packageName).thenReturn(packageName)
         `when`(usage.uid).thenReturn(uid)
-        `when`(usage.permGroupName).thenReturn(permGroupName)
-        `when`(usage.lastAccess).thenReturn(lastAccess)
+        `when`(usage.permissionGroupName).thenReturn(permissionGroupName)
+        `when`(usage.lastAccessTimeMillis).thenReturn(lastAccessTimeMillis)
         `when`(usage.isActive).thenReturn(isActive)
         `when`(usage.isPhoneCall).thenReturn(isPhoneCall)
-        `when`(usage.attribution).thenReturn(attribution)
-
+        `when`(usage.attributionTag).thenReturn(attributionTag)
+        `when`(usage.attributionLabel).thenReturn(attributionLabel)
+        `when`(usage.proxyLabel).thenReturn(proxyLabel)
         return usage
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
index 1baaf6b..c714fa0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
@@ -34,6 +34,7 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import android.content.Intent
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -43,11 +44,11 @@
     companion object {
         private const val TEST_PACKAGE_NAME = "test_pkg"
         private const val TEST_USER_ID = 0
+        private const val TEST_PERM_GROUP = "test_perm_group"
     }
 
     @Mock
-    private lateinit var starter: (String, Int) -> Unit
-
+    private lateinit var starter: (String, Int, CharSequence?, Intent?) -> Unit
     private lateinit var dialog: PrivacyDialog
 
     @Before
@@ -71,16 +72,20 @@
                         TEST_USER_ID,
                         "App",
                         null,
+                        null,
+                        null,
                         0L,
                         false,
                         false,
-                        false
+                        false,
+                        TEST_PERM_GROUP,
+                        null
                 )
         )
         dialog = PrivacyDialog(context, list, starter)
         dialog.show()
         dialog.requireViewById<View>(R.id.privacy_item).callOnClick()
-        verify(starter).invoke(TEST_PACKAGE_NAME, TEST_USER_ID)
+        verify(starter).invoke(TEST_PACKAGE_NAME, TEST_USER_ID, null, null)
     }
 
     @Test
@@ -115,10 +120,14 @@
                         TEST_USER_ID,
                         "App",
                         null,
+                        null,
+                        null,
                         0L,
                         true,
                         false,
-                        false
+                        false,
+                        TEST_PERM_GROUP,
+                        null
                 ),
                 PrivacyDialog.PrivacyElement(
                         PrivacyType.TYPE_MICROPHONE,
@@ -126,10 +135,14 @@
                         TEST_USER_ID,
                         "App",
                         null,
+                        null,
+                        null,
                         0L,
                         false,
                         false,
-                        false
+                        false,
+                        TEST_PERM_GROUP,
+                        null
                 )
         )
         dialog = PrivacyDialog(context, list, starter)
@@ -145,10 +158,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 true,
                 false,
-                false
+                false,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -171,10 +188,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 false,
                 false,
-                false
+                false,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -197,10 +218,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 false,
                 true,
-                false
+                false,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -219,10 +244,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 false,
                 false,
-                true
+                true,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -241,10 +270,14 @@
                 TEST_USER_ID,
                 "App",
                 null,
+                null,
+                null,
                 0L,
                 false,
                 false,
-                true
+                true,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -255,17 +288,21 @@
     }
 
     @Test
-    fun testAttribution() {
+    fun testProxyLabel() {
         val element = PrivacyDialog.PrivacyElement(
                 PrivacyType.TYPE_MICROPHONE,
                 TEST_PACKAGE_NAME,
                 TEST_USER_ID,
                 "App",
-                "attribution",
+                null,
+                null,
+                "proxyLabel",
                 0L,
                 false,
                 false,
-                true
+                true,
+                TEST_PERM_GROUP,
+                null
         )
 
         val list = listOf(element)
@@ -274,7 +311,65 @@
         assertThat(dialog.requireViewById<TextView>(R.id.text).text.toString()).contains(
                 context.getString(
                         R.string.ongoing_privacy_dialog_attribution_text,
-                        element.attribution
+                        element.proxyLabel
+                )
+        )
+    }
+
+    @Test
+    fun testSubattribution() {
+        val element = PrivacyDialog.PrivacyElement(
+                PrivacyType.TYPE_MICROPHONE,
+                TEST_PACKAGE_NAME,
+                TEST_USER_ID,
+                "App",
+                null,
+                "For subattribution",
+                null,
+                0L,
+                true,
+                false,
+                false,
+                TEST_PERM_GROUP,
+                null
+        )
+
+        val list = listOf(element)
+        dialog = PrivacyDialog(context, list, starter)
+        dialog.show()
+        assertThat(dialog.requireViewById<TextView>(R.id.text).text.toString()).contains(
+                context.getString(
+                        R.string.ongoing_privacy_dialog_attribution_label,
+                        element.attributionLabel
+                )
+        )
+    }
+
+    @Test
+    fun testSubattributionAndProxyLabel() {
+        val element = PrivacyDialog.PrivacyElement(
+                PrivacyType.TYPE_MICROPHONE,
+                TEST_PACKAGE_NAME,
+                TEST_USER_ID,
+                "App",
+                null,
+                "For subattribution",
+                "proxy label",
+                0L,
+                true,
+                false,
+                false,
+                TEST_PERM_GROUP,
+                null
+        )
+
+        val list = listOf(element)
+        dialog = PrivacyDialog(context, list, starter)
+        dialog.show()
+        assertThat(dialog.requireViewById<TextView>(R.id.text).text.toString()).contains(
+                context.getString(
+                        R.string.ongoing_privacy_dialog_attribution_proxy_label,
+                        element.attributionLabel, element.proxyLabel
                 )
         )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
index 92743ae5..30a5308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
@@ -1,31 +1,45 @@
 package com.android.systemui.qs
 
 import android.content.Context
+import android.permission.PermissionManager
+import android.provider.DeviceConfig
 import android.testing.AndroidTestingRunner
 import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.privacy.OngoingPrivacyChip
 import com.android.systemui.privacy.PrivacyDialogController
 import com.android.systemui.privacy.PrivacyItemController
 import com.android.systemui.privacy.logging.PrivacyLogger
 import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
 
+    companion object {
+        const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+    }
+
     @Mock
     private lateinit var privacyItemController: PrivacyItemController
     @Mock
@@ -38,11 +52,20 @@
     private lateinit var privacyLogger: PrivacyLogger
     @Mock
     private lateinit var iconContainer: StatusIconContainer
+    @Mock
+    private lateinit var permissionManager: PermissionManager
+    @Mock
+    private lateinit var backgroundExecutor: Executor
+    @Mock
+    private lateinit var activityStarter: ActivityStarter
+    @Mock
+    private lateinit var appOpsController: AppOpsController
 
+    private val uiExecutor = FakeExecutor(FakeSystemClock())
+    private lateinit var deviceConfigProxy: DeviceConfigProxy
     private lateinit var cameraSlotName: String
     private lateinit var microphoneSlotName: String
     private lateinit var locationSlotName: String
-
     private lateinit var controller: HeaderPrivacyIconsController
 
     @Before
@@ -54,6 +77,7 @@
         cameraSlotName = context.getString(com.android.internal.R.string.status_bar_camera)
         microphoneSlotName = context.getString(com.android.internal.R.string.status_bar_microphone)
         locationSlotName = context.getString(com.android.internal.R.string.status_bar_location)
+        deviceConfigProxy = DeviceConfigProxyFake()
 
         controller = HeaderPrivacyIconsController(
                 privacyItemController,
@@ -61,7 +85,13 @@
                 privacyChip,
                 privacyDialogController,
                 privacyLogger,
-                iconContainer
+                iconContainer,
+                permissionManager,
+                backgroundExecutor,
+                uiExecutor,
+                activityStarter,
+                appOpsController,
+                deviceConfigProxy
         )
     }
 
@@ -111,18 +141,38 @@
 
     @Test
     fun testPrivacyChipClicked() {
+        changeProperty(SAFETY_CENTER_ENABLED, false)
         controller.onParentVisible()
 
         val captor = argumentCaptor<View.OnClickListener>()
         verify(privacyChip).setOnClickListener(capture(captor))
-
         captor.value.onClick(privacyChip)
 
         verify(privacyDialogController).showDialog(any(Context::class.java))
     }
 
+    @Test
+    fun testSafetyCenterFlag() {
+        changeProperty(SAFETY_CENTER_ENABLED, true)
+        controller.onParentVisible()
+        val captor = argumentCaptor<View.OnClickListener>()
+        verify(privacyChip).setOnClickListener(capture(captor))
+        captor.value.onClick(privacyChip)
+        verify(privacyDialogController, never()).showDialog(any(Context::class.java))
+    }
+
     private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
         whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
         whenever(privacyItemController.locationAvailable).thenReturn(location)
     }
+
+    private fun changeProperty(name: String, value: Boolean?) {
+        deviceConfigProxy.setProperty(
+            DeviceConfig.NAMESPACE_PRIVACY,
+            name,
+            value?.toString(),
+            false
+        )
+        uiExecutor.runAllReady()
+    }
 }
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index e122993..75d9b7e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -275,12 +275,10 @@
             if (index >= 0) {
                 RemoteViews presentation = dataset.getFieldDialogPresentation(index);
                 if (presentation == null) {
-                    Slog.w(TAG, "fallback to presentation");
-                    presentation = dataset.getFieldPresentation(index);
-                }
-                if (presentation == null) {
-                    Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
-                            + "service didn't provide a presentation for it on " + dataset);
+                    if (sDebug) {
+                        Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
+                                + "service didn't provide a presentation for it on " + dataset);
+                    }
                     continue;
                 }
                 final View view;
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index b026990..33301b1 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -21,8 +21,6 @@
 import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
 import static android.bluetooth.BluetoothAdapter.EXTRA_PREVIOUS_STATE;
 import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
-import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
-import static android.bluetooth.BluetoothAdapter.STATE_ON;
 import static android.bluetooth.BluetoothAdapter.nameForState;
 import static android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED;
 import static android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED;
@@ -230,18 +228,24 @@
 
         if (DEBUG) Log.i(TAG, "stopScan()");
         if (!mScanning) {
-            Log.d(TAG, "  > not scanning.");
+            if (DEBUG) Log.d(TAG, "  > not scanning.");
             return;
         }
+        // mScanCallback is non-null here - it cannot be null when mScanning is true.
+
+        // BluetoothLeScanner will throw an IllegalStateException if stopScan() is called while LE
+        // is not enabled.
+        if (mBtAdapter.isLeEnabled()) {
+            try {
+                mBleScanner.stopScan(mScanCallback);
+            } catch (RuntimeException e) {
+                // Just to be sure not to crash system server here if BluetoothLeScanner throws
+                // another RuntimeException.
+                Slog.w(TAG, "Exception while stopping BLE scanning", e);
+            }
+        }
 
         mScanning = false;
-
-        if (mBtAdapter.getState() != STATE_ON && mBtAdapter.getState() != STATE_BLE_ON) {
-            Log.d(TAG, "BT Adapter is not turned ON");
-            return;
-        }
-
-        mBleScanner.stopScan(mScanCallback);
     }
 
     @MainThread
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index e335a16..09ef03c 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -52,6 +52,7 @@
 import com.android.server.pm.pkg.SharedUserApi;
 import com.android.server.pm.pkg.component.ParsedMainComponent;
 import com.android.server.pm.pkg.mutate.PackageStateMutator;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 
 import java.io.IOException;
 import java.lang.annotation.Retention;
@@ -718,7 +719,8 @@
     /**
      * Returns a package object for the disabled system package name.
      */
-    public abstract @Nullable PackageSetting getDisabledSystemPackage(@NonNull String packageName);
+    public abstract @Nullable PackageStateInternal getDisabledSystemPackage(
+            @NonNull String packageName);
 
     /**
      * Returns the package name for the disabled system package.
@@ -1334,4 +1336,12 @@
     public abstract PackageStateMutator.Result commitPackageStateMutation(
             @Nullable PackageStateMutator.InitialState state,
             @NonNull Consumer<PackageStateMutator> consumer);
+
+    /**
+     * @return package data snapshot for use with other PackageManager infrastructure. This should
+     * only be used as a parameter passed to another PM related class. Do not call methods on this
+     * directly.
+     */
+    @NonNull
+    public abstract PackageDataSnapshot snapshot();
 }
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index aae1cc0..c375c73 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -17,6 +17,8 @@
 package com.android.server;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
@@ -32,6 +34,9 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.FastPrintWriter;
+import com.android.server.pm.Computer;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -51,7 +56,7 @@
     final private static boolean localLOGV = DEBUG || false;
     final private static boolean localVerificationLOGV = DEBUG || false;
 
-    public void addFilter(F f) {
+    public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
         IntentFilter intentFilter = getIntentFilter(f);
         if (localLOGV) {
             Slog.v(TAG, "Adding filter: " + f);
@@ -420,8 +425,9 @@
         return Collections.unmodifiableSet(mFilters);
     }
 
-    public List<R> queryIntentFromList(Intent intent, String resolvedType, boolean defaultOnly,
-            ArrayList<F[]> listCut, int userId) {
+    public List<R> queryIntentFromList(@NonNull Computer computer, Intent intent,
+            String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId,
+            long customFlags) {
         ArrayList<R> resultList = new ArrayList<R>();
 
         final boolean debug = localLOGV ||
@@ -431,16 +437,21 @@
         final String scheme = intent.getScheme();
         int N = listCut.size();
         for (int i = 0; i < N; ++i) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme,
-                    listCut.get(i), resultList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme,
+                    listCut.get(i), resultList, userId, customFlags);
         }
         filterResults(resultList);
         sortResults(resultList);
         return resultList;
     }
 
-    public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
-            int userId) {
+    public List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+            String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+        return queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, 0);
+    }
+
+    protected final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+            String resolvedType, boolean defaultOnly, @UserIdInt int userId, long customFlags) {
         String scheme = intent.getScheme();
 
         ArrayList<R> finalList = new ArrayList<R>();
@@ -512,21 +523,22 @@
         }
 
         FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
+        Computer computer = (Computer) snapshot;
         if (firstTypeCut != null) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
-                    scheme, firstTypeCut, finalList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+                    scheme, firstTypeCut, finalList, userId, customFlags);
         }
         if (secondTypeCut != null) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
-                    scheme, secondTypeCut, finalList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+                    scheme, secondTypeCut, finalList, userId, customFlags);
         }
         if (thirdTypeCut != null) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
-                    scheme, thirdTypeCut, finalList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+                    scheme, thirdTypeCut, finalList, userId, customFlags);
         }
         if (schemeCut != null) {
-            buildResolveList(intent, categories, debug, defaultOnly, resolvedType,
-                    scheme, schemeCut, finalList, userId);
+            buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType,
+                    scheme, schemeCut, finalList, userId, customFlags);
         }
         filterResults(finalList);
         sortResults(finalList);
@@ -554,7 +566,7 @@
      * "stopped", that is whether it should not be included in the result
      * if the intent requests to excluded stopped objects.
      */
-    protected boolean isFilterStopped(F filter, int userId) {
+    protected boolean isFilterStopped(PackageStateInternal packageState, @UserIdInt int userId) {
         return false;
     }
 
@@ -584,7 +596,8 @@
     protected abstract F[] newArray(int size);
 
     @SuppressWarnings("unchecked")
-    protected R newResult(F filter, int match, int userId) {
+    protected R newResult(@NonNull Computer computer, F filter, int match, int userId,
+            long customFlags) {
         return (R)filter;
     }
 
@@ -764,9 +777,10 @@
         return new FastImmutableArraySet<String>(categories.toArray(new String[categories.size()]));
     }
 
-    private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
-            boolean debug, boolean defaultOnly, String resolvedType, String scheme,
-            F[] src, List<R> dest, int userId) {
+    private void buildResolveList(@NonNull Computer computer, Intent intent,
+            FastImmutableArraySet<String> categories, boolean debug, boolean defaultOnly,
+            String resolvedType, String scheme, F[] src, List<R> dest, int userId,
+            long customFlags) {
         final String action = intent.getAction();
         final Uri data = intent.getData();
         final String packageName = intent.getPackage();
@@ -791,7 +805,8 @@
             int match;
             if (debug) Slog.v(TAG, "Matching against filter " + filter);
 
-            if (excludingStopped && isFilterStopped(filter, userId)) {
+            if (excludingStopped && isFilterStopped(computer.getPackageStateInternal(packageName),
+                    userId)) {
                 if (debug) {
                     Slog.v(TAG, "  Filter's target is stopped; skipping");
                 }
@@ -833,7 +848,7 @@
                         Integer.toHexString(match) + " hasDefault="
                         + intentFilter.hasCategory(Intent.CATEGORY_DEFAULT));
                 if (!defaultOnly || intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)) {
-                    final R oneResult = newResult(filter, match, userId);
+                    final R oneResult = newResult(computer, filter, match, userId, customFlags);
                     if (debug) Slog.v(TAG, "    Created result: " + oneResult);
                     if (oneResult != null) {
                         dest.add(oneResult);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6c39a11..6813f3f8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24,6 +24,7 @@
 import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
 import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
+import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE;
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
@@ -395,12 +396,14 @@
 import com.android.server.graphics.fonts.FontManagerInternal;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.os.NativeTombstoneManager;
+import com.android.server.pm.Computer;
 import com.android.server.pm.Installer;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.SELinuxUtil;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.uri.GrantUri;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -1105,10 +1108,11 @@
         }
 
         @Override
-        protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
+        protected BroadcastFilter newResult(@NonNull Computer computer, BroadcastFilter filter,
+                int match, int userId, long customFlags) {
             if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
                     || userId == filter.owningUserId) {
-                return super.newResult(filter, match, userId);
+                return super.newResult(computer, filter, match, userId, customFlags);
             }
             return null;
         }
@@ -13039,7 +13043,7 @@
                 if (!bf.debugCheck()) {
                     Slog.w(TAG, "==> For Dynamic broadcast");
                 }
-                mReceiverResolver.addFilter(bf);
+                mReceiverResolver.addFilter(getPackageManagerInternal().snapshot(), bf);
             }
 
             // Enqueue broadcasts for all existing stickies that match
@@ -13861,6 +13865,7 @@
                     intent, resolvedType, callingUid, users, broadcastAllowList);
         }
         if (intent.getComponent() == null) {
+            final PackageDataSnapshot snapshot = getPackageManagerInternal().snapshot();
             if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
                 // Query one target user at a time, excluding shell-restricted users
                 for (int i = 0; i < users.length; i++) {
@@ -13869,7 +13874,7 @@
                         continue;
                     }
                     List<BroadcastFilter> registeredReceiversForUser =
-                            mReceiverResolver.queryIntent(intent,
+                            mReceiverResolver.queryIntent(snapshot, intent,
                                     resolvedType, false /*defaultOnly*/, users[i]);
                     if (registeredReceivers == null) {
                         registeredReceivers = registeredReceiversForUser;
@@ -13878,7 +13883,7 @@
                     }
                 }
             } else {
-                registeredReceivers = mReceiverResolver.queryIntent(intent,
+                registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
                         resolvedType, false /*defaultOnly*/, userId);
             }
         }
@@ -14339,14 +14344,18 @@
                 return false;
             }
 
-            if (!Build.IS_DEBUGGABLE) {
-                int match = mContext.getPackageManager().checkSignatures(
-                        ii.targetPackage, ii.packageName);
-                if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
+            int match = mContext.getPackageManager().checkSignatures(
+                    ii.targetPackage, ii.packageName);
+            if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) {
+                if (Build.IS_DEBUGGABLE && (flags & INSTR_FLAG_ALWAYS_CHECK_SIGNATURE) == 0) {
+                    Slog.w(TAG, "Instrumentation test " + ii.packageName
+                            + " doesn't have a signature matching the target " + ii.targetPackage
+                            + ", which would not be allowed on the production Android builds");
+                } else {
                     String msg = "Permission Denial: starting instrumentation "
                             + className + " from pid="
                             + Binder.getCallingPid()
-                            + ", uid=" + Binder.getCallingPid()
+                            + ", uid=" + Binder.getCallingUid()
                             + " not allowed because package " + ii.packageName
                             + " does not have a signature matching the target "
                             + ii.targetPackage;
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index bcde343..28b807c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -34,6 +34,7 @@
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_COUNT;
 import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
 
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
@@ -3349,7 +3350,11 @@
             pw.println("  --checkin: output checkin format, resetting data.");
             pw.println("  --C: output checkin format, not resetting data.");
             pw.println("  --proto: output dump in protocol buffer format.");
-            pw.println("  --autofill: dump just the autofill-related state of an activity");
+            pw.printf("  %s: dump just the DUMPABLE-related state of an activity. Use the %s "
+                    + "option to list the supported DUMPABLEs\n", Activity.DUMP_ARG_DUMP_DUMPABLE,
+                    Activity.DUMP_ARG_LIST_DUMPABLES);
+            pw.printf("  %s: show the available dumpables in an activity\n",
+                    Activity.DUMP_ARG_LIST_DUMPABLES);
         } else {
             pw.println("Activity manager (activity) commands:");
             pw.println("  help");
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index d233c5e..2e80efb 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -255,10 +255,9 @@
         public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo,
                 SurfaceControl.DynamicDisplayInfo dynamicInfo,
                 SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
-            boolean changed =
-                    updateSystemPreferredDisplayMode(dynamicInfo.preferredBootDisplayMode);
-            changed |= updateDisplayModesLocked(
-                    dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs);
+            boolean changed = updateDisplayModesLocked(
+                    dynamicInfo.supportedDisplayModes, dynamicInfo.preferredBootDisplayMode,
+                    dynamicInfo.activeDisplayModeId, modeSpecs);
             changed |= updateStaticInfo(staticInfo);
             changed |= updateColorModesLocked(dynamicInfo.supportedColorModes,
                     dynamicInfo.activeColorMode);
@@ -273,10 +272,12 @@
         }
 
         public boolean updateDisplayModesLocked(
-                SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId,
-                SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
+                SurfaceControl.DisplayMode[] displayModes, int preferredSfDisplayModeId,
+                int activeSfDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
             mSfDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
-            mActiveSfDisplayMode = getModeById(displayModes, activeDisplayModeId);
+            mActiveSfDisplayMode = getModeById(displayModes, activeSfDisplayModeId);
+            SurfaceControl.DisplayMode preferredSfDisplayMode =
+                        getModeById(displayModes, preferredSfDisplayModeId);
 
             // Build an updated list of all existing modes.
             ArrayList<DisplayModeRecord> records = new ArrayList<>();
@@ -335,6 +336,27 @@
                 }
             }
 
+            boolean preferredModeChanged = false;
+
+            if (preferredSfDisplayModeId != INVALID_MODE_ID && preferredSfDisplayMode != null) {
+                DisplayModeRecord preferredRecord = null;
+                for (DisplayModeRecord record : records) {
+                    if (record.hasMatchingMode(preferredSfDisplayMode)) {
+                        preferredRecord = record;
+                        break;
+                    }
+                }
+
+                if (preferredRecord != null) {
+                    int preferredModeId = preferredRecord.mMode.getModeId();
+                    if (mSurfaceControlProxy.getBootDisplayModeSupport()
+                            && mSystemPreferredModeId != preferredModeId) {
+                        mSystemPreferredModeId = preferredModeId;
+                        preferredModeChanged = true;
+                    }
+                }
+            }
+
             boolean activeModeChanged = false;
 
             // Check whether SurfaceFlinger or the display device changed the active mode out from
@@ -373,7 +395,7 @@
             boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded;
             // If the records haven't changed then we're done here.
             if (!recordsChanged) {
-                return activeModeChanged;
+                return activeModeChanged || preferredModeChanged;
             }
 
             mSupportedModes.clear();
@@ -386,14 +408,14 @@
                 mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID
                         ? mSystemPreferredModeId : activeRecord.mMode.getModeId();
                 mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID
-                        ? getModeById(mSfDisplayModes, mSystemPreferredModeId).group
+                        ? preferredSfDisplayMode.group
                         : mActiveSfDisplayMode.group;
             } else if (modesAdded && activeModeChanged) {
                 Slog.d(TAG, "New display modes are added and the active mode has changed, "
                         + "use active mode as default mode.");
                 mDefaultModeId = activeRecord.mMode.getModeId();
                 mDefaultModeGroup = mActiveSfDisplayMode.group;
-            } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
+            } else if (findSfDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
                 Slog.w(TAG, "Default display mode no longer available, using currently"
                         + " active mode as default.");
                 mDefaultModeId = activeRecord.mMode.getModeId();
@@ -545,15 +567,6 @@
             return true;
         }
 
-        private boolean updateSystemPreferredDisplayMode(int modeId) {
-            if (!mSurfaceControlProxy.getBootDisplayModeSupport()
-                    || mSystemPreferredModeId == modeId) {
-                return false;
-            }
-            mSystemPreferredModeId = modeId;
-            return true;
-        }
-
         private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes,
                 int modeId) {
             for (SurfaceControl.DisplayMode mode : supportedModes) {
@@ -887,8 +900,10 @@
             if (mUserPreferredMode == null) {
                 mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked());
             } else {
+                int preferredSfDisplayModeId = findSfDisplayModeIdLocked(
+                        mUserPreferredMode.getModeId(), mDefaultModeGroup);
                 mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(),
-                        mUserPreferredMode.getModeId());
+                        preferredSfDisplayModeId);
             }
         }
 
@@ -923,9 +938,9 @@
             // mode based on vendor requirements.
             // Note: We prefer the default mode group over the current one as this is the mode
             // group the vendor prefers.
-            int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId,
+            int baseSfModeId = findSfDisplayModeIdLocked(displayModeSpecs.baseModeId,
                     mDefaultModeGroup);
-            if (baseModeId < 0) {
+            if (baseSfModeId < 0) {
                 // When a display is hotplugged, it's possible for a mode to be removed that was
                 // previously valid. Because of the way display changes are propagated through the
                 // framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -943,7 +958,7 @@
                 getHandler().sendMessage(PooledLambda.obtainMessage(
                         LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
                         getDisplayTokenLocked(),
-                        new SurfaceControl.DesiredDisplayModeSpecs(baseModeId,
+                        new SurfaceControl.DesiredDisplayModeSpecs(baseSfModeId,
                                 mDisplayModeSpecs.allowGroupSwitching,
                                 mDisplayModeSpecs.primaryRefreshRateRange.min,
                                 mDisplayModeSpecs.primaryRefreshRateRange.max,
@@ -1090,14 +1105,14 @@
             pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
         }
 
-        private int findDisplayModeIdLocked(int modeId, int modeGroup) {
-            int matchingModeId = INVALID_MODE_ID;
-            DisplayModeRecord record = mSupportedModes.get(modeId);
+        private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) {
+            int matchingSfDisplayModeId = INVALID_MODE_ID;
+            DisplayModeRecord record = mSupportedModes.get(displayModeId);
             if (record != null) {
                 for (SurfaceControl.DisplayMode mode : mSfDisplayModes) {
                     if (record.hasMatchingMode(mode)) {
-                        if (matchingModeId == INVALID_MODE_ID) {
-                            matchingModeId = mode.id;
+                        if (matchingSfDisplayModeId == INVALID_MODE_ID) {
+                            matchingSfDisplayModeId = mode.id;
                         }
 
                         // Prefer to return a mode that matches the modeGroup
@@ -1107,7 +1122,7 @@
                     }
                 }
             }
-            return matchingModeId;
+            return matchingSfDisplayModeId;
         }
 
         // Returns a mode with id = modeId.
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index 1139d28..bb8a744 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -24,6 +24,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.Handler;
@@ -38,6 +39,8 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.EventLogTags;
 import com.android.server.IntentResolver;
+import com.android.server.LocalServices;
+import com.android.server.pm.Computer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -75,6 +78,9 @@
 
     private final RuleObserver mObserver;
 
+    @NonNull
+    private PackageManagerInternal mPackageManager;
+
     private FirewallIntentResolver mActivityResolver = new FirewallIntentResolver();
     private FirewallIntentResolver mBroadcastResolver = new FirewallIntentResolver();
     private FirewallIntentResolver mServiceResolver = new FirewallIntentResolver();
@@ -123,6 +129,13 @@
         mObserver.startWatching();
     }
 
+    private PackageManagerInternal getPackageManager() {
+        if (mPackageManager == null) {
+            mPackageManager = LocalServices.getService(PackageManagerInternal.class);
+        }
+        return mPackageManager;
+    }
+
     /**
      * This is called from ActivityManager to check if a start activity intent should be allowed.
      * It is assumed the caller is already holding the global ActivityManagerService lock.
@@ -154,7 +167,8 @@
         // For the first pass, find all the rules that have at least one intent-filter or
         // component-filter that matches this intent
         List<Rule> candidateRules;
-        candidateRules = resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, 0);
+        candidateRules = resolver.queryIntent(getPackageManager().snapshot(), intent, resolvedType,
+                false /*defaultOnly*/, 0);
         if (candidateRules == null) {
             candidateRules = new ArrayList<Rule>();
         }
@@ -375,7 +389,7 @@
             for (int ruleIndex=0; ruleIndex<rules.size(); ruleIndex++) {
                 Rule rule = rules.get(ruleIndex);
                 for (int i=0; i<rule.getIntentFilterCount(); i++) {
-                    resolver.addFilter(rule.getIntentFilter(i));
+                    resolver.addFilter(null, rule.getIntentFilter(i));
                 }
                 for (int i=0; i<rule.getComponentFilterCount(); i++) {
                     resolver.addComponentFilter(rule.getComponentFilter(i), rule);
@@ -512,7 +526,8 @@
         }
 
         @Override
-        protected Rule newResult(FirewallIntentFilter filter, int match, int userId) {
+        protected Rule newResult(@NonNull Computer computer, FirewallIntentFilter filter,
+                int match, int userId, long customFlags) {
             return filter.rule;
         }
 
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index 0d9ccd2..2a4882a 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -51,6 +51,8 @@
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.pm.resolution.ComponentResolverApi;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
 
@@ -98,7 +100,7 @@
  * {@link ComputerEngine} and {@link ComputerLocked}.
  */
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-public interface Computer {
+public interface Computer extends PackageDataSnapshot {
 
     /**
      * Every method must be annotated.
@@ -178,6 +180,18 @@
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
     List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
             String resolvedType, int userId);
+
+    /**
+     * Filters out ephemeral activities.
+     * <p>When resolving for an ephemeral app, only activities that 1) are defined in the
+     * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
+     *
+     * @param resolveInfos The pre-filtered list of resolved activities
+     * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
+     *          is performed.
+     * @param intent
+     * @return A filtered list of resolved activities.
+     */
     @Computer.LiveImplementation(override = Computer.LiveImplementation.NOT_ALLOWED)
     List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
             String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
@@ -648,4 +662,16 @@
     @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
     @NonNull
     ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId);
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @NonNull
+    ComponentResolverApi getComponentResolver();
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @Nullable
+    PackageStateInternal getDisabledSystemPackage(@NonNull String packageName);
+
+    @Computer.LiveImplementation(override = LiveImplementation.MANDATORY)
+    @Nullable
+    ResolveInfo getInstantAppInstallerInfo();
 }
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index c437697..9e87898 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -49,7 +49,6 @@
 
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
-import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
 import static com.android.server.pm.PackageManagerService.DEBUG_DOMAIN_VERIFICATION;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
@@ -59,6 +58,7 @@
 import static com.android.server.pm.PackageManagerService.HIDE_EPHEMERAL_APIS;
 import static com.android.server.pm.PackageManagerService.TAG;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
+import static com.android.server.pm.resolution.ComponentResolver.RESOLVE_PRIORITY_SORTER;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -143,6 +143,7 @@
 import com.android.server.pm.pkg.component.ParsedProvider;
 import com.android.server.pm.pkg.component.ParsedService;
 import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.resolution.ComponentResolverApi;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationUtils;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -362,7 +363,7 @@
     private final PermissionManagerServiceInternal mPermissionManager;
     private final ApexManager mApexManager;
     private final PackageManagerServiceInjector mInjector;
-    private final ComponentResolver mComponentResolver;
+    private final ComponentResolverApi mComponentResolver;
     private final InstantAppResolverConnection mInstantAppResolverConnection;
     private final DefaultAppProvider mDefaultAppProvider;
     private final DomainVerificationManagerInternal mDomainVerificationManager;
@@ -644,7 +645,7 @@
         // reader
         String pkgName = intent.getPackage();
         if (pkgName == null) {
-            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
                     resolvedType, flags, userId);
             if (resolveInfos == null) {
                 return Collections.emptyList();
@@ -654,7 +655,7 @@
         }
         final AndroidPackage pkg = mPackages.get(pkgName);
         if (pkg != null) {
-            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(intent,
+            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
                     resolvedType, flags, pkg.getServices(),
                     userId);
             if (resolveInfos == null) {
@@ -691,7 +692,7 @@
             }
 
             // Check for results in the current profile.
-            result = filterIfNotSystemUser(mComponentResolver.queryActivities(
+            result = filterIfNotSystemUser(mComponentResolver.queryActivities(this,
                     intent, resolvedType, flags, userId), userId);
             addInstant = isInstantAppResolutionAllowed(intent, result, userId,
                     false /*skipPackageCheck*/, flags);
@@ -753,7 +754,7 @@
             result = null;
             if (setting != null && setting.getAndroidPackage() != null && (resolveForStart
                     || !shouldFilterApplication(setting, filterCallingUid, userId))) {
-                result = filterIfNotSystemUser(mComponentResolver.queryActivities(
+                result = filterIfNotSystemUser(mComponentResolver.queryActivities(this,
                         intent, resolvedType, flags, setting.getAndroidPackage().getActivities(),
                         userId), userId);
             }
@@ -1181,7 +1182,7 @@
                 sourceUserId)) {
             return null;
         }
-        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
+        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent,
                 resolvedType, flags, parentUserId);
 
         if (resultTargetUser == null || resultTargetUser.isEmpty()) {
@@ -1232,7 +1233,7 @@
             Intent intent, String resolvedType, int userId) {
         CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
         if (resolver != null) {
-            return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
+            return resolver.queryIntent(this, intent, resolvedType, false /*defaultOnly*/, userId);
         }
         return null;
     }
@@ -1447,7 +1448,7 @@
         ResolveInfo localInstantApp = null;
         boolean blockResolution = false;
         if (!alreadyResolvedLocally) {
-            final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(
+            final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(this,
                     intent,
                     resolvedType,
                     flags
@@ -1493,8 +1494,8 @@
                                 null /*callingFeatureId*/, isRequesterInstantApp, userId,
                                 null /*verificationBundle*/, resolveForStart,
                                 digest.getDigestPrefixSecure(), token);
-                auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
-                        mInstantAppResolverConnection, requestObject);
+                auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(this,
+                        mUserManager, mInstantAppResolverConnection, requestObject);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             } else {
                 // we have an instant application locally, but, we can't admit that since
@@ -1830,7 +1831,7 @@
             return null;
         }
 
-        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(intent,
+        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent,
                 resolvedType, flags, targetUserId);
         if (CollectionUtils.isEmpty(resultTargetUser)) {
             return null;
@@ -2586,7 +2587,7 @@
                 mSettings.getPersistentPreferredActivities(userId);
         //TODO(b/158003772): Remove double query
         List<PersistentPreferredActivity> pprefs = ppir != null
-                ? ppir.queryIntent(intent, resolvedType,
+                ? ppir.queryIntent(this, intent, resolvedType,
                 (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                 userId)
                 : new ArrayList<>();
@@ -3244,7 +3245,7 @@
         // Get the list of preferred activities that handle the intent
         if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
         List<PreferredActivity> prefs = pir != null
-                ? pir.queryIntent(intent, resolvedType,
+                ? pir.queryIntent(this, intent, resolvedType,
                 (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                 userId)
                 : null;
@@ -3376,7 +3377,7 @@
                                         pa.mPref.mComponent,
                                         pa.mPref.mAlways);
                                 pir.removeFilter(pa);
-                                pir.addFilter(freshPa);
+                                pir.addFilter(this, freshPa);
                                 result.mChanged = true;
                             } else {
                                 if (DEBUG_PREFERRED) {
@@ -3399,7 +3400,7 @@
                                 PreferredActivity lastChosen = new PreferredActivity(
                                         pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
                                         false);
-                                pir.addFilter(lastChosen);
+                                pir.addFilter(this, lastChosen);
                                 result.mChanged = true;
                             }
                             result.mPreferredResolveInfo = null;
@@ -3454,7 +3455,7 @@
             Slog.v(TAG, "Looking for persistent preferred activities...");
         }
         List<PersistentPreferredActivity> pprefs = ppir != null
-                ? ppir.queryIntent(intent, resolvedType,
+                ? ppir.queryIntent(this, intent, resolvedType,
                 (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                 userId)
                 : null;
@@ -4661,7 +4662,8 @@
             int callingUid) {
         if (!mUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId);
-        final ProviderInfo providerInfo = mComponentResolver.queryProvider(name, flags, userId);
+        final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, flags,
+                userId);
         boolean checkedGrants = false;
         if (providerInfo != null) {
             // Looking for cross-user grants before enforcing the typical cross-users permissions
@@ -4739,7 +4741,7 @@
         final List<String> names = new ArrayList<>();
         final List<ProviderInfo> infos = new ArrayList<>();
         final int callingUserId = UserHandle.getCallingUserId();
-        mComponentResolver.querySyncProviders(names, infos, safeMode, callingUserId);
+        mComponentResolver.querySyncProviders(this, names, infos, safeMode, callingUserId);
         for (int i = infos.size() - 1; i >= 0; i--) {
             final ProviderInfo providerInfo = infos.get(i);
             final PackageStateInternal ps = mSettings.getPackage(providerInfo.packageName);
@@ -4771,8 +4773,8 @@
         if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
         flags = updateFlagsForComponent(flags, userId);
         ArrayList<ProviderInfo> finalList = null;
-        final List<ProviderInfo> matchList =
-                mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId);
+        final List<ProviderInfo> matchList = mComponentResolver.queryProviders(this, processName,
+                metaDataKey, uid, flags, userId);
         final int listSize = (matchList == null ? 0 : matchList.size());
         for (int i = 0; i < listSize; i++) {
             final ProviderInfo providerInfo = matchList.get(i);
@@ -5674,4 +5676,22 @@
     public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
         return mSettings.getSharedUserPackages(sharedUserAppId);
     }
+
+    @NonNull
+    @Override
+    public ComponentResolverApi getComponentResolver() {
+        return mComponentResolver;
+    }
+
+    @Nullable
+    @Override
+    public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+        return mSettings.getDisabledSystemPkg(packageName);
+    }
+
+    @Nullable
+    @Override
+    public ResolveInfo getInstantAppInstallerInfo() {
+        return mInstantAppInstallerInfo;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ComputerLocked.java b/services/core/java/com/android/server/pm/ComputerLocked.java
index 583348b..5d89c7d 100644
--- a/services/core/java/com/android/server/pm/ComputerLocked.java
+++ b/services/core/java/com/android/server/pm/ComputerLocked.java
@@ -48,6 +48,7 @@
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.pm.resolution.ComponentResolverApi;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
 
@@ -868,4 +869,28 @@
             return super.getSharedUserPackages(sharedUserAppId);
         }
     }
+
+    @NonNull
+    @Override
+    public ComponentResolverApi getComponentResolver() {
+        synchronized (mLock) {
+            return super.getComponentResolver();
+        }
+    }
+
+    @Nullable
+    @Override
+    public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+        synchronized (mLock) {
+            return super.getDisabledSystemPackage(packageName);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ResolveInfo getInstantAppInstallerInfo() {
+        synchronized (mLock) {
+            return super.getInstantAppInstallerInfo();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ComputerTracker.java b/services/core/java/com/android/server/pm/ComputerTracker.java
index 72e67da..216ad71 100644
--- a/services/core/java/com/android/server/pm/ComputerTracker.java
+++ b/services/core/java/com/android/server/pm/ComputerTracker.java
@@ -49,6 +49,7 @@
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedUserApi;
+import com.android.server.pm.resolution.ComponentResolverApi;
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
 
@@ -1299,4 +1300,28 @@
             return current.mComputer.getSharedUserPackages(sharedUserAppId);
         }
     }
+
+    @NonNull
+    @Override
+    public ComponentResolverApi getComponentResolver() {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getComponentResolver();
+        }
+    }
+
+    @Nullable
+    @Override
+    public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getDisabledSystemPackage(packageName);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ResolveInfo getInstantAppInstallerInfo() {
+        try (ThreadComputer current = snapshot()) {
+            return current.mComputer.getInstantAppInstallerInfo();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index d5a882b..2b46ed4 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -405,7 +405,8 @@
         }
 
         if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
-            mPm.mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
+            mPm.mComponentResolver.dumpContentProviders(mPm.snapshotComputer(), pw, dumpState,
+                    packageName);
         }
 
         if (!checkin
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 2ef092e..234a230 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -36,6 +36,7 @@
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
+import static android.content.pm.PackageManagerInternal.PACKAGE_SETUP_WIZARD;
 import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
 import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
 import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
@@ -454,7 +455,8 @@
             KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
             ksms.addScannedPackageLPw(pkg);
 
-            mPm.mComponentResolver.addAllComponents(pkg, chatty);
+            mPm.mComponentResolver.addAllComponents(pkg, chatty, mPm.mSetupWizardPackage,
+                    mPm.snapshotComputer());
             mPm.mAppsFilter.addPackage(pkgSetting, isReplace);
             mPm.addAllPackageProperties(pkg);
 
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 79f8dc1..92d6a82 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -57,6 +57,7 @@
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.server.pm.InstantAppResolverConnection.ConnectionException;
 import com.android.server.pm.InstantAppResolverConnection.PhaseTwoCallback;
+import com.android.server.pm.resolution.ComponentResolver;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -134,8 +135,9 @@
         }
     }
 
-    public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(
-            InstantAppResolverConnection connection, InstantAppRequest requestObj) {
+    public static AuxiliaryResolveInfo doInstantAppResolutionPhaseOne(@NonNull Computer computer,
+            @NonNull UserManagerService userManager, InstantAppResolverConnection connection,
+            InstantAppRequest requestObj) {
         final long startTime = System.currentTimeMillis();
         final String token = requestObj.token;
         if (DEBUG_INSTANT) {
@@ -149,7 +151,7 @@
             final List<InstantAppResolveInfo> instantAppResolveInfoList =
                     connection.getInstantAppResolveInfoList(buildRequestInfo(requestObj));
             if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
-                resolveInfo = InstantAppResolver.filterInstantAppIntent(
+                resolveInfo = InstantAppResolver.filterInstantAppIntent(computer, userManager,
                         instantAppResolveInfoList, origIntent, requestObj.resolvedType,
                         requestObj.userId, origIntent.getPackage(), token,
                         requestObj.hostDigestPrefixSecure);
@@ -187,9 +189,10 @@
         return resolveInfo;
     }
 
-    public static void doInstantAppResolutionPhaseTwo(Context context,
-            InstantAppResolverConnection connection, InstantAppRequest requestObj,
-            ActivityInfo instantAppInstaller, Handler callbackHandler) {
+    public static void doInstantAppResolutionPhaseTwo(Context context, @NonNull Computer computer,
+            @NonNull UserManagerService userManager, InstantAppResolverConnection connection,
+            InstantAppRequest requestObj, ActivityInfo instantAppInstaller,
+            Handler callbackHandler) {
         final long startTime = System.currentTimeMillis();
         final String token = requestObj.token;
         if (DEBUG_INSTANT) {
@@ -205,7 +208,7 @@
                 final Intent failureIntent;
                 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                     final AuxiliaryResolveInfo instantAppIntentInfo =
-                            InstantAppResolver.filterInstantAppIntent(
+                            InstantAppResolver.filterInstantAppIntent(computer, userManager,
                                     instantAppResolveInfoList, origIntent, null /*resolvedType*/,
                                     0 /*userId*/, origIntent.getPackage(),
                                     token, requestObj.hostDigestPrefixSecure);
@@ -386,7 +389,8 @@
         );
     }
 
-    private static AuxiliaryResolveInfo filterInstantAppIntent(
+    private static AuxiliaryResolveInfo filterInstantAppIntent(@NonNull Computer computer,
+            @NonNull UserManagerService userManager,
             List<InstantAppResolveInfo> instantAppResolveInfoList, Intent origIntent,
             String resolvedType, int userId, String packageName, String token,
             int[] hostDigestPrefixSecure) {
@@ -421,7 +425,8 @@
             }
             // We matched a resolve info; resolve the filters to see if anything matches completely.
             List<AuxiliaryResolveInfo.AuxiliaryFilter> matchFilters = computeResolveFilters(
-                    origIntent, resolvedType, userId, packageName, token, instantAppResolveInfo);
+                    computer, userManager, origIntent, resolvedType, userId, packageName, token,
+                    instantAppResolveInfo);
             if (matchFilters != null) {
                 if (matchFilters.isEmpty()) {
                     requiresSecondPhase = true;
@@ -464,7 +469,8 @@
      *
      */
     private static List<AuxiliaryResolveInfo.AuxiliaryFilter> computeResolveFilters(
-            Intent origIntent, String resolvedType, int userId, String packageName, String token,
+            @NonNull Computer computer, @NonNull UserManagerService userManager, Intent origIntent,
+            String resolvedType, int userId, String packageName, String token,
             InstantAppResolveInfo instantAppInfo) {
         if (instantAppInfo.shouldLetInstallerDecide()) {
             return Collections.singletonList(
@@ -490,7 +496,7 @@
             return Collections.emptyList();
         }
         final ComponentResolver.InstantAppIntentResolver instantAppResolver =
-                new ComponentResolver.InstantAppIntentResolver();
+                new ComponentResolver.InstantAppIntentResolver(userManager);
         for (int j = instantAppFilters.size() - 1; j >= 0; --j) {
             final InstantAppIntentFilter instantAppFilter = instantAppFilters.get(j);
             final List<IntentFilter> splitFilters = instantAppFilter.getFilters();
@@ -508,7 +514,7 @@
                         && filter.hasCategory(Intent.CATEGORY_BROWSABLE)) {
                     continue;
                 }
-                instantAppResolver.addFilter(
+                instantAppResolver.addFilter(computer,
                         new AuxiliaryResolveInfo.AuxiliaryFilter(
                                 filter,
                                 instantAppInfo,
@@ -518,8 +524,8 @@
             }
         }
         List<AuxiliaryResolveInfo.AuxiliaryFilter> matchedResolveInfoList =
-                instantAppResolver.queryIntent(
-                        origIntent, resolvedType, false /*defaultOnly*/, userId);
+                instantAppResolver.queryIntent(computer, origIntent, resolvedType,
+                        false /*defaultOnly*/, userId);
         if (!matchedResolveInfoList.isEmpty()) {
             if (DEBUG_INSTANT) {
                 Log.d(TAG, "[" + token + "] Found match(es); " + matchedResolveInfoList);
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index ec71940..46e2aa3 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -313,6 +313,8 @@
             }
             case INSTANT_APP_RESOLUTION_PHASE_TWO: {
                 InstantAppResolver.doInstantAppResolutionPhaseTwo(mPm.mContext,
+                        mPm.snapshotComputer(),
+                        mPm.mUserManager,
                         mPm.mInstantAppResolverConnection,
                         (InstantAppRequest) msg.obj,
                         mPm.mInstantAppInstallerActivity,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 725e92a..0b7a6a7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -111,7 +111,6 @@
 import android.content.pm.PackageManager.Property;
 import android.content.pm.PackageManager.PropertyLocation;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
 import android.content.pm.PackagePartitions;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
@@ -242,6 +241,8 @@
 import com.android.server.pm.pkg.mutate.PackageStateWrite;
 import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.resolution.ComponentResolver;
+import com.android.server.pm.resolution.ComponentResolverApi;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationService;
 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
@@ -791,9 +792,6 @@
     private final AtomicInteger mNextMoveId = new AtomicInteger();
     final MovePackageHelper.MoveCallbacks mMoveCallbacks;
 
-    // Cache of users who need badging.
-    private final SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray();
-
     /**
      * Token for keys in mPendingVerification.
      * Handler thread only!
@@ -912,6 +910,8 @@
 
     final UserManagerService mUserManager;
 
+    final UserNeedsBadgingCache mUserNeedsBadging;
+
     // Stores a list of users whose package restrictions file needs to be updated
     final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
 
@@ -992,7 +992,7 @@
         public final ApplicationInfo androidApplication;
         public final String appPredictionServicePackage;
         public final AppsFilter appsFilter;
-        public final ComponentResolver componentResolver;
+        public final ComponentResolverApi componentResolver;
         public final PackageManagerService service;
         public final WatchedArrayMap<String, Integer> frozenPackages;
         public final SharedLibrariesRead sharedLibraries;
@@ -1081,17 +1081,10 @@
     private final SnapshotStatistics mSnapshotStatistics;
 
     /**
-     * Return the live computer.
-     */
-    Computer liveComputer() {
-        return mLiveComputer;
-    }
-
-    /**
      * Return the cached computer.  The method will rebuild the cached computer if necessary.
      * The live computer will be returned if snapshots are disabled.
      */
-    Computer snapshotComputer() {
+    public Computer snapshotComputer() {
         if (Thread.holdsLock(mLock)) {
             // If the current thread holds mLock then it may have modified state but not
             // yet invalidated the snapshot.  Always give the thread the live computer.
@@ -1171,8 +1164,8 @@
 
     @Override
     public void notifyPackagesReplacedReceived(String[] packages) {
-        ArraySet<String> packagesToNotify =
-                mComputer.getNotifyPackagesForReplacedReceived(packages);
+        Computer computer = snapshotComputer();
+        ArraySet<String> packagesToNotify = computer.getNotifyPackagesForReplacedReceived(packages);
         for (int index = 0; index < packagesToNotify.size(); index++) {
             notifyInstallObserver(packagesToNotify.valueAt(index));
         }
@@ -1441,7 +1434,7 @@
                 context, lock, installer, installLock, new PackageAbiHelperImpl(),
                 backgroundHandler,
                 SYSTEM_PARTITIONS,
-                (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
+                (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mUserNeedsBadging),
                 (i, pm) -> PermissionManagerService.create(context,
                         i.getSystemConfig().getAvailableFeatures()),
                 (i, pm) -> new UserManagerService(context, pm,
@@ -1619,6 +1612,7 @@
         mPermissionManager = injector.getPermissionManagerServiceInternal();
         mSettings = injector.getSettings();
         mUserManager = injector.getUserManagerService();
+        mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
         mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
         mHandler = injector.getHandler();
         mSharedLibraries = injector.getSharedLibrariesImpl();
@@ -1744,6 +1738,7 @@
         mTestUtilityService = LocalServices.getService(TestUtilityService.class);
         LocalServices.addService(PackageManagerInternal.class, mPmInternal);
         mUserManager = injector.getUserManagerService();
+        mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
         mComponentResolver = injector.getComponentResolver();
         mPermissionManager = injector.getPermissionManagerServiceInternal();
         mSettings = injector.getSettings();
@@ -1846,7 +1841,9 @@
                 mAppDataHelper);
         mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
         mPreferredActivityHelper = new PreferredActivityHelper(this);
-        mResolveIntentHelper = new ResolveIntentHelper(this, mPreferredActivityHelper);
+        mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
+                injector.getCompatibility(), mUserManager, mDomainVerificationManager,
+                mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity);
         mDexOptHelper = new DexOptHelper(this);
         mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
                 mProtectedPackages);
@@ -1862,6 +1859,7 @@
             registerObservers(true);
         }
 
+        Computer computer = mLiveComputer;
         // CHECKSTYLE:OFF IndentationCheck
         synchronized (mInstallLock) {
         // writer
@@ -1975,12 +1973,12 @@
                     userIds, startTime);
 
             // Resolve the storage manager.
-            mStorageManagerPackage = getStorageManagerPackageName();
+            mStorageManagerPackage = getStorageManagerPackageName(computer);
 
             // Resolve protected action filters. Only the setup wizard is allowed to
             // have a high priority filter for these actions.
-            mSetupWizardPackage = getSetupWizardPackageNameImpl();
-            mComponentResolver.fixProtectedFilterPriorities();
+            mSetupWizardPackage = getSetupWizardPackageNameImpl(computer);
+            mComponentResolver.fixProtectedFilterPriorities(mPmInternal.getSetupWizardPackageName());
 
             mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
             mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
@@ -2108,13 +2106,13 @@
                     SystemClock.uptimeMillis());
 
             if (!mOnlyCore) {
-                mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr();
-                mRequiredInstallerPackage = getRequiredInstallerLPr();
-                mRequiredUninstallerPackage = getRequiredUninstallerLPr();
+                mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr(computer);
+                mRequiredInstallerPackage = getRequiredInstallerLPr(computer);
+                mRequiredUninstallerPackage = getRequiredUninstallerLPr(computer);
                 ComponentName intentFilterVerifierComponent =
-                        getIntentFilterVerifierComponentNameLPr();
+                        getIntentFilterVerifierComponentNameLPr(computer);
                 ComponentName domainVerificationAgent =
-                        getDomainVerificationAgentComponentNameLPr();
+                        getDomainVerificationAgentComponentNameLPr(computer);
 
                 DomainVerificationProxy domainVerificationProxy = DomainVerificationProxy.makeProxy(
                         intentFilterVerifierComponent, domainVerificationAgent, mContext,
@@ -2137,7 +2135,7 @@
 
             // PermissionController hosts default permission granting and role management, so it's a
             // critical part of the core system.
-            mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
+            mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(computer);
 
             mSettings.setPermissionControllerVersion(
                     getPackageInfo(mRequiredPermissionControllerPackage, 0,
@@ -2170,7 +2168,8 @@
                 mInstantAppResolverConnection =
                         mInjector.getInstantAppResolverConnection(instantAppResolverComponent);
                 mInstantAppResolverSettingsComponent =
-                        getInstantAppResolverSettingsLPr(instantAppResolverComponent);
+                        getInstantAppResolverSettingsLPr(computer,
+                                instantAppResolverComponent);
             } else {
                 mInstantAppResolverConnection = null;
                 mInstantAppResolverSettingsComponent = null;
@@ -2258,11 +2257,13 @@
                 "persist.pm.mock-upgrade", false /* default */);
     }
 
-    private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
+    @Nullable
+    private String getRequiredButNotReallyRequiredVerifierLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
 
         final List<ResolveInfo> matches =
-                mResolveIntentHelper.queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
+                mResolveIntentHelper.queryIntentReceiversInternal(computer, intent,
+                        PACKAGE_MIME_TYPE,
                         MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                         UserHandle.USER_SYSTEM, Binder.getCallingUid());
         if (matches.size() == 1) {
@@ -2300,12 +2301,13 @@
         return servicesExtensionPackage;
     }
 
-    private @NonNull String getRequiredInstallerLPr() {
+    private @NonNull String getRequiredInstallerLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setDataAndType(Uri.parse("content://com.example/foo.apk"), PACKAGE_MIME_TYPE);
 
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent,
+                PACKAGE_MIME_TYPE,
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                 UserHandle.USER_SYSTEM);
         if (matches.size() == 1) {
@@ -2319,14 +2321,14 @@
         }
     }
 
-    private @NonNull String getRequiredUninstallerLPr() {
+    private @NonNull String getRequiredUninstallerLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setData(Uri.fromParts(PACKAGE_SCHEME, "foo.bar", null));
 
-        final ResolveInfo resolveInfo = resolveIntent(intent, null,
-                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
-                UserHandle.USER_SYSTEM);
+        final ResolveInfo resolveInfo = mResolveIntentHelper.resolveIntentInternal(computer, intent,
+                null, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                0 /*privateResolveFlags*/, UserHandle.USER_SYSTEM, false, Binder.getCallingUid());
         if (resolveInfo == null ||
                 mResolveActivity.name.equals(resolveInfo.getComponentInfo().name)) {
             throw new RuntimeException("There must be exactly one uninstaller; found "
@@ -2335,11 +2337,11 @@
         return resolveInfo.getComponentInfo().packageName;
     }
 
-    private @NonNull String getRequiredPermissionControllerLPr() {
+    private @NonNull String getRequiredPermissionControllerLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
 
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                 UserHandle.USER_SYSTEM);
         if (matches.size() == 1) {
@@ -2354,11 +2356,13 @@
         }
     }
 
-    private @NonNull ComponentName getIntentFilterVerifierComponentNameLPr() {
+    @NonNull
+    private ComponentName getIntentFilterVerifierComponentNameLPr(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
 
         final List<ResolveInfo> matches =
-                mResolveIntentHelper.queryIntentReceiversInternal(intent, PACKAGE_MIME_TYPE,
+                mResolveIntentHelper.queryIntentReceiversInternal(computer, intent,
+                        PACKAGE_MIME_TYPE,
                         MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                         UserHandle.USER_SYSTEM, Binder.getCallingUid());
         ResolveInfo best = null;
@@ -2384,10 +2388,10 @@
     }
 
     @Nullable
-    private ComponentName getDomainVerificationAgentComponentNameLPr() {
+    private ComponentName getDomainVerificationAgentComponentNameLPr(@NonNull Computer computer) {
         Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION);
         List<ResolveInfo> matches =
-                mResolveIntentHelper.queryIntentReceiversInternal(intent, null,
+                mResolveIntentHelper.queryIntentReceiversInternal(computer, intent, null,
                         MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                         UserHandle.USER_SYSTEM, Binder.getCallingUid());
         ResolveInfo best = null;
@@ -2496,13 +2500,14 @@
                         | MATCH_DIRECT_BOOT_UNAWARE
                         | Intent.FLAG_IGNORE_EPHEMERAL
                         | (mIsEngBuild ? 0 : MATCH_SYSTEM_ONLY);
+        final Computer computer = snapshotComputer();
         final Intent intent = new Intent();
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
         List<ResolveInfo> matches = null;
         for (String action : orderedActions) {
             intent.setAction(action);
-            matches = queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
+            matches = computer.queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
                     resolveFlags, UserHandle.USER_SYSTEM);
             if (matches.isEmpty()) {
                 if (DEBUG_INSTANT) {
@@ -2532,14 +2537,14 @@
         }
     }
 
-    private @Nullable ComponentName getInstantAppResolverSettingsLPr(
+    private @Nullable ComponentName getInstantAppResolverSettingsLPr(@NonNull Computer computer,
             @NonNull ComponentName resolver) {
         final Intent intent =  new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS)
                 .addCategory(Intent.CATEGORY_DEFAULT)
                 .setPackage(resolver.getPackageName());
         final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
-        List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, resolveFlags,
-                UserHandle.USER_SYSTEM);
+        List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
+                resolveFlags, UserHandle.USER_SYSTEM);
         if (matches.isEmpty()) {
             return null;
         }
@@ -2942,27 +2947,6 @@
     }
 
     /**
-     * Update given flags when being used to request {@link PackageInfo}.
-     */
-    private long updateFlagsForPackage(long flags, int userId) {
-        return mComputer.updateFlagsForPackage(flags, userId);
-    }
-
-    /**
-     * Update given flags when being used to request {@link ApplicationInfo}.
-     */
-    private long updateFlagsForApplication(long flags, int userId) {
-        return mComputer.updateFlagsForApplication(flags, userId);
-    }
-
-    /**
-     * Update given flags when being used to request {@link ComponentInfo}.
-     */
-    private long updateFlagsForComponent(long flags, int userId) {
-        return mComputer.updateFlagsForComponent(flags, userId);
-    }
-
-    /**
      * Update given flags when being used to request {@link ResolveInfo}.
      * <p>Instant apps are resolved specially, depending upon context. Minimally,
      * {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT}
@@ -3314,8 +3298,8 @@
     @Override
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
-        return mResolveIntentHelper.resolveIntentInternal(intent, resolvedType, flags,
-                0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
+        return mResolveIntentHelper.resolveIntentInternal(snapshotComputer(), intent, resolvedType,
+                flags, 0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
     }
 
     @Override
@@ -3414,8 +3398,8 @@
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
 
-            return new ParceledListSlice<>(
-                    queryIntentActivitiesInternal(intent, resolvedType, flags, userId));
+            return new ParceledListSlice<>(snapshotComputer().queryIntentActivitiesInternal(intent,
+                    resolvedType, flags, userId));
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -3429,68 +3413,28 @@
         return mComputer.getInstantAppPackageName(callingUid);
     }
 
-    @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
-        return mComputer.queryIntentActivitiesInternal(intent,
-                resolvedType, flags, userId);
-    }
-
-    @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
-            @PrivateResolveFlags long privateResolveFlags, int filterCallingUid, int userId,
-            boolean resolveForStart, boolean allowDynamicSplits) {
-        return mComputer.queryIntentActivitiesInternal(intent,
-                resolvedType, flags, privateResolveFlags,
-                filterCallingUid, userId, resolveForStart, allowDynamicSplits);
-    }
-
-    private CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
-            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
-            int parentUserId) {
-        return mComputer.getCrossProfileDomainPreferredLpr(intent,
-                resolvedType, flags, sourceUserId, parentUserId);
-    }
-
-    /**
-     * Filters out ephemeral activities.
-     * <p>When resolving for an ephemeral app, only activities that 1) are defined in the
-     * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
-     *
-     * @param resolveInfos The pre-filtered list of resolved activities
-     * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
-     *          is performed.
-     * @param intent
-     * @return A filtered list of resolved activities.
-     */
-    List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
-            String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
-            boolean resolveForStart, int userId, Intent intent) {
-        return mComputer.applyPostResolutionFilter(resolveInfos,
-                ephemeralPkgName, allowDynamicSplits, filterCallingUid,
-                resolveForStart, userId, intent);
-    }
-
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
             Intent[] specifics, String[] specificTypes, Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return new ParceledListSlice<>(mResolveIntentHelper.queryIntentActivityOptionsInternal(
-                caller, specifics, specificTypes, intent, resolvedType, flags, userId));
+                snapshotComputer(), caller, specifics, specificTypes, intent, resolvedType, flags,
+                userId));
     }
 
     @Override
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
-        return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(intent,
-                resolvedType, flags, userId, Binder.getCallingUid()));
+        return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(
+                snapshotComputer(), intent, resolvedType, flags, userId, Binder.getCallingUid()));
     }
 
     @Override
     public ResolveInfo resolveService(Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         final int callingUid = Binder.getCallingUid();
-        return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
-                callingUid);
+        return mResolveIntentHelper.resolveServiceInternal(snapshotComputer(), intent, resolvedType,
+                flags, userId, callingUid);
     }
 
     @Override
@@ -3513,7 +3457,7 @@
     public @NonNull ParceledListSlice<ResolveInfo> queryIntentContentProviders(Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
         return new ParceledListSlice<>(mResolveIntentHelper.queryIntentContentProvidersInternal(
-                intent, resolvedType, flags, userId));
+                snapshotComputer(), intent, resolvedType, flags, userId));
     }
 
     @Override
@@ -3907,15 +3851,9 @@
         synchronized (mLock) {
             mPackageUsage.writeNow(mSettings.getPackagesLocked());
 
-            // This is the last chance to write out pending restriction settings
-            if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) {
-                mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS);
-                synchronized (mDirtyUsers) {
-                    for (int userId : mDirtyUsers) {
-                        mSettings.writePackageRestrictionsLPr(userId);
-                    }
-                    mDirtyUsers.clear();
-                }
+            if (mHandler.hasMessages(WRITE_SETTINGS)) {
+                mHandler.removeMessages(WRITE_SETTINGS);
+                writeSettings();
             }
         }
     }
@@ -5409,7 +5347,7 @@
                     }
                 }
             }
-            resolver.addFilter(newFilter);
+            resolver.addFilter(snapshotComputer(), newFilter);
         }
         scheduleWritePackageRestrictions(sourceUserId);
     }
@@ -5498,11 +5436,11 @@
         mPreferredActivityHelper.setHomeActivity(comp, userId);
     }
 
-    private @Nullable String getSetupWizardPackageNameImpl() {
+    private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) {
         final Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
 
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
                         | MATCH_DISABLED_COMPONENTS,
                 UserHandle.myUserId());
@@ -5515,10 +5453,10 @@
         }
     }
 
-    private @Nullable String getStorageManagerPackageName() {
+    private @Nullable String getStorageManagerPackageName(@NonNull Computer computer) {
         final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
 
-        final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
                         | MATCH_DISABLED_COMPONENTS,
                 UserHandle.myUserId());
@@ -6784,21 +6722,7 @@
     }
 
     boolean userNeedsBadging(int userId) {
-        int index = mUserNeedsBadging.indexOfKey(userId);
-        if (index < 0) {
-            final UserInfo userInfo;
-            final long token = Binder.clearCallingIdentity();
-            try {
-                userInfo = mUserManager.getUserInfo(userId);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            final boolean b;
-            b = userInfo != null && userInfo.isManagedProfile();
-            mUserNeedsBadging.put(userId, b);
-            return b;
-        }
-        return mUserNeedsBadging.valueAt(index);
+        return mUserNeedsBadging.get(userId);
     }
 
     @Nullable
@@ -7034,16 +6958,14 @@
         }
 
         @Override
-        public PackageSetting getDisabledSystemPackage(@NonNull String packageName) {
-            synchronized (mLock) {
-                return mSettings.getDisabledSystemPkgLPr(packageName);
-            }
+        public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
+            return snapshotComputer().getDisabledSystemPackage(packageName);
         }
 
         @Override
         public @Nullable
         String getDisabledSystemPackageName(@NonNull String packageName) {
-            PackageSetting disabledPkgSetting = getDisabledSystemPackage(
+            PackageStateInternal disabledPkgSetting = getDisabledSystemPackage(
                     packageName);
             AndroidPackage disabledPkg = disabledPkgSetting == null
                     ? null : disabledPkgSetting.getPkg();
@@ -7183,9 +7105,8 @@
         public List<ResolveInfo> queryIntentActivities(
                 Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
                 int filterCallingUid, int userId) {
-            return PackageManagerService.this
-                    .queryIntentActivitiesInternal(intent, resolvedType, flags, 0, filterCallingUid,
-                            userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
+            return snapshotComputer().queryIntentActivitiesInternal(intent, resolvedType, flags,
+                    userId);
         }
 
         @Override
@@ -7193,7 +7114,7 @@
                 String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
                 int filterCallingUid, int userId) {
             return PackageManagerService.this.mResolveIntentHelper.queryIntentReceiversInternal(
-                    intent, resolvedType, flags, userId, filterCallingUid);
+                    snapshotComputer(), intent, resolvedType, flags, userId, filterCallingUid);
         }
 
         @Override
@@ -7436,7 +7357,7 @@
                 @PackageManager.ResolveInfoFlagsBits long flags,
                 @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
                 boolean resolveForStart, int filterCallingUid) {
-            return mResolveIntentHelper.resolveIntentInternal(
+            return mResolveIntentHelper.resolveIntentInternal(snapshotComputer(),
                     intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
                     filterCallingUid);
         }
@@ -7444,8 +7365,8 @@
         @Override
         public ResolveInfo resolveService(Intent intent, String resolvedType,
                 @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
-            return mResolveIntentHelper.resolveServiceInternal(intent, resolvedType, flags, userId,
-                    callingUid);
+            return mResolveIntentHelper.resolveServiceInternal(snapshotComputer(), intent,
+                    resolvedType, flags, userId, callingUid);
         }
 
         @Override
@@ -7900,6 +7821,12 @@
                 @NonNull Consumer<PackageStateMutator> consumer) {
             return PackageManagerService.this.commitPackageStateMutation(state, consumer);
         }
+
+        @NonNull
+        @Override
+        public Computer snapshot() {
+            return snapshotComputer();
+        }
     }
 
     private boolean setEnabledOverlayPackages(@UserIdInt int userId,
@@ -8346,16 +8273,6 @@
         }
     }
 
-    private void applyMimeGroupChanges(String packageName, String mimeGroup) {
-        if (mComponentResolver.updateMimeGroup(packageName, mimeGroup)) {
-            Binder.withCleanCallingIdentity(() ->
-                    mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
-                            UserHandle.USER_ALL));
-        }
-
-        mPmInternal.writeSettings(false);
-    }
-
     @Override
     public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
         enforceOwnerRights(packageName, Binder.getCallingUid());
@@ -8375,7 +8292,13 @@
         commitPackageStateMutation(null, packageName, packageStateWrite -> {
             packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet);
         });
-        applyMimeGroupChanges(packageName, mimeGroup);
+        if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) {
+            Binder.withCleanCallingIdentity(() ->
+                    mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
+                            UserHandle.USER_ALL));
+        }
+
+        scheduleWriteSettings();
     }
 
     @Override
@@ -8607,8 +8530,8 @@
     @Override
     public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
             String featureId, int userId) throws RemoteException {
-        return mResolveIntentHelper.getLaunchIntentSenderForPackage(packageName, callingPackage,
-                featureId, userId);
+        return mResolveIntentHelper.getLaunchIntentSenderForPackage(snapshotComputer(),
+                packageName, callingPackage, featureId, userId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 05bb01e..a02237f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -34,6 +34,7 @@
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.resolution.ComponentResolver;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import java.util.List;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 2f56bfe..a9471cf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -93,6 +93,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.resolution.ComponentResolverApi;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import dalvik.system.VMRuntime;
@@ -1064,7 +1065,7 @@
 
     // Static to give access to ComputeEngine
     public static void applyEnforceIntentFilterMatching(
-            PlatformCompat compat, ComponentResolver resolver,
+            PlatformCompat compat, ComponentResolverApi resolver,
             List<ResolveInfo> resolveInfos, boolean isReceiver,
             Intent intent, String resolvedType, int filterCallingUid) {
         // Do not enforce filter matching when the caller is system or root.
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index bb82e6a..8c49baf 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -140,8 +140,8 @@
             return false;
         }
         final Intent intent = mPm.getHomeIntent();
-        final List<ResolveInfo> resolveInfos = mPm.queryIntentActivitiesInternal(intent, null,
-                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
+        final List<ResolveInfo> resolveInfos = mPm.snapshotComputer().queryIntentActivitiesInternal(
+                intent, null, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
         final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
                 intent, null, 0, resolveInfos, true, false, false, userId);
         final String packageName = preferredResolveInfo != null
@@ -209,7 +209,8 @@
             if (removeExisting && existing != null) {
                 Settings.removeFilters(pir, filter, existing);
             }
-            pir.addFilter(new PreferredActivity(filter, match, set, activity, always));
+            pir.addFilter(mPm.snapshotComputer(),
+                    new PreferredActivity(filter, match, set, activity, always));
             mPm.scheduleWritePackageRestrictions(userId);
         }
         if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(userId))) {
@@ -394,6 +395,7 @@
         }
         synchronized (mPm.mLock) {
             mPm.mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter(
+                    mPm.snapshotComputer(),
                     new PersistentPreferredActivity(filter, activity, true));
             mPm.scheduleWritePackageRestrictions(userId);
         }
@@ -672,8 +674,8 @@
                 0, userId, callingUid, false /*includeInstantApps*/,
                 mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
                         0));
-        final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                flags, userId);
+        final List<ResolveInfo> query = mPm.snapshotComputer().queryIntentActivitiesInternal(intent,
+                resolvedType, flags, userId);
         synchronized (mPm.mLock) {
             return mPm.findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false,
                     userId);
@@ -699,8 +701,8 @@
             filter.dump(new PrintStreamPrinter(System.out), "    ");
         }
         intent.setComponent(null);
-        final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                flags, userId);
+        final List<ResolveInfo> query = mPm.snapshotComputer().queryIntentActivitiesInternal(intent,
+                resolvedType, flags, userId);
         // Find any earlier preferred or last chosen entries and nuke them
         findPreferredActivityNotLocked(
                 intent, resolvedType, flags, query, false, true, false, userId);
@@ -715,8 +717,8 @@
         }
         final int userId = UserHandle.getCallingUserId();
         if (DEBUG_PREFERRED) Log.v(TAG, "Querying last chosen activity for " + intent);
-        final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                flags, userId);
+        final List<ResolveInfo> query = mPm.snapshotComputer().queryIntentActivitiesInternal(intent,
+                resolvedType, flags, userId);
         return findPreferredActivityNotLocked(
                 intent, resolvedType, flags, query, false, false, false, userId);
     }
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 2aff90f..25356a4 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -27,6 +27,8 @@
 import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -50,23 +52,52 @@
 
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.resolution.ComponentResolverApi;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Supplier;
 
 final class ResolveIntentHelper {
-    private final PackageManagerService mPm;
+    @NonNull
+    private final Context mContext;
+    @NonNull
+    private final PlatformCompat mPlatformCompat;
+    @NonNull
+    private final UserManagerService mUserManager;
+    @NonNull
     private final PreferredActivityHelper mPreferredActivityHelper;
+    @NonNull
+    private final DomainVerificationManagerInternal mDomainVerificationManager;
+    @NonNull
+    private final UserNeedsBadgingCache mUserNeedsBadging;
+    @NonNull
+    private final Supplier<ResolveInfo> mResolveInfoSupplier;
+    @NonNull
+    private final Supplier<ActivityInfo> mInstantAppInstallerActivitySupplier;
 
-    // TODO(b/198166813): remove PMS dependency
-    ResolveIntentHelper(PackageManagerService pm, PreferredActivityHelper preferredActivityHelper) {
-        mPm = pm;
+    ResolveIntentHelper(@NonNull Context context,
+            @NonNull PreferredActivityHelper preferredActivityHelper,
+            @NonNull PlatformCompat platformCompat, @NonNull UserManagerService userManager,
+            @NonNull DomainVerificationManagerInternal domainVerificationManager,
+            @NonNull UserNeedsBadgingCache userNeedsBadgingCache,
+            @NonNull Supplier<ResolveInfo> resolveInfoSupplier,
+            @NonNull Supplier<ActivityInfo> instantAppInstallerActivitySupplier) {
+        mContext = context;
         mPreferredActivityHelper = preferredActivityHelper;
+        mPlatformCompat = platformCompat;
+        mUserManager = userManager;
+        mDomainVerificationManager = domainVerificationManager;
+        mUserNeedsBadging = userNeedsBadgingCache;
+        mResolveInfoSupplier = resolveInfoSupplier;
+        mInstantAppInstallerActivitySupplier = instantAppInstallerActivitySupplier;
     }
 
     /**
@@ -74,35 +105,33 @@
      * However, if {@code resolveForStart} is {@code true}, all instant apps are visible
      * since we need to allow the system to start any installed application.
      */
-    public ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
+    public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
             boolean resolveForStart, int filterCallingUid) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
 
-            if (!mPm.mUserManager.exists(userId)) return null;
+            if (!mUserManager.exists(userId)) return null;
             final int callingUid = Binder.getCallingUid();
-            flags = mPm.updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
-                    mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+            flags = computer.updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+                    computer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
                             resolvedType, flags));
-            mPm.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+            computer.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
                     false /*checkShell*/, "resolve intent");
 
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
-            final List<ResolveInfo> query = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                    flags, privateResolveFlags, filterCallingUid, userId, resolveForStart,
-                    true /*allowDynamicSplits*/);
+            final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent,
+                    resolvedType, flags, privateResolveFlags, filterCallingUid, userId,
+                    resolveForStart, true /*allowDynamicSplits*/);
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             final boolean queryMayBeFiltered =
                     UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
                             && !resolveForStart;
 
-            final ResolveInfo bestChoice =
-                    chooseBestActivity(
-                            intent, resolvedType, flags, privateResolveFlags, query, userId,
-                            queryMayBeFiltered);
+            final ResolveInfo bestChoice = chooseBestActivity(computer, intent, resolvedType, flags,
+                    privateResolveFlags, query, userId, queryMayBeFiltered);
             final boolean nonBrowserOnly =
                     (privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
             if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
@@ -114,7 +143,7 @@
         }
     }
 
-    private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
+    private ResolveInfo chooseBestActivity(Computer computer, Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
             List<ResolveInfo> query, int userId, boolean queryMayBeFiltered) {
@@ -156,9 +185,10 @@
                     // If we have an ephemeral app, use it
                     if (ri.activityInfo.applicationInfo.isInstantApp()) {
                         final String packageName = ri.activityInfo.packageName;
-                        final PackageStateInternal ps = mPm.getPackageStateInternal(packageName);
+                        final PackageStateInternal ps =
+                                computer.getPackageStateInternal(packageName);
                         if (ps != null && PackageManagerServiceUtils.hasAnyDomainApproval(
-                                mPm.mDomainVerificationManager, ps, intent, flags, userId)) {
+                                mDomainVerificationManager, ps, intent, flags, userId)) {
                             return ri;
                         }
                     }
@@ -167,7 +197,7 @@
                         & PackageManagerInternal.RESOLVE_NON_RESOLVER_ONLY) != 0) {
                     return null;
                 }
-                ri = new ResolveInfo(mPm.getResolveInfo());
+                ri = new ResolveInfo(mResolveInfoSupplier.get());
                 // if all resolve options are browsers, mark the resolver's info as if it were
                 // also a browser.
                 ri.handleAllWebDataURI = browserCount == n;
@@ -184,7 +214,7 @@
                 if (!TextUtils.isEmpty(intentPackage) && allHavePackage(query, intentPackage)) {
                     final ApplicationInfo appi = query.get(0).activityInfo.applicationInfo;
                     ri.resolvePackageName = intentPackage;
-                    if (mPm.userNeedsBadging(userId)) {
+                    if (mUserNeedsBadging.get(userId)) {
                         ri.noResourceId = true;
                     } else {
                         ri.icon = appi.icon;
@@ -225,13 +255,14 @@
         return true;
     }
 
-    public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
-            String featureId, int userId) throws RemoteException {
+    public IntentSender getLaunchIntentSenderForPackage(@NonNull Computer computer,
+            String packageName, String callingPackage, String featureId, int userId)
+            throws RemoteException {
         Objects.requireNonNull(packageName);
         final int callingUid = Binder.getCallingUid();
-        mPm.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
+        computer.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                 false /* checkShell */, "get launch intent sender for package");
-        final int packageUid = mPm.getPackageUid(callingPackage, 0 /* flags */, userId);
+        final int packageUid = computer.getPackageUid(callingPackage, 0 /* flags */, userId);
         if (!UserHandle.isSameApp(callingUid, packageUid)) {
             throw new SecurityException("getLaunchIntentSenderForPackage() from calling uid: "
                     + callingUid + " does not own package: " + callingPackage);
@@ -242,17 +273,17 @@
         final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
         intentToResolve.addCategory(Intent.CATEGORY_INFO);
         intentToResolve.setPackage(packageName);
-        String resolvedType = intentToResolve.resolveTypeIfNeeded(
-                mPm.mContext.getContentResolver());
-        List<ResolveInfo> ris = mPm.queryIntentActivitiesInternal(intentToResolve, resolvedType,
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        String resolvedType = intentToResolve.resolveTypeIfNeeded(contentResolver);
+        List<ResolveInfo> ris = computer.queryIntentActivitiesInternal(intentToResolve, resolvedType,
                 0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
                 true /* resolveForStart */, false /* allowDynamicSplits */);
         if (ris == null || ris.size() <= 0) {
             intentToResolve.removeCategory(Intent.CATEGORY_INFO);
             intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
             intentToResolve.setPackage(packageName);
-            resolvedType = intentToResolve.resolveTypeIfNeeded(mPm.mContext.getContentResolver());
-            ris = mPm.queryIntentActivitiesInternal(intentToResolve, resolvedType,
+            resolvedType = intentToResolve.resolveTypeIfNeeded(contentResolver);
+            ris = computer.queryIntentActivitiesInternal(intentToResolve, resolvedType,
                     0 /* flags */, 0 /* privateResolveFlags */, callingUid, userId,
                     true /* resolveForStart */, false /* allowDynamicSplits */);
         }
@@ -277,17 +308,17 @@
 
     // In this method, we have to know the actual calling UID, but in some cases Binder's
     // call identity is removed, so the UID has to be passed in explicitly.
-    public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
+    public @NonNull List<ResolveInfo> queryIntentReceiversInternal(Computer computer, Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
             int filterCallingUid) {
-        if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
-        mPm.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
+        if (!mUserManager.exists(userId)) return Collections.emptyList();
+        computer.enforceCrossUserPermission(filterCallingUid, userId, false /*requireFullPermission*/,
                 false /*checkShell*/, "query intent receivers");
-        final String instantAppPkgName = mPm.getInstantAppPackageName(filterCallingUid);
-        flags = mPm.updateFlagsForResolve(
-                flags, userId, filterCallingUid, false /*includeInstantApps*/,
-                mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
-                        flags));
+        final String instantAppPkgName = computer.getInstantAppPackageName(filterCallingUid);
+        flags = computer.updateFlagsForResolve(flags, userId, filterCallingUid,
+                false /*includeInstantApps*/,
+                computer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+                        resolvedType, flags));
         Intent originalIntent = null;
         ComponentName comp = intent.getComponent();
         if (comp == null) {
@@ -297,9 +328,10 @@
                 comp = intent.getComponent();
             }
         }
+        final ComponentResolverApi componentResolver = computer.getComponentResolver();
         List<ResolveInfo> list = Collections.emptyList();
         if (comp != null) {
-            final ActivityInfo ai = mPm.getReceiverInfo(comp, flags, userId);
+            final ActivityInfo ai = computer.getReceiverInfo(comp, flags, userId);
             if (ai != null) {
                 // When specifying an explicit component, we prevent the activity from being
                 // used when either 1) the calling package is normal and the activity is within
@@ -335,28 +367,25 @@
                     list = new ArrayList<>(1);
                     list.add(ri);
                     PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
-                            mPm.mInjector.getCompatibility(), mPm.mComponentResolver,
-                            list, true, intent, resolvedType, filterCallingUid);
+                            mPlatformCompat, componentResolver, list, true, intent,
+                            resolvedType, filterCallingUid);
                 }
             }
         } else {
-            // reader
-            synchronized (mPm.mLock) {
-                String pkgName = intent.getPackage();
-                if (pkgName == null) {
-                    final List<ResolveInfo> result = mPm.mComponentResolver.queryReceivers(
-                            intent, resolvedType, flags, userId);
-                    if (result != null) {
-                        list = result;
-                    }
+            String pkgName = intent.getPackage();
+            if (pkgName == null) {
+                final List<ResolveInfo> result = componentResolver
+                        .queryReceivers(computer, intent, resolvedType, flags, userId);
+                if (result != null) {
+                    list = result;
                 }
-                final AndroidPackage pkg = mPm.mPackages.get(pkgName);
-                if (pkg != null) {
-                    final List<ResolveInfo> result = mPm.mComponentResolver.queryReceivers(
-                            intent, resolvedType, flags, pkg.getReceivers(), userId);
-                    if (result != null) {
-                        list = result;
-                    }
+            }
+            final AndroidPackage pkg = computer.getPackage(pkgName);
+            if (pkg != null) {
+                final List<ResolveInfo> result = componentResolver.queryReceivers(computer,
+                        intent, resolvedType, flags, pkg.getReceivers(), userId);
+                if (result != null) {
+                    list = result;
                 }
             }
         }
@@ -364,21 +393,22 @@
         if (originalIntent != null) {
             // We also have to ensure all components match the original intent
             PackageManagerServiceUtils.applyEnforceIntentFilterMatching(
-                    mPm.mInjector.getCompatibility(), mPm.mComponentResolver,
+                    mPlatformCompat, componentResolver,
                     list, true, originalIntent, resolvedType, filterCallingUid);
         }
 
-        return mPm.applyPostResolutionFilter(
-                list, instantAppPkgName, false, filterCallingUid, false, userId, intent);
+        return computer.applyPostResolutionFilter(list, instantAppPkgName, false, filterCallingUid,
+                false, userId, intent);
     }
 
 
-    public ResolveInfo resolveServiceInternal(Intent intent, String resolvedType,
-            @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
-        if (!mPm.mUserManager.exists(userId)) return null;
-        flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+    public ResolveInfo resolveServiceInternal(@NonNull Computer computer, Intent intent,
+            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
+            int callingUid) {
+        if (!mUserManager.exists(userId)) return null;
+        flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
                 false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
-        List<ResolveInfo> query = mPm.queryIntentServicesInternal(
+        List<ResolveInfo> query = computer.queryIntentServicesInternal(
                 intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
         if (query != null) {
             if (query.size() >= 1) {
@@ -391,12 +421,12 @@
     }
 
     public @NonNull List<ResolveInfo> queryIntentContentProvidersInternal(
-            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
-            int userId) {
-        if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
+            @NonNull Computer computer, Intent intent, String resolvedType,
+            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
+        if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
-        final String instantAppPkgName = mPm.getInstantAppPackageName(callingUid);
-        flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+        final String instantAppPkgName = computer.getInstantAppPackageName(callingUid);
+        flags = computer.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
                 false /* isImplicitImageCaptureIntentAndNotSetByDpc */);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
@@ -407,7 +437,7 @@
         }
         if (comp != null) {
             final List<ResolveInfo> list = new ArrayList<>(1);
-            final ProviderInfo pi = mPm.getProviderInfo(comp, flags, userId);
+            final ProviderInfo pi = computer.getProviderInfo(comp, flags, userId);
             if (pi != null) {
                 // When specifying an explicit component, we prevent the provider from being
                 // used when either 1) the provider is in an instant application and the
@@ -432,8 +462,8 @@
                                 || (matchVisibleToInstantAppOnly && isCallerInstantApp
                                 && isTargetHiddenFromInstantApp));
                 final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
-                        && mPm.shouldFilterApplication(
-                        mPm.getPackageStateInternal(pi.applicationInfo.packageName,
+                        && computer.shouldFilterApplication(
+                        computer.getPackageStateInternal(pi.applicationInfo.packageName,
                                 Process.SYSTEM_UID), callingUid, userId);
                 if (!blockResolution && !blockNormalResolution) {
                     final ResolveInfo ri = new ResolveInfo();
@@ -444,46 +474,40 @@
             return list;
         }
 
-        // reader
-        synchronized (mPm.mLock) {
-            String pkgName = intent.getPackage();
-            if (pkgName == null) {
-                final List<ResolveInfo> resolveInfos = mPm.mComponentResolver.queryProviders(intent,
-                        resolvedType, flags, userId);
-                if (resolveInfos == null) {
-                    return Collections.emptyList();
-                }
-                return applyPostContentProviderResolutionFilter(
-                        resolveInfos, instantAppPkgName, userId, callingUid);
+        final ComponentResolverApi componentResolver = computer.getComponentResolver();
+        String pkgName = intent.getPackage();
+        if (pkgName == null) {
+            final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer,
+                    intent, resolvedType, flags, userId);
+            if (resolveInfos == null) {
+                return Collections.emptyList();
             }
-            final AndroidPackage pkg = mPm.mPackages.get(pkgName);
-            if (pkg != null) {
-                final List<ResolveInfo> resolveInfos = mPm.mComponentResolver.queryProviders(intent,
-                        resolvedType, flags,
-                        pkg.getProviders(), userId);
-                if (resolveInfos == null) {
-                    return Collections.emptyList();
-                }
-                return applyPostContentProviderResolutionFilter(
-                        resolveInfos, instantAppPkgName, userId, callingUid);
-            }
-            return Collections.emptyList();
+            return applyPostContentProviderResolutionFilter(computer, resolveInfos,
+                    instantAppPkgName, userId, callingUid);
         }
+        final AndroidPackage pkg = computer.getPackage(pkgName);
+        if (pkg != null) {
+            final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer,
+                    intent, resolvedType, flags, pkg.getProviders(), userId);
+            if (resolveInfos == null) {
+                return Collections.emptyList();
+            }
+            return applyPostContentProviderResolutionFilter(computer, resolveInfos,
+                    instantAppPkgName, userId, callingUid);
+        }
+        return Collections.emptyList();
     }
 
-    private List<ResolveInfo> applyPostContentProviderResolutionFilter(
+    private List<ResolveInfo> applyPostContentProviderResolutionFilter(@NonNull Computer computer,
             List<ResolveInfo> resolveInfos, String instantAppPkgName,
             @UserIdInt int userId, int callingUid) {
         for (int i = resolveInfos.size() - 1; i >= 0; i--) {
             final ResolveInfo info = resolveInfos.get(i);
 
             if (instantAppPkgName == null) {
-                SettingBase callingSetting =
-                        mPm.mSettings.getSettingLPr(UserHandle.getAppId(callingUid));
-                PackageStateInternal resolvedSetting =
-                        mPm.getPackageStateInternal(info.providerInfo.packageName, 0);
-                if (!mPm.mAppsFilter.shouldFilterApplication(
-                        callingUid, callingSetting, resolvedSetting, userId)) {
+                PackageStateInternal resolvedSetting = computer.getPackageStateInternal(
+                        info.providerInfo.packageName, 0);
+                if (!computer.shouldFilterApplication(resolvedSetting, callingUid, userId)) {
                     continue;
                 }
             }
@@ -494,7 +518,7 @@
                 if (info.providerInfo.splitName != null
                         && !ArrayUtils.contains(info.providerInfo.applicationInfo.splitNames,
                         info.providerInfo.splitName)) {
-                    if (mPm.mInstantAppInstallerActivity == null) {
+                    if (mInstantAppInstallerActivitySupplier.get() == null) {
                         if (DEBUG_INSTANT) {
                             Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
                         }
@@ -507,7 +531,7 @@
                         Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
                     }
                     final ResolveInfo installerInfo = new ResolveInfo(
-                            mPm.getInstantAppInstallerInfo());
+                            computer.getInstantAppInstallerInfo());
                     installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
                             null /*failureActivity*/,
                             info.providerInfo.packageName,
@@ -531,20 +555,21 @@
         return resolveInfos;
     }
 
-    public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(ComponentName caller,
-            Intent[] specifics, String[] specificTypes, Intent intent,
+    public @NonNull List<ResolveInfo> queryIntentActivityOptionsInternal(Computer computer,
+            ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent,
             String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
-        if (!mPm.mUserManager.exists(userId)) return Collections.emptyList();
+        if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
-        flags = mPm.updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
-                mPm.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId, resolvedType,
-                        flags));
-        mPm.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
+        flags = computer.updateFlagsForResolve(flags, userId, callingUid,
+                false /*includeInstantApps*/,
+                computer.isImplicitImageCaptureIntentAndNotSetByDpcLocked(intent, userId,
+                        resolvedType, flags));
+        computer.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
                 false /*checkShell*/, "query intent activity options");
         final String resultsAction = intent.getAction();
 
-        final List<ResolveInfo> results = mPm.queryIntentActivitiesInternal(intent, resolvedType,
-                flags | PackageManager.GET_RESOLVED_FILTER, userId);
+        final List<ResolveInfo> results = computer.queryIntentActivitiesInternal(intent,
+                resolvedType, flags | PackageManager.GET_RESOLVED_FILTER, userId);
 
         if (DEBUG_INTENT_MATCHING) {
             Log.v(TAG, "Query " + intent + ": " + results);
@@ -584,21 +609,20 @@
 
                 ComponentName comp = sintent.getComponent();
                 if (comp == null) {
-                    ri = mPm.resolveIntent(
-                            sintent,
-                            specificTypes != null ? specificTypes[i] : null,
-                            flags, userId);
+                    ri = resolveIntentInternal(computer, sintent,
+                            specificTypes != null ? specificTypes[i] : null, flags,
+                            0 /*privateResolveFlags*/, userId, false, Binder.getCallingUid());
                     if (ri == null) {
                         continue;
                     }
-                    if (ri == mPm.getResolveInfo()) {
+                    if (ri == mResolveInfoSupplier.get()) {
                         // ACK!  Must do something better with this.
                     }
                     ai = ri.activityInfo;
                     comp = new ComponentName(ai.applicationInfo.packageName,
                             ai.name);
                 } else {
-                    ai = mPm.getActivityInfo(comp, flags, userId);
+                    ai = computer.getActivityInfo(comp, flags, userId);
                     if (ai == null) {
                         continue;
                     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4d7da1b..2ad35b7 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -120,6 +120,7 @@
 import com.android.server.pm.pkg.component.ParsedPermission;
 import com.android.server.pm.pkg.component.ParsedProcess;
 import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.resolution.ComponentResolver;
 import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
 import com.android.server.pm.verify.domain.DomainVerificationPersistence;
@@ -1551,7 +1552,7 @@
                 if (pa.mPref.getParseError() == null) {
                     final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId);
                     if (resolver.shouldAddPreferredActivity(pa)) {
-                        resolver.addFilter(pa);
+                        resolver.addFilter(null, pa);
                     }
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -1579,7 +1580,7 @@
             String tagName = parser.getName();
             if (tagName.equals(TAG_ITEM)) {
                 PersistentPreferredActivity ppa = new PersistentPreferredActivity(parser);
-                editPersistentPreferredActivitiesLPw(userId).addFilter(ppa);
+                editPersistentPreferredActivitiesLPw(userId).addFilter(null, ppa);
             } else {
                 PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Unknown element under <" + TAG_PERSISTENT_PREFERRED_ACTIVITIES + ">: "
@@ -1601,7 +1602,7 @@
             final String tagName = parser.getName();
             if (tagName.equals(TAG_ITEM)) {
                 CrossProfileIntentFilter cpif = new CrossProfileIntentFilter(parser);
-                editCrossProfileIntentResolverLPw(userId).addFilter(cpif);
+                editCrossProfileIntentResolverLPw(userId).addFilter(null, cpif);
             } else {
                 String msg = "Unknown element under " +  TAG_CROSS_PROFILE_INTENT_FILTERS + ": " +
                         tagName;
@@ -1868,8 +1869,7 @@
                                 .build();
                     }
                     if (suspended && suspendParamsMap == null) {
-                        final SuspendParams suspendParams =
-                                SuspendParams.getInstanceOrNull(
+                        final SuspendParams suspendParams = new SuspendParams(
                                         oldSuspendDialogInfo,
                                         suspendedAppExtras,
                                         suspendedLauncherExtras);
@@ -3517,7 +3517,7 @@
                 removeFilters(pir, filter, existing);
             }
             PreferredActivity pa = new PreferredActivity(filter, systemMatch, set, cn, true);
-            pir.addFilter(pa);
+            pir.addFilter(null, pa);
         } else if (haveNonSys == null) {
             StringBuilder sb = new StringBuilder();
             sb.append("No component ");
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index 85d1367..bd1c9c7 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -112,7 +112,7 @@
         }
 
         final SuspendParams newSuspendParams =
-                SuspendParams.getInstanceOrNull(dialogInfo, appExtras, launcherExtras);
+                new SuspendParams(dialogInfo, appExtras, launcherExtras);
 
         final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final IntArray changedUids = new IntArray(packageNames.length);
@@ -148,13 +148,12 @@
 
                 final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
                         packageState.getUserStateOrDefault(userId).getSuspendParams();
-                final SuspendParams suspendParams = suspendParamsMap == null
-                        ? null : suspendParamsMap.get(packageName);
-                boolean hasSuspension = suspendParams != null;
                 if (suspended) {
-                    if (hasSuspension) {
+                    if (suspendParamsMap != null && suspendParamsMap.containsKey(packageName)) {
+                        final SuspendParams suspendParams = suspendParamsMap.get(packageName);
                         // Skip if there's no changes
-                        if (Objects.equals(suspendParams.getDialogInfo(), dialogInfo)
+                        if (suspendParams != null
+                                && Objects.equals(suspendParams.getDialogInfo(), dialogInfo)
                                 && Objects.equals(suspendParams.getAppExtras(), appExtras)
                                 && Objects.equals(suspendParams.getLauncherExtras(),
                                 launcherExtras)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5c4d011..b7c55c5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -36,6 +36,7 @@
 import android.app.IStopUserCallback;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
+import android.app.StatsManager;
 import android.app.admin.DevicePolicyEventLogger;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
@@ -98,6 +99,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import android.util.StatsEvent;
 import android.util.TimeUtils;
 import android.util.TypedValue;
 import android.util.TypedXmlPullParser;
@@ -608,6 +610,8 @@
                 if (mUms.mPm.isDeviceUpgrading()) {
                     mUms.cleanupPreCreatedUsers();
                 }
+
+                mUms.registerStatsCallbacks();
             }
         }
 
@@ -4279,6 +4283,56 @@
                         : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE);
     }
 
+    /** Register callbacks for statsd pulled atoms. */
+    private void registerStatsCallbacks() {
+        final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+        statsManager.setPullAtomCallback(
+                FrameworkStatsLog.USER_INFO,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                this::onPullAtom);
+    }
+
+    /** Writes a UserInfo pulled atom for each user on the device. */
+    private int onPullAtom(int atomTag, List<StatsEvent> data) {
+        if (atomTag != FrameworkStatsLog.USER_INFO) {
+            Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
+            return android.app.StatsManager.PULL_SKIP;
+        }
+        final List<UserInfo> users = getUsersInternal(true, true, true);
+        final int size = users.size();
+        for (int idx = 0; idx < size; idx++) {
+            final UserInfo user = users.get(idx);
+            if (user.id == UserHandle.USER_SYSTEM) {
+                // Skip user 0. It's not interesting. We already know it exists, is running, and (if
+                // we know the device configuration) its userType.
+                continue;
+            }
+
+            final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
+            final String userTypeCustom = (userTypeStandard ==
+                    FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN) ?
+                    user.userType : null;
+
+            boolean isUserRunningUnlocked;
+            synchronized (mUserStates) {
+                isUserRunningUnlocked =
+                        mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+            }
+
+            data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
+                    user.id,
+                    userTypeStandard,
+                    userTypeCustom,
+                    user.flags,
+                    user.creationTime,
+                    user.lastLoggedInTime,
+                    isUserRunningUnlocked
+            ));
+        }
+        return android.app.StatsManager.PULL_SUCCESS;
+    }
+
     @VisibleForTesting
     UserData putUserInfo(UserInfo userInfo) {
         final UserData userData = new UserData();
diff --git a/services/core/java/com/android/server/pm/UserNeedsBadgingCache.java b/services/core/java/com/android/server/pm/UserNeedsBadgingCache.java
new file mode 100644
index 0000000..90c9228
--- /dev/null
+++ b/services/core/java/com/android/server/pm/UserNeedsBadgingCache.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+public class UserNeedsBadgingCache {
+
+    private final Object mLock = new Object();
+
+    // Cache of users who need badging.
+    @GuardedBy("mLock")
+    @NonNull
+    private final SparseBooleanArray mUserCache = new SparseBooleanArray();
+
+    @NonNull
+    private final UserManagerService mUserManager;
+
+    public UserNeedsBadgingCache(@NonNull UserManagerService userManager) {
+        mUserManager = userManager;
+    }
+
+    public void delete(@UserIdInt int userId) {
+        synchronized (mLock) {
+            mUserCache.delete(userId);
+        }
+    }
+
+    public boolean get(@UserIdInt int userId) {
+        synchronized (mLock) {
+            int index = mUserCache.indexOfKey(userId);
+            if (index >= 0) {
+                return mUserCache.valueAt(index);
+            }
+        }
+
+        final UserInfo userInfo;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            userInfo = mUserManager.getUserInfo(userId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        final boolean b;
+        b = userInfo != null && userInfo.isManagedProfile();
+        synchronized (mLock) {
+            mUserCache.put(userId, b);
+        }
+        return b;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/WatchedIntentResolver.java b/services/core/java/com/android/server/pm/WatchedIntentResolver.java
index 1c3d884..daa50ab 100644
--- a/services/core/java/com/android/server/pm/WatchedIntentResolver.java
+++ b/services/core/java/com/android/server/pm/WatchedIntentResolver.java
@@ -18,8 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.IntentFilter;
 
 import com.android.server.IntentResolver;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.Watchable;
 import com.android.server.utils.WatchableImpl;
@@ -99,8 +101,8 @@
     }
 
     @Override
-    public void addFilter(F f) {
-        super.addFilter(f);
+    public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
+        super.addFilter(snapshot, f);
         f.registerObserver(mWatcher);
         onChanged();
     }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index ed47bfb7..87494a6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -3364,7 +3364,7 @@
             // For updated system applications, a privileged/oem permission
             // is granted only if it had been defined by the original application.
             if (pkgSetting.getTransientState().isUpdatedSystemApp()) {
-                final PackageSetting disabledPs = mPackageManagerInt
+                final PackageStateInternal disabledPs = mPackageManagerInt
                         .getDisabledSystemPackage(pkg.getPackageName());
                 final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.getPkg();
                 if (disabledPkg != null
@@ -3762,7 +3762,7 @@
             return;
         }
 
-        PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
+        PackageStateInternal disabledPs = mPackageManagerInt.getDisabledSystemPackage(
                 pkg.getPackageName());
         boolean isShadowingSystemPkg = disabledPs != null && disabledPs.getAppId() == pkg.getUid();
 
@@ -4104,7 +4104,7 @@
     }
 
     private boolean isPermissionDeclaredByDisabledSystemPkg(@NonNull Permission permission) {
-        final PackageSetting disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage(
+        final PackageStateInternal disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage(
                     permission.getPackageName());
         if (disabledSourcePs != null && disabledSourcePs.getPkg() != null) {
             final String permissionName = permission.getName();
diff --git a/services/core/java/com/android/server/pm/pkg/SuspendParams.java b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
index d24ce96..0926ba2 100644
--- a/services/core/java/com/android/server/pm/pkg/SuspendParams.java
+++ b/services/core/java/com/android/server/pm/pkg/SuspendParams.java
@@ -46,27 +46,13 @@
     private final PersistableBundle appExtras;
     private final PersistableBundle launcherExtras;
 
-    private SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
+    public SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
             PersistableBundle launcherExtras) {
         this.dialogInfo = dialogInfo;
         this.appExtras = appExtras;
         this.launcherExtras = launcherExtras;
     }
 
-    /**
-     * Returns a {@link SuspendParams} object with the given fields. Returns {@code null} if all
-     * the fields are {@code null}.
-     *
-     * @return A {@link SuspendParams} object or {@code null}.
-     */
-    public static SuspendParams getInstanceOrNull(SuspendDialogInfo dialogInfo,
-            PersistableBundle appExtras, PersistableBundle launcherExtras) {
-        if (dialogInfo == null && appExtras == null && launcherExtras == null) {
-            return null;
-        }
-        return new SuspendParams(dialogInfo, appExtras, launcherExtras);
-    }
-
     @Override
     public boolean equals(@Nullable Object obj) {
         if (this == obj) {
@@ -170,7 +156,7 @@
             Slog.e(LOG_TAG, "Exception while trying to parse SuspendParams,"
                     + " some fields may default", e);
         }
-        return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
+        return new SuspendParams(readDialogInfo, readAppExtras, readLauncherExtras);
     }
 
     public SuspendDialogInfo getDialogInfo() {
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
similarity index 70%
rename from services/core/java/com/android/server/pm/ComponentResolver.java
rename to services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index aa393d2..cf9370d 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2022 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,16 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.server.pm;
+package com.android.server.pm.resolution;
 
 import static android.content.pm.PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
-import static android.content.pm.PackageManagerInternal.PACKAGE_SETUP_WIZARD;
 
 import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -32,11 +32,9 @@
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.InstantAppResolveInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
@@ -46,14 +44,18 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.IntentResolver;
+import com.android.server.pm.Computer;
+import com.android.server.pm.DumpState;
+import com.android.server.pm.PackageManagerException;
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.UserNeedsBadgingCache;
 import com.android.server.pm.parsing.PackageInfoUtils;
-import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageStateUtils;
 import com.android.server.pm.pkg.PackageUserStateInternal;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ParsedActivity;
@@ -63,12 +65,13 @@
 import com.android.server.pm.pkg.component.ParsedProvider;
 import com.android.server.pm.pkg.component.ParsedProviderImpl;
 import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.utils.Snappable;
 import com.android.server.utils.SnapshotCache;
-import com.android.server.utils.WatchableImpl;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
@@ -79,9 +82,7 @@
 import java.util.function.Function;
 
 /** Resolves all Android component types [activities, services, providers and receivers]. */
-public class ComponentResolver
-        extends WatchableImpl
-        implements Snappable {
+public class ComponentResolver extends ComponentResolverLocked implements Snappable {
     private static final boolean DEBUG = false;
     private static final String TAG = "PackageManager";
     private static final boolean DEBUG_FILTERS = false;
@@ -104,7 +105,7 @@
         PROTECTED_ACTIONS.add(Intent.ACTION_VIEW);
     }
 
-    static final Comparator<ResolveInfo> RESOLVE_PRIORITY_SORTER = (r1, r2) -> {
+    public static final Comparator<ResolveInfo> RESOLVE_PRIORITY_SORTER = (r1, r2) -> {
         int v1 = r1.priority;
         int v2 = r2.priority;
         //System.out.println("Comparing: q1=" + q1 + " q2=" + q2);
@@ -140,62 +141,8 @@
         return 0;
     };
 
-    private static UserManagerService sUserManager;
-    private static PackageManagerInternal sPackageManagerInternal;
-
-    /**
-     * Locking within package manager is going to get worse before it gets better. Currently,
-     * we need to share the {@link PackageManagerService} lock to prevent deadlocks. This occurs
-     * because in order to safely query the resolvers, we need to obtain this lock. However,
-     * during resolution, we call into the {@link PackageManagerService}. This is _not_ to
-     * operate on data controlled by the service proper, but, to check the state of package
-     * settings [contained in a {@link Settings} object]. However, the {@link Settings} object
-     * happens to be protected by the main {@link PackageManagerService} lock.
-     * <p>
-     * There are a couple potential solutions.
-     * <ol>
-     * <li>Split all of our locks into reader/writer locks. This would allow multiple,
-     * simultaneous read operations and means we don't have to be as cautious about lock
-     * layering. Only when we want to perform a write operation will we ever be in a
-     * position to deadlock the system.</li>
-     * <li>Use the same lock across all classes within the {@code com.android.server.pm}
-     * package. By unifying the lock object, we remove any potential lock layering issues
-     * within the package manager. However, we already have a sense that this lock is
-     * heavily contended and merely adding more dependencies on it will have further
-     * impact.</li>
-     * <li>Implement proper lock ordering within the package manager. By defining the
-     * relative layer of the component [eg. {@link PackageManagerService} is at the top.
-     * Somewhere in the middle would be {@link ComponentResolver}. At the very bottom
-     * would be {@link Settings}.] The ordering would allow higher layers to hold their
-     * lock while calling down. Lower layers must relinquish their lock before calling up.
-     * Since {@link Settings} would live at the lowest layer, the {@link ComponentResolver}
-     * would be able to hold its lock while checking the package setting state.</li>
-     * </ol>
-     */
-    private final PackageManagerTracedLock mLock;
-
-    /** All available activities, for your resolving pleasure. */
-    @GuardedBy("mLock")
-    private final ActivityIntentResolver mActivities;
-
-    /** All available providers, for your resolving pleasure. */
-    @GuardedBy("mLock")
-    private final ProviderIntentResolver mProviders;
-
-    /** All available receivers, for your resolving pleasure. */
-    @GuardedBy("mLock")
-    private final ReceiverIntentResolver mReceivers;
-
-    /** All available services, for your resolving pleasure. */
-    @GuardedBy("mLock")
-    private final ServiceIntentResolver mServices;
-
-    /** Mapping from provider authority [first directory in content URI codePath) to provider. */
-    @GuardedBy("mLock")
-    private final ArrayMap<String, ParsedProvider> mProvidersByAuthority;
-
     /** Whether or not processing protected filters should be deferred. */
-    private boolean mDeferProtectedFilters = true;
+    boolean mDeferProtectedFilters = true;
 
     /**
      * Tracks high priority intent filters for protected actions. During boot, certain
@@ -210,348 +157,62 @@
      * This is a pair of component package name to actual filter, because we don't store the
      * name inside the filter. It's technically independent of the component it's contained in.
      */
-    private List<Pair<ParsedMainComponent, ParsedIntentInfo>> mProtectedFilters;
+    List<Pair<ParsedMainComponent, ParsedIntentInfo>> mProtectedFilters;
 
-    ComponentResolver(UserManagerService userManager,
-            PackageManagerInternal packageManagerInternal,
-            PackageManagerTracedLock lock) {
-        sPackageManagerInternal = packageManagerInternal;
-        sUserManager = userManager;
-        mLock = lock;
-
-        mActivities = new ActivityIntentResolver();
-        mProviders = new ProviderIntentResolver();
-        mReceivers = new ReceiverIntentResolver();
-        mServices = new ServiceIntentResolver();
+    public ComponentResolver(@NonNull UserManagerService userManager,
+            @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+        super(userManager);
+        mActivities = new ActivityIntentResolver(userManager, userNeedsBadgingCache);
+        mProviders = new ProviderIntentResolver(userManager);
+        mReceivers = new ReceiverIntentResolver(userManager, userNeedsBadgingCache);
+        mServices = new ServiceIntentResolver(userManager);
         mProvidersByAuthority = new ArrayMap<>();
         mDeferProtectedFilters = true;
 
-        mSnapshot = new SnapshotCache<ComponentResolver>(this, this) {
+        mSnapshot = new SnapshotCache<ComponentResolverApi>(this, this) {
                 @Override
-                public ComponentResolver createSnapshot() {
-                    return new ComponentResolver(mSource);
+                public ComponentResolverApi createSnapshot() {
+                    return new ComponentResolverSnapshot(ComponentResolver.this,
+                            userNeedsBadgingCache);
                 }};
     }
 
-    // Copy constructor used in creating snapshots.
-    private ComponentResolver(ComponentResolver orig) {
-        // Do not set the static variables that are set in the default constructor.   Do
-        // create a new object for the lock.  The snapshot is read-only, so a lock is not
-        // strictly required.  However, the current code is simpler if the lock exists,
-        // but does not contend with any outside class.
-        // TODO: make the snapshot lock-free
-        mLock = new PackageManagerTracedLock();
-
-        mActivities = new ActivityIntentResolver(orig.mActivities);
-        mProviders = new ProviderIntentResolver(orig.mProviders);
-        mReceivers = new ReceiverIntentResolver(orig.mReceivers);
-        mServices = new ServiceIntentResolver(orig.mServices);
-        mProvidersByAuthority = new ArrayMap<>(orig.mProvidersByAuthority);
-        mDeferProtectedFilters = orig.mDeferProtectedFilters;
-        mProtectedFilters = (mProtectedFilters == null)
-                            ? null
-                            : new ArrayList<>(orig.mProtectedFilters);
-
-        mSnapshot = null;
-    }
-
-    final SnapshotCache<ComponentResolver> mSnapshot;
+    final SnapshotCache<ComponentResolverApi> mSnapshot;
 
     /**
      * Create a snapshot.
      */
-    public ComponentResolver snapshot() {
+    public ComponentResolverApi snapshot() {
         return mSnapshot.snapshot();
     }
 
-
-    /** Returns the given activity */
-    @Nullable
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public ParsedActivity getActivity(@NonNull ComponentName component) {
-        synchronized (mLock) {
-            return mActivities.mActivities.get(component);
-        }
-    }
-
-    /** Returns the given provider */
-    @Nullable
-    ParsedProvider getProvider(@NonNull ComponentName component) {
-        synchronized (mLock) {
-            return mProviders.mProviders.get(component);
-        }
-    }
-
-    /** Returns the given receiver */
-    @Nullable
-    ParsedActivity getReceiver(@NonNull ComponentName component) {
-        synchronized (mLock) {
-            return mReceivers.mActivities.get(component);
-        }
-    }
-
-    /** Returns the given service */
-    @Nullable
-    ParsedService getService(@NonNull ComponentName component) {
-        synchronized (mLock) {
-            return mServices.mServices.get(component);
-        }
-    }
-
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean componentExists(@NonNull ComponentName componentName) {
-        synchronized (mLock) {
-            ParsedMainComponent component = mActivities.mActivities.get(componentName);
-            if (component != null) {
-                return true;
-            }
-            component = mReceivers.mActivities.get(componentName);
-            if (component != null) {
-                return true;
-            }
-            component = mServices.mServices.get(componentName);
-            if (component != null) {
-                return true;
-            }
-            return mProviders.mProviders.get(componentName) != null;
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryActivities(Intent intent, String resolvedType, long flags,
-            int userId) {
-        synchronized (mLock) {
-            return mActivities.queryIntent(intent, resolvedType, flags, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryActivities(Intent intent, String resolvedType, long flags,
-            List<ParsedActivity> activities, int userId) {
-        synchronized (mLock) {
-            return mActivities.queryIntentForPackage(
-                    intent, resolvedType, flags, activities, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryProviders(Intent intent, String resolvedType, long flags, int userId) {
-        synchronized (mLock) {
-            return mProviders.queryIntent(intent, resolvedType, flags, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryProviders(Intent intent, String resolvedType, long flags,
-            List<ParsedProvider> providers, int userId) {
-        synchronized (mLock) {
-            return mProviders.queryIntentForPackage(intent, resolvedType, flags, providers, userId);
-        }
-    }
-
-    @Nullable
-    List<ProviderInfo> queryProviders(String processName, String metaDataKey, int uid, long flags,
-            int userId) {
-        if (!sUserManager.exists(userId)) {
-            return null;
-        }
-        List<ProviderInfo> providerList = null;
-        CachedApplicationInfoGenerator appInfoGenerator = null;
-        synchronized (mLock) {
-            for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
-                final ParsedProvider p = mProviders.mProviders.valueAt(i);
-                if (p.getAuthority() == null) {
-                    continue;
-                }
-
-                final PackageStateInternal ps =
-                        sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
-                if (ps == null) {
-                    continue;
-                }
-
-                AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
-                if (pkg == null) {
-                    continue;
-                }
-
-                if (processName != null && (!p.getProcessName().equals(processName)
-                        || !UserHandle.isSameApp(pkg.getUid(), uid))) {
-                    continue;
-                }
-                // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter.
-                if (metaDataKey != null && !p.getMetaData().containsKey(metaDataKey)) {
-                    continue;
-                }
-                if (appInfoGenerator == null) {
-                    appInfoGenerator = new CachedApplicationInfoGenerator();
-                }
-                final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
-                final ApplicationInfo appInfo =
-                        appInfoGenerator.generate(pkg, flags, state, userId, ps);
-                if (appInfo == null) {
-                    continue;
-                }
-
-                final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
-                        pkg, p, flags, state, appInfo, userId, ps);
-                if (info == null) {
-                    continue;
-                }
-                if (providerList == null) {
-                    providerList = new ArrayList<>(i + 1);
-                }
-                providerList.add(info);
-            }
-        }
-        return providerList;
-    }
-
-    @Nullable
-    ProviderInfo queryProvider(String authority, long flags, int userId) {
-        synchronized (mLock) {
-            final ParsedProvider p = mProvidersByAuthority.get(authority);
-            if (p == null) {
-                return null;
-            }
-            final PackageStateInternal ps =
-                    sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
-            if (ps == null) {
-                return null;
-            }
-            final AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
-            if (pkg == null) {
-                return null;
-            }
-            final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
-            ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
-                    pkg, flags, state, userId, ps);
-            if (appInfo == null) {
-                return null;
-            }
-            return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId, ps);
-        }
-    }
-
-    void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo, boolean safeMode,
-            int userId) {
-        synchronized (mLock) {
-            CachedApplicationInfoGenerator appInfoGenerator = null;
-            for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
-                final ParsedProvider p = mProvidersByAuthority.valueAt(i);
-                if (!p.isSyncable()) {
-                    continue;
-                }
-
-                final PackageStateInternal ps =
-                        sPackageManagerInternal.getPackageStateInternal(p.getPackageName());
-                if (ps == null) {
-                    continue;
-                }
-
-                final AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
-                if (pkg == null) {
-                    continue;
-                }
-
-                if (safeMode && !pkg.isSystem()) {
-                    continue;
-                }
-                if (appInfoGenerator == null) {
-                    appInfoGenerator = new CachedApplicationInfoGenerator();
-                }
-                final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
-                final ApplicationInfo appInfo =
-                        appInfoGenerator.generate(pkg, 0, state, userId, ps);
-                if (appInfo == null) {
-                    continue;
-                }
-
-                final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
-                        pkg, p, 0, state, appInfo, userId, ps);
-                if (info == null) {
-                    continue;
-                }
-                outNames.add(mProvidersByAuthority.keyAt(i));
-                outInfo.add(info);
-            }
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, long flags, int userId) {
-        synchronized (mLock) {
-            return mReceivers.queryIntent(intent, resolvedType, flags, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryReceivers(Intent intent, String resolvedType, long flags,
-            List<ParsedActivity> receivers, int userId) {
-        synchronized (mLock) {
-            return mReceivers.queryIntentForPackage(intent, resolvedType, flags, receivers, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryServices(Intent intent, String resolvedType, long flags, int userId) {
-        synchronized (mLock) {
-            return mServices.queryIntent(intent, resolvedType, flags, userId);
-        }
-    }
-
-    @Nullable
-    List<ResolveInfo> queryServices(Intent intent, String resolvedType, long flags,
-            List<ParsedService> services, int userId) {
-        synchronized (mLock) {
-            return mServices.queryIntentForPackage(intent, resolvedType, flags, services, userId);
-        }
-    }
-
-    /** Returns {@code true} if the given activity is defined by some package */
-    boolean isActivityDefined(ComponentName component) {
-        synchronized (mLock) {
-            return mActivities.mActivities.get(component) != null;
-        }
-    }
-
-    /** Asserts none of the providers defined in the given package haven't already been defined. */
-    void assertProvidersNotDefined(AndroidPackage pkg) throws PackageManagerException {
-        synchronized (mLock) {
-            assertProvidersNotDefinedLocked(pkg);
-        }
-    }
-
     /** Add all components defined in the given package to the internal structures. */
-    void addAllComponents(AndroidPackage pkg, boolean chatty) {
+    public void addAllComponents(AndroidPackage pkg, boolean chatty,
+            @Nullable String setupWizardPackage, @NonNull Computer computer) {
         final ArrayList<Pair<ParsedActivity, ParsedIntentInfo>> newIntents = new ArrayList<>();
         synchronized (mLock) {
-            addActivitiesLocked(pkg, newIntents, chatty);
-            addReceiversLocked(pkg, chatty);
-            addProvidersLocked(pkg, chatty);
-            addServicesLocked(pkg, chatty);
+            addActivitiesLocked(computer, pkg, newIntents, chatty);
+            addReceiversLocked(computer, pkg, chatty);
+            addProvidersLocked(computer, pkg, chatty);
+            addServicesLocked(computer, pkg, chatty);
             onChanged();
         }
-        // expect single setupwizard package
-        final String setupWizardPackage = ArrayUtils.firstOrNull(
-                sPackageManagerInternal.getKnownPackageNames(
-                        PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
 
         for (int i = newIntents.size() - 1; i >= 0; --i) {
             final Pair<ParsedActivity, ParsedIntentInfo> pair = newIntents.get(i);
-            final PackageSetting disabledPkgSetting = (PackageSetting) sPackageManagerInternal
+            final PackageStateInternal disabledPkgSetting = computer
                     .getDisabledSystemPackage(pair.first.getPackageName());
             final AndroidPackage disabledPkg =
                     disabledPkgSetting == null ? null : disabledPkgSetting.getPkg();
             final List<ParsedActivity> systemActivities =
                     disabledPkg != null ? disabledPkg.getActivities() : null;
-            adjustPriority(systemActivities, pair.first, pair.second, setupWizardPackage);
+            adjustPriority(computer, systemActivities, pair.first, pair.second, setupWizardPackage);
             onChanged();
         }
     }
 
     /** Removes all components defined in the given package from the internal structures. */
-    void removeAllComponents(AndroidPackage pkg, boolean chatty) {
+    public void removeAllComponents(AndroidPackage pkg, boolean chatty) {
         synchronized (mLock) {
             removeAllComponentsLocked(pkg, chatty);
             onChanged();
@@ -562,7 +223,7 @@
      * Reprocess any protected filters that have been deferred. At this point, we've scanned
      * all of the filters defined on the /system partition and know the special components.
      */
-    void fixProtectedFilterPriorities() {
+    public void fixProtectedFilterPriorities(@Nullable String setupWizardPackage) {
         synchronized (mLock) {
             if (!mDeferProtectedFilters) {
                 return;
@@ -576,11 +237,6 @@
                     mProtectedFilters;
             mProtectedFilters = null;
 
-            // expect single setupwizard package
-            final String setupWizardPackage = ArrayUtils.firstOrNull(
-                sPackageManagerInternal.getKnownPackageNames(
-                    PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
-
             if (DEBUG_FILTERS && setupWizardPackage == null) {
                 Slog.i(TAG, "No setup wizard;"
                         + " All protected intents capped to priority 0");
@@ -615,7 +271,7 @@
         }
     }
 
-    void dumpActivityResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpActivityResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
         synchronized (mLock) {
             if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
                             : "Activity Resolver Table:", "  ", packageName,
@@ -625,7 +281,7 @@
         }
     }
 
-    void dumpProviderResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpProviderResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
         synchronized (mLock) {
             if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
                             : "Provider Resolver Table:", "  ", packageName,
@@ -635,7 +291,7 @@
         }
     }
 
-    void dumpReceiverResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpReceiverResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
         synchronized (mLock) {
             if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
                             : "Receiver Resolver Table:", "  ", packageName,
@@ -645,7 +301,7 @@
         }
     }
 
-    void dumpServiceResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpServiceResolvers(PrintWriter pw, DumpState dumpState, String packageName) {
         synchronized (mLock) {
             if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
                             : "Service Resolver Table:", "  ", packageName,
@@ -655,7 +311,8 @@
         }
     }
 
-    void dumpContentProviders(PrintWriter pw, DumpState dumpState, String packageName) {
+    public void dumpContentProviders(@NonNull Computer computer, PrintWriter pw,
+            DumpState dumpState, String packageName) {
         synchronized (mLock) {
             boolean printedSomething = false;
             for (ParsedProvider p : mProviders.mProviders.values()) {
@@ -695,7 +352,7 @@
                 pw.print("    ");
                 pw.println(p.toString());
 
-                AndroidPackage pkg = sPackageManagerInternal.getPackage(p.getPackageName());
+                AndroidPackage pkg = computer.getPackage(p.getPackageName());
 
                 if (pkg != null) {
                     pw.print("      applicationInfo=");
@@ -705,7 +362,7 @@
         }
     }
 
-    void dumpServicePermissions(PrintWriter pw, DumpState dumpState) {
+    public void dumpServicePermissions(PrintWriter pw, DumpState dumpState) {
         synchronized (mLock) {
             if (dumpState.onTitlePrinted()) pw.println();
             pw.println("Service permissions:");
@@ -728,13 +385,13 @@
     }
 
     @GuardedBy("mLock")
-    private void addActivitiesLocked(AndroidPackage pkg,
+    private void addActivitiesLocked(@NonNull Computer computer, AndroidPackage pkg,
             List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents, boolean chatty) {
         final int activitiesSize = ArrayUtils.size(pkg.getActivities());
         StringBuilder r = null;
         for (int i = 0; i < activitiesSize; i++) {
             ParsedActivity a = pkg.getActivities().get(i);
-            mActivities.addActivity(a, "activity", newIntents);
+            mActivities.addActivity(computer, a, "activity", newIntents);
             if (DEBUG_PACKAGE_SCANNING && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
@@ -750,12 +407,12 @@
     }
 
     @GuardedBy("mLock")
-    private void addProvidersLocked(AndroidPackage pkg, boolean chatty) {
+    private void addProvidersLocked(@NonNull Computer computer, AndroidPackage pkg, boolean chatty) {
         final int providersSize = ArrayUtils.size(pkg.getProviders());
         StringBuilder r = null;
         for (int i = 0; i < providersSize; i++) {
             ParsedProvider p = pkg.getProviders().get(i);
-            mProviders.addProvider(p);
+            mProviders.addProvider(computer, p);
             if (p.getAuthority() != null) {
                 String[] names = p.getAuthority().split(";");
 
@@ -814,12 +471,12 @@
     }
 
     @GuardedBy("mLock")
-    private void addReceiversLocked(AndroidPackage pkg, boolean chatty) {
+    private void addReceiversLocked(@NonNull Computer computer, AndroidPackage pkg, boolean chatty) {
         final int receiversSize = ArrayUtils.size(pkg.getReceivers());
         StringBuilder r = null;
         for (int i = 0; i < receiversSize; i++) {
             ParsedActivity a = pkg.getReceivers().get(i);
-            mReceivers.addActivity(a, "receiver", null);
+            mReceivers.addActivity(computer, a, "receiver", null);
             if (DEBUG_PACKAGE_SCANNING && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
@@ -835,12 +492,12 @@
     }
 
     @GuardedBy("mLock")
-    private void addServicesLocked(AndroidPackage pkg, boolean chatty) {
+    private void addServicesLocked(@NonNull Computer computer, AndroidPackage pkg, boolean chatty) {
         final int servicesSize = ArrayUtils.size(pkg.getServices());
         StringBuilder r = null;
         for (int i = 0; i < servicesSize; i++) {
             ParsedService s = pkg.getServices().get(i);
-            mServices.addService(s);
+            mServices.addService(computer, s);
             if (DEBUG_PACKAGE_SCANNING && chatty) {
                 if (r == null) {
                     r = new StringBuilder(256);
@@ -945,8 +602,8 @@
      * <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is
      * allowed to obtain any priority on any action.
      */
-    private void adjustPriority(List<ParsedActivity> systemActivities, ParsedActivity activity,
-            ParsedIntentInfo intentInfo, String setupWizardPackage) {
+    private void adjustPriority(@NonNull Computer computer, List<ParsedActivity> systemActivities,
+            ParsedActivity activity, ParsedIntentInfo intentInfo, String setupWizardPackage) {
         // nothing to do; priority is fine as-is
         IntentFilter intentFilter = intentInfo.getIntentFilter();
         if (intentFilter.getPriority() <= 0) {
@@ -954,7 +611,7 @@
         }
 
         String packageName = activity.getPackageName();
-        AndroidPackage pkg = sPackageManagerInternal.getPackage(packageName);
+        AndroidPackage pkg = computer.getPackage(packageName);
 
         final boolean privilegedApp = pkg.isPrivileged();
         String className = activity.getClassName();
@@ -1231,28 +888,30 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void assertProvidersNotDefinedLocked(AndroidPackage pkg)
+    /** Asserts none of the providers defined in the given package haven't already been defined. */
+    public void assertProvidersNotDefined(@NonNull AndroidPackage pkg)
             throws PackageManagerException {
-        final int providersSize = ArrayUtils.size(pkg.getProviders());
-        int i;
-        for (i = 0; i < providersSize; i++) {
-            ParsedProvider p = pkg.getProviders().get(i);
-            if (p.getAuthority() != null) {
-                final String[] names = p.getAuthority().split(";");
-                for (int j = 0; j < names.length; j++) {
-                    if (mProvidersByAuthority.containsKey(names[j])) {
-                        final ParsedProvider other = mProvidersByAuthority.get(names[j]);
-                        final String otherPackageName =
-                                (other != null && other.getComponentName() != null)
-                                        ? other.getComponentName().getPackageName() : "?";
-                        // if we're installing over the same already-installed package, this is ok
-                        if (!otherPackageName.equals(pkg.getPackageName())) {
-                            throw new PackageManagerException(
-                                    INSTALL_FAILED_CONFLICTING_PROVIDER,
-                                    "Can't install because provider name " + names[j]
-                                            + " (in package " + pkg.getPackageName()
-                                            + ") is already used by " + otherPackageName);
+        synchronized (mLock) {
+            final int providersSize = ArrayUtils.size(pkg.getProviders());
+            int i;
+            for (i = 0; i < providersSize; i++) {
+                ParsedProvider p = pkg.getProviders().get(i);
+                if (p.getAuthority() != null) {
+                    final String[] names = p.getAuthority().split(";");
+                    for (int j = 0; j < names.length; j++) {
+                        if (mProvidersByAuthority.containsKey(names[j])) {
+                            final ParsedProvider other = mProvidersByAuthority.get(names[j]);
+                            final String otherPackageName =
+                                    (other != null && other.getComponentName() != null)
+                                            ? other.getComponentName().getPackageName() : "?";
+                            // if installing over the same already-installed package,this is ok
+                            if (!otherPackageName.equals(pkg.getPackageName())) {
+                                throw new PackageManagerException(
+                                        INSTALL_FAILED_CONFLICTING_PROVIDER,
+                                        "Can't install because provider name " + names[j]
+                                                + " (in package " + pkg.getPackageName()
+                                                + ") is already used by " + otherPackageName);
+                            }
                         }
                     }
                 }
@@ -1266,22 +925,31 @@
         private final ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>();
         private boolean mIsUpdatingMimeGroup = false;
 
+        @NonNull
+        protected final UserManagerService mUserManager;
+
         // Default constructor
-        MimeGroupsAwareIntentResolver() {
+        MimeGroupsAwareIntentResolver(@NonNull UserManagerService userManager) {
+            mUserManager = userManager;
         }
 
         // Copy constructor used in creating snapshots
-        MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig) {
+        MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig,
+                @NonNull UserManagerService userManager) {
+            mUserManager = userManager;
             copyFrom(orig);
             copyInto(mMimeGroupToFilter, orig.mMimeGroupToFilter);
             mIsUpdatingMimeGroup = orig.mIsUpdatingMimeGroup;
         }
 
         @Override
-        public void addFilter(F f) {
+        public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) {
             IntentFilter intentFilter = getIntentFilter(f);
-            applyMimeGroups(f);
-            super.addFilter(f);
+            // We assume Computer is available for this class and all subclasses. Because this class
+            // uses subclass method override to handle logic, the Computer parameter must be in the
+            // base, leading to this odd nullability.
+            applyMimeGroups((Computer) snapshot, f);
+            super.addFilter(snapshot, f);
 
             if (!mIsUpdatingMimeGroup) {
                 register_intent_filter(f, intentFilter.mimeGroupsIterator(), mMimeGroupToFilter,
@@ -1309,7 +977,8 @@
          * @param mimeGroup MIME group to update
          * @return true, if any intent filters were changed due to this update
          */
-        public boolean updateMimeGroup(String packageName, String mimeGroup) {
+        public boolean updateMimeGroup(@NonNull Computer computer, String packageName,
+                String mimeGroup) {
             F[] filters = mMimeGroupToFilter.get(mimeGroup);
             int n = filters != null ? filters.length : 0;
 
@@ -1318,18 +987,18 @@
             F filter;
             for (int i = 0; i < n && (filter = filters[i]) != null; i++) {
                 if (isPackageForFilter(packageName, filter)) {
-                    hasChanges |= updateFilter(filter);
+                    hasChanges |= updateFilter(computer, filter);
                 }
             }
             mIsUpdatingMimeGroup = false;
             return hasChanges;
         }
 
-        private boolean updateFilter(F f) {
+        private boolean updateFilter(@NonNull Computer computer, F f) {
             IntentFilter filter = getIntentFilter(f);
             List<String> oldTypes = filter.dataTypes();
             removeFilter(f);
-            addFilter(f);
+            addFilter(computer, f);
             List<String> newTypes = filter.dataTypes();
             return !equalLists(oldTypes, newTypes);
         }
@@ -1350,16 +1019,18 @@
             return first.equals(second);
         }
 
-        private void applyMimeGroups(F f) {
+        private void applyMimeGroups(@NonNull Computer computer, F f) {
             IntentFilter filter = getIntentFilter(f);
 
             for (int i = filter.countMimeGroups() - 1; i >= 0; i--) {
-                List<String> mimeTypes = sPackageManagerInternal.getMimeGroup(
-                        f.first.getPackageName(), filter.getMimeGroup(i));
+                final PackageStateInternal packageState = computer.getPackageStateInternal(
+                        f.first.getPackageName());
 
-                for (int typeIndex = mimeTypes.size() - 1; typeIndex >= 0; typeIndex--) {
-                    String mimeType = mimeTypes.get(typeIndex);
+                Collection<String> mimeTypes = packageState == null
+                        ? Collections.emptyList() : packageState.getMimeGroups()
+                        .get(filter.getMimeGroup(i));
 
+                for (String mimeType : mimeTypes) {
                     try {
                         filter.addDynamicDataType(mimeType);
                     } catch (IntentFilter.MalformedMimeTypeException e) {
@@ -1370,50 +1041,74 @@
                 }
             }
         }
+
+        @Override
+        protected boolean isFilterStopped(@Nullable PackageStateInternal packageState,
+                @UserIdInt int userId) {
+            if (!mUserManager.exists(userId)) {
+                return true;
+            }
+
+            if (packageState == null || packageState.getPkg() == null) {
+                return false;
+            }
+
+            // System apps are never considered stopped for purposes of
+            // filtering, because there may be no way for the user to
+            // actually re-launch them.
+            return !packageState.isSystem()
+                    && packageState.getUserStateOrDefault(userId).isStopped();
+        }
     }
 
-    private static class ActivityIntentResolver
+    public static class ActivityIntentResolver
             extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
 
+        @NonNull
+        private UserNeedsBadgingCache mUserNeedsBadging;
+
         // Default constructor
-        ActivityIntentResolver() {
+        ActivityIntentResolver(@NonNull UserManagerService userManager,
+                @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+            super(userManager);
+            mUserNeedsBadging = userNeedsBadgingCache;
         }
 
         // Copy constructor used in creating snapshots
-        ActivityIntentResolver(ActivityIntentResolver orig) {
-            super(orig);
+        ActivityIntentResolver(@NonNull ActivityIntentResolver orig,
+                @NonNull UserManagerService userManager,
+                @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+            super(orig, userManager);
             mActivities.putAll(orig.mActivities);
-            mFlags = orig.mFlags;
+            mUserNeedsBadging = userNeedsBadgingCache;
         }
 
         @Override
-        public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
-                boolean defaultOnly, int userId) {
-            if (!sUserManager.exists(userId)) return null;
-            mFlags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0);
-            return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+        public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+                String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+            if (!mUserManager.exists(userId)) return null;
+            long flags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0);
+            return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
         }
 
-        List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
-                int userId) {
-            if (!sUserManager.exists(userId)) {
+        List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, int userId) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
-            mFlags = flags;
-            return super.queryIntent(intent, resolvedType,
-                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
-                    userId);
+            return super.queryIntent(computer, intent, resolvedType,
+                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
         }
 
-        List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
-                long flags, List<ParsedActivity> packageActivities, int userId) {
-            if (!sUserManager.exists(userId)) {
+        List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, List<ParsedActivity> packageActivities,
+                int userId) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
             if (packageActivities == null) {
                 return Collections.emptyList();
             }
-            mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int activitiesSize = packageActivities.size();
             ArrayList<Pair<ParsedActivity, ParsedIntentInfo>[]> listCut =
@@ -1431,10 +1126,11 @@
                     listCut.add(array);
                 }
             }
-            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+            return super.queryIntentFromList(computer, intent, resolvedType,
+                    defaultOnly, listCut, userId, flags);
         }
 
-        protected void addActivity(ParsedActivity a, String type,
+        protected void addActivity(@NonNull Computer computer, ParsedActivity a, String type,
                 List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) {
             mActivities.put(a.getComponentName(), a);
             if (DEBUG_SHOW_INFO) {
@@ -1455,7 +1151,7 @@
                 if (!intentFilter.debugCheck()) {
                     Log.w(TAG, "==> For Activity " + a.getName());
                 }
-                addFilter(Pair.create(a, intent));
+                addFilter(computer, Pair.create(a, intent));
             }
         }
 
@@ -1497,11 +1193,6 @@
         }
 
         @Override
-        protected boolean isFilterStopped(Pair<ParsedActivity, ParsedIntentInfo> filter, int userId) {
-            return ComponentResolver.isFilterStopped(filter, userId);
-        }
-
-        @Override
         protected boolean isPackageForFilter(String packageName,
                 Pair<ParsedActivity, ParsedIntentInfo> info) {
             return packageName.equals(info.first.getPackageName());
@@ -1517,43 +1208,35 @@
         }
 
         @Override
-        protected ResolveInfo newResult(Pair<ParsedActivity, ParsedIntentInfo> pair,
-                int match, int userId) {
+        protected ResolveInfo newResult(@NonNull Computer computer,
+                Pair<ParsedActivity, ParsedIntentInfo> pair, int match, int userId,
+                long customFlags) {
             ParsedActivity activity = pair.first;
             ParsedIntentInfo info = pair.second;
             IntentFilter intentFilter = info.getIntentFilter();
 
-            if (!sUserManager.exists(userId)) {
+            if (!mUserManager.exists(userId)) {
                 if (DEBUG) {
                     log("User doesn't exist", info, match, userId);
                 }
                 return null;
             }
 
-            AndroidPackage pkg = sPackageManagerInternal.getPackage(activity.getPackageName());
-            if (pkg == null) {
-                return null;
-            }
-
-            if (!sPackageManagerInternal.isEnabledAndMatches(activity, mFlags, userId)) {
+            final PackageStateInternal packageState =
+                    computer.getPackageStateInternal(activity.getPackageName());
+            if (packageState == null || packageState.getPkg() == null
+                    || !PackageStateUtils.isEnabledAndMatches(packageState, activity, customFlags,
+                    userId)) {
                 if (DEBUG) {
-                    log("!PackageManagerInternal.isEnabledAndMatches; mFlags="
-                            + DebugUtils.flagsToString(PackageManager.class, "MATCH_", mFlags),
+                    log("!PackageManagerInternal.isEnabledAndMatches; flags="
+                            + DebugUtils.flagsToString(PackageManager.class, "MATCH_", customFlags),
                             info, match, userId);
                 }
                 return null;
             }
-            PackageStateInternal ps =
-                    sPackageManagerInternal.getPackageStateInternal(activity.getPackageName());
-            if (ps == null) {
-                if (DEBUG) {
-                    log("info.activity.owner.mExtras == null", info, match, userId);
-                }
-                return null;
-            }
-            final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
-            ActivityInfo ai = PackageInfoUtils.generateActivityInfo(pkg, activity, mFlags,
-                    userState, userId, ps);
+            final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+            ActivityInfo ai = PackageInfoUtils.generateActivityInfo(packageState.getPkg(), activity,
+                    customFlags, userState, userId, packageState);
             if (ai == null) {
                 if (DEBUG) {
                     log("Failed to create ActivityInfo based on " + activity, info, match,
@@ -1562,15 +1245,15 @@
                 return null;
             }
             final boolean matchExplicitlyVisibleOnly =
-                    (mFlags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
+                    (customFlags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
             final boolean matchVisibleToInstantApp =
-                    (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+                    (customFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
             final boolean componentVisible =
                     matchVisibleToInstantApp
                     && intentFilter.isVisibleToInstantApp()
                     && (!matchExplicitlyVisibleOnly
                             || intentFilter.isExplicitlyVisibleToInstantApp());
-            final boolean matchInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+            final boolean matchInstantApp = (customFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to ephemeral apps
             if (matchVisibleToInstantApp && !(componentVisible || userState.isInstantApp())) {
                 if (DEBUG) {
@@ -1595,7 +1278,7 @@
             }
             // throw out instant app filters if updates are available; will trigger
             // instant app resolution
-            if (userState.isInstantApp() && ps.isUpdateAvailable()) {
+            if (userState.isInstantApp() && packageState.isUpdateAvailable()) {
                 if (DEBUG) {
                     log("Instant app update is available", info, match, userId);
                 }
@@ -1604,7 +1287,7 @@
             final ResolveInfo res =
                     new ResolveInfo(intentFilter.hasCategory(Intent.CATEGORY_BROWSABLE));
             res.activityInfo = ai;
-            if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+            if ((customFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = intentFilter;
             }
             res.handleAllWebDataURI = intentFilter.handleAllWebDataURI();
@@ -1617,7 +1300,7 @@
             res.isDefault = info.isHasDefault();
             res.labelRes = info.getLabelRes();
             res.nonLocalizedLabel = info.getNonLocalizedLabel();
-            if (sPackageManagerInternal.userNeedsBadging(userId)) {
+            if (mUserNeedsBadging.get(userId)) {
                 res.noResourceId = true;
             } else {
                 res.icon = info.getIcon();
@@ -1683,19 +1366,22 @@
         // ActivityIntentResolver.
         protected final ArrayMap<ComponentName, ParsedActivity> mActivities =
                 new ArrayMap<>();
-        private long mFlags;
     }
 
     // Both receivers and activities share a class, but point to different get methods
-    private static final class ReceiverIntentResolver extends ActivityIntentResolver {
+    public static final class ReceiverIntentResolver extends ActivityIntentResolver {
 
         // Default constructor
-        ReceiverIntentResolver() {
+        ReceiverIntentResolver(@NonNull UserManagerService userManager,
+                @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+            super(userManager, userNeedsBadgingCache);
         }
 
         // Copy constructor used in creating snapshots
-        ReceiverIntentResolver(ReceiverIntentResolver orig) {
-            super(orig);
+        ReceiverIntentResolver(@NonNull ReceiverIntentResolver orig,
+                @NonNull UserManagerService userManager,
+                @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+            super(orig, userManager, userNeedsBadgingCache);
         }
 
         @Override
@@ -1704,48 +1390,50 @@
         }
     }
 
-    private static final class ProviderIntentResolver
+    public static final class ProviderIntentResolver
             extends MimeGroupsAwareIntentResolver<Pair<ParsedProvider, ParsedIntentInfo>, ResolveInfo> {
         // Default constructor
-        ProviderIntentResolver() {
+        ProviderIntentResolver(@NonNull UserManagerService userManager) {
+            super(userManager);
         }
 
         // Copy constructor used in creating snapshots
-        ProviderIntentResolver(ProviderIntentResolver orig) {
-            super(orig);
+        ProviderIntentResolver(@NonNull ProviderIntentResolver orig,
+                @NonNull UserManagerService userManager) {
+            super(orig, userManager);
             mProviders.putAll(orig.mProviders);
-            mFlags = orig.mFlags;
         }
 
         @Override
-        public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
-                boolean defaultOnly, int userId) {
-            mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
-            return super.queryIntent(intent, resolvedType, defaultOnly, userId);
-        }
-
-        @Nullable
-        List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
-                int userId) {
-            if (!sUserManager.exists(userId)) {
+        public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+                String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
-            mFlags = flags;
-            return super.queryIntent(intent, resolvedType,
-                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
-                    userId);
+            long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+            return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
         }
 
         @Nullable
-        List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
-                long flags, List<ParsedProvider> packageProviders, int userId) {
-            if (!sUserManager.exists(userId)) {
+        List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, int userId) {
+            if (!mUserManager.exists(userId)) {
+                return null;
+            }
+            return super.queryIntent(computer, intent, resolvedType,
+                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
+        }
+
+        @Nullable
+        List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, List<ParsedProvider> packageProviders,
+                int userId) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
             if (packageProviders == null) {
                 return Collections.emptyList();
             }
-            mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int providersSize = packageProviders.size();
             ArrayList<Pair<ParsedProvider, ParsedIntentInfo>[]> listCut =
@@ -1763,10 +1451,11 @@
                     listCut.add(array);
                 }
             }
-            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+            return super.queryIntentFromList(computer, intent, resolvedType,
+                    defaultOnly, listCut, userId, flags);
         }
 
-        void addProvider(ParsedProvider p) {
+        void addProvider(@NonNull Computer computer, ParsedProvider p) {
             if (mProviders.containsKey(p.getComponentName())) {
                 Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring");
                 return;
@@ -1789,7 +1478,7 @@
                 if (!intentFilter.debugCheck()) {
                     Log.w(TAG, "==> For Provider " + p.getName());
                 }
-                addFilter(Pair.create(p, intent));
+                addFilter(computer, Pair.create(p, intent));
             }
         }
 
@@ -1832,21 +1521,16 @@
         }
 
         @Override
-        protected boolean isFilterStopped(Pair<ParsedProvider, ParsedIntentInfo> filter,
-                int userId) {
-            return ComponentResolver.isFilterStopped(filter, userId);
-        }
-
-        @Override
         protected boolean isPackageForFilter(String packageName,
                 Pair<ParsedProvider, ParsedIntentInfo> info) {
             return packageName.equals(info.first.getPackageName());
         }
 
         @Override
-        protected ResolveInfo newResult(Pair<ParsedProvider, ParsedIntentInfo> pair,
-                int match, int userId) {
-            if (!sUserManager.exists(userId)) {
+        protected ResolveInfo newResult(@NonNull Computer computer,
+                Pair<ParsedProvider, ParsedIntentInfo> pair, int match, int userId,
+                long customFlags) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
 
@@ -1854,24 +1538,18 @@
             ParsedIntentInfo intentInfo = pair.second;
             IntentFilter filter = intentInfo.getIntentFilter();
 
-            AndroidPackage pkg = sPackageManagerInternal.getPackage(provider.getPackageName());
-            if (pkg == null) {
+            PackageStateInternal packageState =
+                    computer.getPackageStateInternal(provider.getPackageName());
+            if (packageState == null || packageState.getPkg() == null
+                    || !PackageStateUtils.isEnabledAndMatches(packageState, provider, customFlags,
+                    userId)) {
                 return null;
             }
 
-            if (!sPackageManagerInternal.isEnabledAndMatches(provider, mFlags, userId)) {
-                return null;
-            }
-
-            PackageStateInternal ps =
-                    sPackageManagerInternal.getPackageStateInternal(provider.getPackageName());
-            if (ps == null) {
-                return null;
-            }
-            final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
-            final boolean matchVisibleToInstantApp = (mFlags
+            final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+            final boolean matchVisibleToInstantApp = (customFlags
                     & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
-            final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+            final boolean isInstantApp = (customFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to instant applications
             if (matchVisibleToInstantApp
                     && !(filter.isVisibleToInstantApp() || userState.isInstantApp())) {
@@ -1883,22 +1561,22 @@
             }
             // throw out instant application filters if updates are available; will trigger
             // instant application resolution
-            if (userState.isInstantApp() && ps.isUpdateAvailable()) {
+            if (userState.isInstantApp() && packageState.isUpdateAvailable()) {
                 return null;
             }
             final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
-                    pkg, mFlags, userState, userId, ps);
+                    packageState.getPkg(), customFlags, userState, userId, packageState);
             if (appInfo == null) {
                 return null;
             }
-            ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider, mFlags,
-                    userState, appInfo, userId, ps);
+            ProviderInfo pi = PackageInfoUtils.generateProviderInfo(packageState.getPkg(), provider,
+                    customFlags, userState, appInfo, userId, packageState);
             if (pi == null) {
                 return null;
             }
             final ResolveInfo res = new ResolveInfo();
             res.providerInfo = pi;
-            if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+            if ((customFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
             res.priority = filter.getPriority();
@@ -1959,46 +1637,46 @@
             return input.second.getIntentFilter();
         }
 
-        private final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
-        private long mFlags;
+        final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
     }
 
-    private static final class ServiceIntentResolver
+    public static final class ServiceIntentResolver
             extends MimeGroupsAwareIntentResolver<Pair<ParsedService, ParsedIntentInfo>, ResolveInfo> {
         // Default constructor
-        ServiceIntentResolver() {
+        ServiceIntentResolver(@NonNull UserManagerService userManager) {
+            super(userManager);
         }
 
         // Copy constructor used in creating snapshots
-        ServiceIntentResolver(ServiceIntentResolver orig) {
-            copyFrom(orig);
+        ServiceIntentResolver(@NonNull ServiceIntentResolver orig,
+                @NonNull UserManagerService userManager) {
+            super(orig, userManager);
             mServices.putAll(orig.mServices);
-            mFlags = orig.mFlags;
         }
 
         @Override
-        public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
-                boolean defaultOnly, int userId) {
-            mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
-            return super.queryIntent(intent, resolvedType, defaultOnly, userId);
+        public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
+                String resolvedType, boolean defaultOnly, @UserIdInt int userId) {
+            if (!mUserManager.exists(userId)) {
+                return null;
+            }
+            long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
+            return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags);
         }
 
-        List<ResolveInfo> queryIntent(Intent intent, String resolvedType, long flags,
-                int userId) {
-            if (!sUserManager.exists(userId)) return null;
-            mFlags = flags;
-            return super.queryIntent(intent, resolvedType,
-                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
-                    userId);
+        List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, int userId) {
+            if (!mUserManager.exists(userId)) return null;
+            return super.queryIntent(computer, intent, resolvedType,
+                    (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags);
         }
 
-        List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
-                long flags, List<ParsedService> packageServices, int userId) {
-            if (!sUserManager.exists(userId)) return null;
+        List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent,
+                String resolvedType, long flags, List<ParsedService> packageServices, int userId) {
+            if (!mUserManager.exists(userId)) return null;
             if (packageServices == null) {
                 return Collections.emptyList();
             }
-            mFlags = flags;
             final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
             final int servicesSize = packageServices.size();
             ArrayList<Pair<ParsedService, ParsedIntentInfo>[]> listCut =
@@ -2016,10 +1694,11 @@
                     listCut.add(array);
                 }
             }
-            return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
+            return super.queryIntentFromList(computer, intent, resolvedType,
+                    defaultOnly, listCut, userId, flags);
         }
 
-        void addService(ParsedService s) {
+        void addService(@NonNull Computer computer, ParsedService s) {
             mServices.put(s.getComponentName(), s);
             if (DEBUG_SHOW_INFO) {
                 Log.v(TAG, "  service:");
@@ -2037,7 +1716,7 @@
                 if (!intentFilter.debugCheck()) {
                     Log.w(TAG, "==> For Service " + s.getName());
                 }
-                addFilter(Pair.create(s, intent));
+                addFilter(computer, Pair.create(s, intent));
             }
         }
 
@@ -2080,48 +1759,38 @@
         }
 
         @Override
-        protected boolean isFilterStopped(Pair<ParsedService, ParsedIntentInfo> filter, int userId) {
-            return ComponentResolver.isFilterStopped(filter, userId);
-        }
-
-        @Override
         protected boolean isPackageForFilter(String packageName,
                 Pair<ParsedService, ParsedIntentInfo> info) {
             return packageName.equals(info.first.getPackageName());
         }
 
         @Override
-        protected ResolveInfo newResult(Pair<ParsedService, ParsedIntentInfo> pair, int match,
-                int userId) {
-            if (!sUserManager.exists(userId)) return null;
+        protected ResolveInfo newResult(@NonNull Computer computer,
+                Pair<ParsedService, ParsedIntentInfo> pair, int match, int userId,
+                long customFlags) {
+            if (!mUserManager.exists(userId)) return null;
 
             ParsedService service = pair.first;
             ParsedIntentInfo intentInfo = pair.second;
             IntentFilter filter = intentInfo.getIntentFilter();
 
-            AndroidPackage pkg = sPackageManagerInternal.getPackage(service.getPackageName());
-            if (pkg == null) {
+            final PackageStateInternal packageState = computer.getPackageStateInternal(
+                    service.getPackageName());
+            if (packageState == null || packageState.getPkg() == null
+                    || !PackageStateUtils.isEnabledAndMatches(packageState, service, customFlags,
+                    userId)) {
                 return null;
             }
 
-            if (!sPackageManagerInternal.isEnabledAndMatches(service, mFlags, userId)) {
-                return null;
-            }
-
-            PackageStateInternal ps =
-                    sPackageManagerInternal.getPackageStateInternal(service.getPackageName());
-            if (ps == null) {
-                return null;
-            }
-            final PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
-            ServiceInfo si = PackageInfoUtils.generateServiceInfo(pkg, service, mFlags,
-                    userState, userId, ps);
+            final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+            ServiceInfo si = PackageInfoUtils.generateServiceInfo(packageState.getPkg(), service,
+                    customFlags, userState, userId, packageState);
             if (si == null) {
                 return null;
             }
             final boolean matchVisibleToInstantApp =
-                    (mFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
-            final boolean isInstantApp = (mFlags & PackageManager.MATCH_INSTANT) != 0;
+                    (customFlags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
+            final boolean isInstantApp = (customFlags & PackageManager.MATCH_INSTANT) != 0;
             // throw out filters that aren't visible to ephemeral apps
             if (matchVisibleToInstantApp
                     && !(filter.isVisibleToInstantApp() || userState.isInstantApp())) {
@@ -2133,12 +1802,12 @@
             }
             // throw out instant app filters if updates are available; will trigger
             // instant app resolution
-            if (userState.isInstantApp() && ps.isUpdateAvailable()) {
+            if (userState.isInstantApp() && packageState.isUpdateAvailable()) {
                 return null;
             }
             final ResolveInfo res = new ResolveInfo();
             res.serviceInfo = si;
-            if ((mFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
+            if ((customFlags & PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = filter;
             }
             res.priority = filter.getPriority();
@@ -2203,11 +1872,10 @@
         }
 
         // Keys are String (activity class name), values are Activity.
-        private final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
-        private long mFlags;
+        final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
     }
 
-    static final class InstantAppIntentResolver
+    public static final class InstantAppIntentResolver
             extends IntentResolver<AuxiliaryResolveInfo.AuxiliaryFilter,
             AuxiliaryResolveInfo.AuxiliaryFilter> {
         /**
@@ -2224,6 +1892,13 @@
         final ArrayMap<String, Pair<Integer, InstantAppResolveInfo>> mOrderResult =
                 new ArrayMap<>();
 
+        @NonNull
+        private final UserManagerService mUserManager;
+
+        public InstantAppIntentResolver(@NonNull UserManagerService userManager) {
+            mUserManager = userManager;
+        }
+
         @Override
         protected AuxiliaryResolveInfo.AuxiliaryFilter[] newArray(int size) {
             return new AuxiliaryResolveInfo.AuxiliaryFilter[size];
@@ -2236,9 +1911,10 @@
         }
 
         @Override
-        protected AuxiliaryResolveInfo.AuxiliaryFilter newResult(
-                AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId) {
-            if (!sUserManager.exists(userId)) {
+        protected AuxiliaryResolveInfo.AuxiliaryFilter newResult(@NonNull Computer computer,
+                AuxiliaryResolveInfo.AuxiliaryFilter responseObj, int match, int userId,
+                long customFlags) {
+            if (!mUserManager.exists(userId)) {
                 return null;
             }
             final String packageName = responseObj.resolveInfo.getPackageName();
@@ -2296,40 +1972,17 @@
         }
     }
 
-    private static boolean isFilterStopped(Pair<? extends ParsedComponent, ParsedIntentInfo> pair,
-            int userId) {
-        if (!sUserManager.exists(userId)) {
-            return true;
-        }
-
-        AndroidPackage pkg = sPackageManagerInternal.getPackage(pair.first.getPackageName());
-        if (pkg == null) {
-            return false;
-        }
-
-        PackageStateInternal ps =
-                sPackageManagerInternal.getPackageStateInternal(pair.first.getPackageName());
-        if (ps == null) {
-            return false;
-        }
-
-        // System apps are never considered stopped for purposes of
-        // filtering, because there may be no way for the user to
-        // actually re-launch them.
-        return !ps.isSystem() && ps.getUserStateOrDefault(userId).isStopped();
-    }
-
     /**
      * Removes MIME type from the group, by delegating to IntentResolvers
      * @return true if any intent filters were changed due to this update
      */
-    boolean updateMimeGroup(String packageName, String group) {
+    public boolean updateMimeGroup(@NonNull Computer computer, String packageName, String group) {
         boolean hasChanges = false;
         synchronized (mLock) {
-            hasChanges |= mActivities.updateMimeGroup(packageName, group);
-            hasChanges |= mProviders.updateMimeGroup(packageName, group);
-            hasChanges |= mReceivers.updateMimeGroup(packageName, group);
-            hasChanges |= mServices.updateMimeGroup(packageName, group);
+            hasChanges |= mActivities.updateMimeGroup(computer, packageName, group);
+            hasChanges |= mProviders.updateMimeGroup(computer, packageName, group);
+            hasChanges |= mReceivers.updateMimeGroup(computer, packageName, group);
+            hasChanges |= mServices.updateMimeGroup(computer, packageName, group);
             if (hasChanges) {
                 onChanged();
             }
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
new file mode 100644
index 0000000..b6f2b2a0
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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.resolution;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.Computer;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+
+import java.util.List;
+
+public interface ComponentResolverApi {
+
+    boolean isActivityDefined(@NonNull ComponentName component);
+
+    @Nullable
+    ParsedActivity getActivity(@NonNull ComponentName component);
+
+    @Nullable
+    ParsedProvider getProvider(@NonNull ComponentName component);
+
+    @Nullable
+    ParsedActivity getReceiver(@NonNull ComponentName component);
+
+    @Nullable
+    ParsedService getService(@NonNull ComponentName component);
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    boolean componentExists(@NonNull ComponentName componentName);
+
+    @Nullable
+    List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
+            @UserIdInt int userId);
+
+    @Nullable
+    ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority, long flags,
+            @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
+            @UserIdInt int userId);
+
+    @Nullable
+    List<ProviderInfo> queryProviders(@NonNull Computer computer, @Nullable String processName,
+            @Nullable String metaDataKey, int uid, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
+            @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId);
+
+    @Nullable
+    List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
+            @UserIdInt int userId);
+
+    void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
+            @NonNull List<ProviderInfo> outInfo, boolean safeMode, @UserIdInt int userId);
+}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
new file mode 100644
index 0000000..6b50fc6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2022 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.resolution;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+
+import com.android.server.pm.Computer;
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedMainComponent;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+import com.android.server.utils.WatchableImpl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class ComponentResolverBase extends WatchableImpl implements ComponentResolverApi {
+
+    @NonNull
+    protected ComponentResolver.ActivityIntentResolver mActivities;
+
+    @NonNull
+    protected ComponentResolver.ProviderIntentResolver mProviders;
+
+    @NonNull
+    protected ComponentResolver.ReceiverIntentResolver mReceivers;
+
+    @NonNull
+    protected ComponentResolver.ServiceIntentResolver mServices;
+
+    /** Mapping from provider authority [first directory in content URI codePath) to provider. */
+    @NonNull
+    protected ArrayMap<String, ParsedProvider> mProvidersByAuthority;
+
+    @NonNull
+    protected UserManagerService mUserManager;
+
+    protected ComponentResolverBase(@NonNull UserManagerService userManager) {
+        mUserManager = userManager;
+    }
+
+    @Override
+    public boolean componentExists(@NonNull ComponentName componentName) {
+        ParsedMainComponent component = mActivities.mActivities.get(componentName);
+        if (component != null) {
+            return true;
+        }
+        component = mReceivers.mActivities.get(componentName);
+        if (component != null) {
+            return true;
+        }
+        component = mServices.mServices.get(componentName);
+        if (component != null) {
+            return true;
+        }
+        return mProviders.mProviders.get(componentName) != null;
+    }
+
+    @Nullable
+    @Override
+    public ParsedActivity getActivity(@NonNull ComponentName component) {
+        return mActivities.mActivities.get(component);
+    }
+
+    @Nullable
+    @Override
+    public ParsedProvider getProvider(@NonNull ComponentName component) {
+        return mProviders.mProviders.get(component);
+    }
+
+    @Nullable
+    @Override
+    public ParsedActivity getReceiver(@NonNull ComponentName component) {
+        return mReceivers.mActivities.get(component);
+    }
+
+    @Nullable
+    @Override
+    public ParsedService getService(@NonNull ComponentName component) {
+        return mServices.mServices.get(component);
+    }
+
+    /**
+     * Returns {@code true} if the given activity is defined by some package
+     */
+    @Override
+    public boolean isActivityDefined(@NonNull ComponentName component) {
+        return mActivities.mActivities.get(component) != null;
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, int userId) {
+        return mActivities.queryIntent(computer, intent, resolvedType, flags, userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
+            int userId) {
+        return mActivities.queryIntentForPackage(computer, intent, resolvedType, flags, activities,
+                userId);
+    }
+
+    @Nullable
+    @Override
+    public ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority,
+            long flags, int userId) {
+        final ParsedProvider p = mProvidersByAuthority.get(authority);
+        if (p == null) {
+            return null;
+        }
+        PackageStateInternal packageState = computer.getPackageStateInternal(p.getPackageName());
+        if (packageState == null) {
+            return null;
+        }
+        final AndroidPackage pkg = packageState.getPkg();
+        if (pkg == null) {
+            return null;
+        }
+        final PackageUserStateInternal state = packageState.getUserStateOrDefault(userId);
+        ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+                pkg, flags, state, userId, packageState);
+        if (appInfo == null) {
+            return null;
+        }
+        return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId,
+                packageState);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, int userId) {
+        return mProviders.queryIntent(computer, intent, resolvedType, flags, userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
+            @UserIdInt int userId) {
+        return mProviders.queryIntentForPackage(computer, intent, resolvedType, flags, providers,
+                userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ProviderInfo> queryProviders(@NonNull Computer computer,
+            @Nullable String processName, @Nullable String metaDataKey, int uid, long flags,
+            int userId) {
+        if (!mUserManager.exists(userId)) {
+            return null;
+        }
+        List<ProviderInfo> providerList = null;
+        PackageInfoUtils.CachedApplicationInfoGenerator appInfoGenerator = null;
+        for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
+            final ParsedProvider p = mProviders.mProviders.valueAt(i);
+            if (p.getAuthority() == null) {
+                continue;
+            }
+
+            final PackageStateInternal ps = computer.getPackageStateInternal(p.getPackageName());
+            if (ps == null) {
+                continue;
+            }
+
+            AndroidPackage pkg = ps.getPkg();
+            if (pkg == null) {
+                continue;
+            }
+
+            if (processName != null && (!p.getProcessName().equals(processName)
+                    || !UserHandle.isSameApp(pkg.getUid(), uid))) {
+                continue;
+            }
+            // See PM.queryContentProviders()'s javadoc for why we have the metaData parameter.
+            if (metaDataKey != null && !p.getMetaData().containsKey(metaDataKey)) {
+                continue;
+            }
+            if (appInfoGenerator == null) {
+                appInfoGenerator = new PackageInfoUtils.CachedApplicationInfoGenerator();
+            }
+            final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
+            final ApplicationInfo appInfo =
+                    appInfoGenerator.generate(pkg, flags, state, userId, ps);
+            if (appInfo == null) {
+                continue;
+            }
+
+            final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+                    pkg, p, flags, state, appInfo, userId, ps);
+            if (info == null) {
+                continue;
+            }
+            if (providerList == null) {
+                providerList = new ArrayList<>(i + 1);
+            }
+            providerList.add(info);
+        }
+        return providerList;
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, int userId) {
+        return mReceivers.queryIntent(computer, intent, resolvedType, flags, userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
+            @UserIdInt int userId) {
+        return mReceivers.queryIntentForPackage(computer, intent, resolvedType, flags, receivers,
+                userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        return mServices.queryIntent(computer, intent, resolvedType, flags, userId);
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
+            @UserIdInt int userId) {
+        return mServices.queryIntentForPackage(computer, intent, resolvedType, flags, services,
+                userId);
+    }
+
+    @Override
+    public void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
+            @NonNull List<ProviderInfo> outInfo, boolean safeMode, int userId) {
+        PackageInfoUtils.CachedApplicationInfoGenerator appInfoGenerator = null;
+        for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
+            final ParsedProvider p = mProvidersByAuthority.valueAt(i);
+            if (!p.isSyncable()) {
+                continue;
+            }
+
+            final PackageStateInternal ps = computer.getPackageStateInternal(p.getPackageName());
+            if (ps == null) {
+                continue;
+            }
+
+            final AndroidPackage pkg = ps.getPkg();
+            if (pkg == null) {
+                continue;
+            }
+
+            if (safeMode && !pkg.isSystem()) {
+                continue;
+            }
+            if (appInfoGenerator == null) {
+                appInfoGenerator = new PackageInfoUtils.CachedApplicationInfoGenerator();
+            }
+            final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
+            final ApplicationInfo appInfo =
+                    appInfoGenerator.generate(pkg, 0, state, userId, ps);
+            if (appInfo == null) {
+                continue;
+            }
+
+            final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+                    pkg, p, 0, state, appInfo, userId, ps);
+            if (info == null) {
+                continue;
+            }
+            outNames.add(mProvidersByAuthority.keyAt(i));
+            outInfo.add(info);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
new file mode 100644
index 0000000..ecc53eb
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 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.resolution;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+
+import com.android.server.pm.Computer;
+import com.android.server.pm.PackageManagerTracedLock;
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.pkg.component.ParsedActivity;
+import com.android.server.pm.pkg.component.ParsedProvider;
+import com.android.server.pm.pkg.component.ParsedService;
+
+import java.util.List;
+
+public abstract class ComponentResolverLocked extends ComponentResolverBase {
+
+    protected final PackageManagerTracedLock mLock = new PackageManagerTracedLock();
+
+    protected ComponentResolverLocked(@NonNull UserManagerService userManager) {
+        super(userManager);
+    }
+
+    @Override
+    public boolean componentExists(@NonNull ComponentName componentName) {
+        synchronized (mLock) {
+            return super.componentExists(componentName);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ParsedActivity getActivity(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.getActivity(component);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ParsedProvider getProvider(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.getProvider(component);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ParsedActivity getReceiver(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.getReceiver(component);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ParsedService getService(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.getService(component);
+        }
+    }
+
+    @Override
+    public boolean isActivityDefined(@NonNull ComponentName component) {
+        synchronized (mLock) {
+            return super.isActivityDefined(component);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryActivities(computer, intent, resolvedType, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryActivities(computer, intent, resolvedType, flags, activities, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority,
+            long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryProvider(computer, authority, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryProviders(computer, intent, resolvedType, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryProviders(computer, intent, resolvedType, flags, providers, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ProviderInfo> queryProviders(@NonNull Computer computer,
+            @Nullable String processName, @Nullable String metaDataKey, int uid, long flags,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryProviders(computer, processName, metaDataKey, uid, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryReceivers(computer, intent, resolvedType, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryReceivers(computer, intent, resolvedType, flags, receivers, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryServices(computer, intent, resolvedType, flags, userId);
+        }
+    }
+
+    @Nullable
+    @Override
+    public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent,
+            @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services,
+            @UserIdInt int userId) {
+        synchronized (mLock) {
+            return super.queryServices(computer, intent, resolvedType, flags, services, userId);
+        }
+    }
+
+    @Override
+    public void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames,
+            @NonNull List<ProviderInfo> outInfo, boolean safeMode, @UserIdInt int userId) {
+        synchronized (mLock) {
+            super.querySyncProviders(computer, outNames, outInfo, safeMode, userId);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverSnapshot.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverSnapshot.java
new file mode 100644
index 0000000..3a96fe6
--- /dev/null
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverSnapshot.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.resolution;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+
+import com.android.server.pm.UserManagerService;
+import com.android.server.pm.UserNeedsBadgingCache;
+
+public class ComponentResolverSnapshot extends ComponentResolverBase {
+
+    public ComponentResolverSnapshot(@NonNull ComponentResolver orig,
+            @NonNull UserNeedsBadgingCache userNeedsBadgingCache) {
+        super(UserManagerService.getInstance());
+        mActivities = new ComponentResolver.ActivityIntentResolver(orig.mActivities, mUserManager,
+                userNeedsBadgingCache);
+        mProviders = new ComponentResolver.ProviderIntentResolver(orig.mProviders, mUserManager);
+        mReceivers = new ComponentResolver.ReceiverIntentResolver(orig.mReceivers, mUserManager,
+                userNeedsBadgingCache);
+        mServices = new ComponentResolver.ServiceIntentResolver(orig.mServices, mUserManager);
+        mProvidersByAuthority = new ArrayMap<>(orig.mProvidersByAuthority);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/snapshot/PackageDataSnapshot.java b/services/core/java/com/android/server/pm/snapshot/PackageDataSnapshot.java
new file mode 100644
index 0000000..b091445
--- /dev/null
+++ b/services/core/java/com/android/server/pm/snapshot/PackageDataSnapshot.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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.snapshot;
+
+public interface PackageDataSnapshot {
+}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8a105dd..bd58472 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1177,7 +1177,10 @@
     @Override
     public void onBootPhase(int phase) {
         synchronized (mLock) {
-            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            if (phase == PHASE_SYSTEM_SERVICES_READY) {
+                systemReady();
+
+            } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                 incrementBootCount();
 
             } else if (phase == PHASE_BOOT_COMPLETED) {
@@ -1203,7 +1206,7 @@
         }
     }
 
-    public void systemReady() {
+    private void systemReady() {
         synchronized (mLock) {
             mSystemReady = true;
             mDreamManager = getLocalService(DreamManagerInternal.class);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
index ffcb2bd..b4613a7 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverPolicy.java
@@ -276,7 +276,7 @@
     }
 
     /**
-     * Called by {@link PowerManagerService#systemReady}, *with no lock held.*
+     * Called by {@link PowerManagerService#onBootPhase}, *with no lock held.*
      */
     public void systemReady() {
         ConcurrentUtils.wtfIfLockHeld(TAG, mLock);
diff --git a/services/core/java/com/android/server/sensorprivacy/PersistedState.java b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
index 06f5fc0..e79efdb8 100644
--- a/services/core/java/com/android/server/sensorprivacy/PersistedState.java
+++ b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
@@ -296,7 +296,7 @@
                 SensorState sensorState = states.valueAt(i);
 
                 // Do not persist hardware toggle states. Will be restored on reboot
-                if (userSensor.mType != SensorPrivacyManager.ToggleTypes.SOFTWARE) {
+                if (userSensor.mType != SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE) {
                     continue;
                 }
 
@@ -478,7 +478,7 @@
                 for (int j = 0; j < numSensors; j++) {
                     int sensor = userIndividualEnabled.keyAt(j);
                     SensorState sensorState = userIndividualEnabled.valueAt(j);
-                    result.addState(SensorPrivacyManager.ToggleTypes.SOFTWARE,
+                    result.addState(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
                             userId, sensor, sensorState.getState(), sensorState.getLastChange());
                 }
             }
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 358f69e..a8e2d43 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -39,8 +39,8 @@
 import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
 import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
 import static android.hardware.SensorPrivacyManager.Sources.SHELL;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.HARDWARE;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.SOFTWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
 import static android.os.UserHandle.USER_NULL;
 import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
 
@@ -310,11 +310,12 @@
             // Reset sensor privacy when restriction is added
             if (!prevRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)
                     && newRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)) {
-                setToggleSensorPrivacyUnchecked(SOFTWARE, userId, OTHER, CAMERA, false);
+                setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, CAMERA, false);
             }
             if (!prevRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)
                     && newRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)) {
-                setToggleSensorPrivacyUnchecked(SOFTWARE, userId, OTHER, MICROPHONE, false);
+                setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, MICROPHONE,
+                        false);
             }
         }
 
@@ -565,7 +566,7 @@
          */
         private String getSensorUseActivityName(ArraySet<Integer> sensors) {
             for (Integer sensor : sensors) {
-                if (isToggleSensorPrivacyEnabled(HARDWARE, sensor)) {
+                if (isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor)) {
                     return mContext.getResources().getString(
                             R.string.config_sensorUseStartedActivity_hwToggle);
                 }
@@ -691,7 +692,7 @@
                 return;
             }
 
-            setToggleSensorPrivacyUnchecked(SOFTWARE, userId, source, sensor, enable);
+            setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, source, sensor, enable);
         }
 
         private void setToggleSensorPrivacyUnchecked(int toggleType, int userId, int source,
@@ -866,8 +867,8 @@
 
         @Override
         public boolean isCombinedToggleSensorPrivacyEnabled(int sensor) {
-            return isToggleSensorPrivacyEnabled(SOFTWARE, sensor) || isToggleSensorPrivacyEnabled(
-                    HARDWARE, sensor);
+            return isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor)
+                    || isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor);
         }
 
         private boolean isToggleSensorPrivacyEnabledInternal(int userId, int toggleType,
@@ -879,7 +880,7 @@
 
         @Override
         public boolean supportsSensorToggle(int toggleType, int sensor) {
-            if (toggleType == SOFTWARE) {
+            if (toggleType == TOGGLE_TYPE_SOFTWARE) {
                 if (sensor == MICROPHONE) {
                     return mContext.getResources()
                             .getBoolean(R.bool.config_supportsMicToggle);
@@ -887,7 +888,7 @@
                     return mContext.getResources()
                             .getBoolean(R.bool.config_supportsCamToggle);
                 }
-            } else if (toggleType == SensorPrivacyManager.ToggleTypes.HARDWARE) {
+            } else if (toggleType == TOGGLE_TYPE_HARDWARE) {
                 if (sensor == MICROPHONE) {
                     return mContext.getResources()
                             .getBoolean(R.bool.config_supportsHardwareMicToggle);
@@ -1003,37 +1004,41 @@
             final int hwToggleIdx = 1;
             // Get SW toggles state
             mSensorPrivacyStateController.atomic(() -> {
-                prevMicState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE,
-                        MICROPHONE);
-                prevCamState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE,
-                        CAMERA);
-                micState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE,
-                        MICROPHONE);
-                camState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE, CAMERA);
+                prevMicState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_SOFTWARE, MICROPHONE);
+                prevCamState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_SOFTWARE, CAMERA);
+                micState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_SOFTWARE, MICROPHONE);
+                camState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_SOFTWARE, CAMERA);
             });
             // Get HW toggles state
             mSensorPrivacyStateController.atomic(() -> {
-                prevMicState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, HARDWARE,
-                        MICROPHONE);
-                prevCamState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, HARDWARE,
-                        CAMERA);
-                micState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, HARDWARE,
-                        MICROPHONE);
-                camState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, HARDWARE, CAMERA);
+                prevMicState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_HARDWARE, MICROPHONE);
+                prevCamState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from,
+                        TOGGLE_TYPE_HARDWARE, CAMERA);
+                micState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_HARDWARE, MICROPHONE);
+                camState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to,
+                        TOGGLE_TYPE_HARDWARE, CAMERA);
             });
 
             if (from == USER_NULL || prevMicState[swToggleIdx] != micState[swToggleIdx]
                     || prevMicState[hwToggleIdx] != micState[hwToggleIdx]) {
-                mHandler.handleSensorPrivacyChanged(to, SOFTWARE, MICROPHONE,
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, MICROPHONE,
                         micState[swToggleIdx]);
-                mHandler.handleSensorPrivacyChanged(to, HARDWARE, MICROPHONE,
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, MICROPHONE,
                         micState[hwToggleIdx]);
                 setGlobalRestriction(MICROPHONE, micState[swToggleIdx] || micState[hwToggleIdx]);
             }
             if (from == USER_NULL || prevCamState[swToggleIdx] != camState[swToggleIdx]
                     || prevCamState[hwToggleIdx] != camState[hwToggleIdx]) {
-                mHandler.handleSensorPrivacyChanged(to, SOFTWARE, CAMERA, camState[swToggleIdx]);
-                mHandler.handleSensorPrivacyChanged(to, HARDWARE, CAMERA, camState[hwToggleIdx]);
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, CAMERA,
+                        camState[swToggleIdx]);
+                mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, CAMERA,
+                        camState[hwToggleIdx]);
                 setGlobalRestriction(CAMERA, camState[swToggleIdx] || camState[hwToggleIdx]);
             }
         }
@@ -1437,7 +1442,7 @@
         public boolean isSensorPrivacyEnabled(int userId, int sensor) {
             return SensorPrivacyService.this
                     .mSensorPrivacyServiceImpl.isToggleSensorPrivacyEnabledInternal(userId,
-                            SOFTWARE, sensor);
+                            TOGGLE_TYPE_SOFTWARE, sensor);
         }
 
         @Override
@@ -1487,10 +1492,12 @@
             userId = (userId == UserHandle.USER_CURRENT ? mCurrentUser : userId);
             final int realUserId = (userId == UserHandle.USER_NULL ? mContext.getUserId() : userId);
 
-            sps.setToggleSensorPrivacyUnchecked(HARDWARE, realUserId, OTHER, sensor, enable);
+            sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_HARDWARE, realUserId, OTHER, sensor,
+                    enable);
             // Also disable the SW toggle when disabling the HW toggle
             if (!enable) {
-                sps.setToggleSensorPrivacyUnchecked(SOFTWARE, realUserId, OTHER, sensor, enable);
+                sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, realUserId, OTHER, sensor,
+                        enable);
             }
         }
     }
@@ -1546,9 +1553,9 @@
                 if (!mIsInEmergencyCall) {
                     mIsInEmergencyCall = true;
                     if (mSensorPrivacyServiceImpl
-                            .isToggleSensorPrivacyEnabled(SOFTWARE, MICROPHONE)) {
+                            .isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, MICROPHONE)) {
                         mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
-                                SOFTWARE, mCurrentUser, OTHER, MICROPHONE, false);
+                                TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, false);
                         mMicUnmutedForEmergencyCall = true;
                     } else {
                         mMicUnmutedForEmergencyCall = false;
@@ -1574,7 +1581,7 @@
                     mIsInEmergencyCall = false;
                     if (mMicUnmutedForEmergencyCall) {
                         mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
-                                SOFTWARE, mCurrentUser, OTHER, MICROPHONE, true);
+                                TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, true);
                         mMicUnmutedForEmergencyCall = false;
                     }
                 }
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index c638201..9bbae4b 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -29,7 +29,9 @@
 import android.os.IHwBinder;
 import android.os.RemoteException;
 import android.system.OsConstants;
+import android.util.Log;
 
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -54,6 +56,8 @@
  * </ul>
  */
 final class SoundTriggerHw2Compat implements ISoundTriggerHal {
+    private static final String TAG = "SoundTriggerHw2Compat";
+
     private final @NonNull Runnable mRebootRunnable;
     private final @NonNull IHwBinder mBinder;
     private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
@@ -226,6 +230,16 @@
             return handle.get();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
+        } finally {
+            // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+            //   clause, instead of having to explicitly close internal fields.
+            if (hidlModel.data != null) {
+                try {
+                    hidlModel.data.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to close file", e);
+                }
+            }
         }
     }
 
@@ -252,6 +266,16 @@
             return handle.get();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
+        } finally {
+            // TODO(b/219825762): We should be able to use the entire object in a try-with-resources
+            //   clause, instead of having to explicitly close internal fields.
+            if (hidlModel.common.data != null) {
+                try {
+                    hidlModel.common.data.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to close file", e);
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d84d724..b0efa5b 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2537,7 +2537,6 @@
         }
         removeStartingWindowAnimation(true /* prepareAnimation */);
 
-        // TODO(b/215316431): Add tests
         final Task task = getTask();
         if (prevEligibleForLetterboxEducation != isEligibleForLetterboxEducation()
                 && task != null) {
@@ -7717,7 +7716,6 @@
      *     once the starting window is removed in {@link #removeStartingWindow}).
      * </ul>
      */
-    // TODO(b/215316431): Add tests
     boolean isEligibleForLetterboxEducation() {
         return mWmService.mLetterboxConfiguration.getIsEducationEnabled()
                 && mIsEligibleForFixedOrientationLetterbox
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ddfdddc..4c5c705 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3326,8 +3326,8 @@
         }
         if (mInsetsStateController != null) {
             for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
-                final InsetsSourceProvider provider = mInsetsStateController.peekSourceProvider(
-                        type);
+                final WindowContainerInsetsSourceProvider provider = mInsetsStateController
+                        .peekSourceProvider(type);
                 if (provider != null) {
                     provider.dumpDebug(proto, type == ITYPE_IME ? IME_INSETS_SOURCE_PROVIDER :
                             INSETS_SOURCE_PROVIDERS, logLevel);
@@ -4036,7 +4036,7 @@
         // app.
         assignWindowLayers(true /* setLayoutNeeded */);
         // 3. The z-order of IME might have been changed. Update the above insets state.
-        mInsetsStateController.updateAboveInsetsState(mInputMethodWindow,
+        mInsetsStateController.updateAboveInsetsState(
                 mInsetsStateController.getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
         // 4. Update the IME control target to apply any inset change and animation.
         // 5. Reparent the IME container surface to either the input target app, or the IME window
@@ -4167,7 +4167,7 @@
         if (mImeInputTarget != target) {
             ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
             setImeInputTarget(target);
-            mInsetsStateController.updateAboveInsetsState(mInputMethodWindow, mInsetsStateController
+            mInsetsStateController.updateAboveInsetsState(mInsetsStateController
                     .getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
             updateImeControlTarget();
         }
@@ -4810,6 +4810,17 @@
         }
 
         @Override
+        void updateAboveInsetsState(InsetsState aboveInsetsState,
+                SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+                ArraySet<WindowState> insetsChangedWindows) {
+            if (skipImeWindowsDuringTraversal(mDisplayContent)) {
+                return;
+            }
+            super.updateAboveInsetsState(aboveInsetsState, localInsetsSourceProvidersFromParent,
+                    insetsChangedWindows);
+        }
+
+        @Override
         boolean forAllWindows(ToBooleanFunction<WindowState> callback,
                 boolean traverseTopToBottom) {
             final DisplayContent dc = mDisplayContent;
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 8f97220..f24e429 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -46,7 +46,7 @@
  * Controller for IME inset source on the server. It's called provider as it provides the
  * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
  */
-final class ImeInsetsSourceProvider extends InsetsSourceProvider {
+final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider {
 
     private InsetsControlTarget mImeRequester;
     private Runnable mShowImeRunner;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 433f1071..bb6d83c 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -176,7 +176,8 @@
     }
 
     boolean isHidden(@InternalInsetsType int type) {
-        final InsetsSourceProvider provider =  mStateController.peekSourceProvider(type);
+        final WindowContainerInsetsSourceProvider provider = mStateController
+                .peekSourceProvider(type);
         return provider != null && provider.hasWindowContainer()
                 && !provider.getSource().isVisible();
     }
@@ -358,10 +359,10 @@
 
             // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
             if (type == ITYPE_IME) {
-                ArrayMap<Integer, InsetsSourceProvider> providers = mStateController
+                ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController
                         .getSourceProviders();
                 for (int i = providers.size() - 1; i >= 0; i--) {
-                    InsetsSourceProvider otherProvider = providers.valueAt(i);
+                    WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i);
                     if (otherProvider.overridesImeFrame()) {
                         InsetsSource override =
                                 new InsetsSource(
@@ -662,7 +663,7 @@
         final IntArray showingTransientTypes = mShowingTransientTypes;
         for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
             final @InternalInsetsType int type = showingTransientTypes.get(i);
-            InsetsSourceProvider provider = mStateController.getSourceProvider(type);
+            WindowContainerInsetsSourceProvider provider = mStateController.getSourceProvider(type);
             InsetsSourceControl control = provider.getControl(mDummyControlTarget);
             if (control == null || control.getLeash() == null) {
                 continue;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 4c7a297..047bf2f 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -65,7 +65,7 @@
  * Controller for a specific inset source on the server. It's called provider as it provides the
  * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
  */
-class InsetsSourceProvider {
+abstract class InsetsSourceProvider {
 
     protected final DisplayContent mDisplayContent;
     protected final @NonNull InsetsSource mSource;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 32e70d9..78608e2 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -35,7 +35,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.SparseArray;
-import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
@@ -56,7 +55,8 @@
     private final InsetsState mState = new InsetsState();
     private final DisplayContent mDisplayContent;
 
-    private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>();
+    private final ArrayMap<Integer, WindowContainerInsetsSourceProvider> mProviders =
+            new ArrayMap<>();
     private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap =
             new ArrayMap<>();
     private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>();
@@ -108,21 +108,22 @@
         return result;
     }
 
-    ArrayMap<Integer, InsetsSourceProvider> getSourceProviders() {
+    ArrayMap<Integer, WindowContainerInsetsSourceProvider> getSourceProviders() {
         return mProviders;
     }
 
     /**
      * @return The provider of a specific type.
      */
-    InsetsSourceProvider getSourceProvider(@InternalInsetsType int type) {
+    WindowContainerInsetsSourceProvider getSourceProvider(@InternalInsetsType int type) {
         if (type == ITYPE_IME) {
             return mProviders.computeIfAbsent(type,
                     key -> new ImeInsetsSourceProvider(
                             mState.getSource(key), this, mDisplayContent));
         } else {
             return mProviders.computeIfAbsent(type,
-                    key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
+                    key -> new WindowContainerInsetsSourceProvider(mState.getSource(key), this,
+                            mDisplayContent));
         }
     }
 
@@ -133,7 +134,8 @@
     /**
      * @return The provider of a specific type or null if we don't have it.
      */
-    @Nullable InsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) {
+    @Nullable
+    WindowContainerInsetsSourceProvider peekSourceProvider(@InternalInsetsType int type) {
         return mProviders.get(type);
     }
 
@@ -153,58 +155,25 @@
     }
 
     /**
-     * Updates {@link WindowState#mAboveInsetsState} for all windows in the display while the
-     * z-order of a window is changed.
+     * Updates {@link WindowState#mAboveInsetsState} for all windows in the display.
      *
-     * @param win The window whose z-order has changed.
      * @param notifyInsetsChange {@code true} if the clients should be notified about the change.
      */
-    void updateAboveInsetsState(WindowState win, boolean notifyInsetsChange) {
-        if (win == null || win.getDisplayContent() != mDisplayContent) {
-            return;
-        }
-        final boolean[] aboveWin = { true };
+    void updateAboveInsetsState(boolean notifyInsetsChange) {
         final InsetsState aboveInsetsState = new InsetsState();
         aboveInsetsState.set(mState,
                 displayCutout() | systemGestures() | mandatorySystemGestures());
-        final SparseArray<InsetsSource> winProvidedSources = win.getProvidedInsetsSources();
-        final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
-        mDisplayContent.forAllWindows(w -> {
-            if (aboveWin[0]) {
-                if (w == win) {
-                    aboveWin[0] = false;
-                    if (!win.mAboveInsetsState.equals(aboveInsetsState)) {
-                        win.mAboveInsetsState.set(aboveInsetsState);
-                        insetsChangedWindows.add(win);
-                    }
-                    return winProvidedSources.size() == 0;
-                } else {
-                    final SparseArray<InsetsSource> providedSources = w.getProvidedInsetsSources();
-                    for (int i = providedSources.size() - 1; i >= 0; i--) {
-                        aboveInsetsState.addSource(providedSources.valueAt(i));
-                    }
-                    if (winProvidedSources.size() == 0) {
-                        return false;
-                    }
-                    boolean changed = false;
-                    for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
-                        changed |= w.mAboveInsetsState.removeSource(winProvidedSources.keyAt(i));
-                    }
-                    if (changed) {
-                        insetsChangedWindows.add(w);
-                    }
-                }
-            } else {
-                for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
-                    w.mAboveInsetsState.addSource(winProvidedSources.valueAt(i));
-                }
-                insetsChangedWindows.add(w);
-            }
-            return false;
-        }, true /* traverseTopToBottom */);
+        final ArraySet<WindowState> insetsChangedWindows = new ArraySet<>();
+        final SparseArray<InsetsSourceProvider>
+                localInsetsSourceProvidersFromParent = new SparseArray<>();
+        // This method will iterate on the entire hierarchy in top to bottom z-order manner. The
+        // aboveInsetsState will be modified as per the insets provided by the WindowState being
+        // visited.
+        mDisplayContent.updateAboveInsetsState(aboveInsetsState,
+                localInsetsSourceProvidersFromParent, insetsChangedWindows);
         if (notifyInsetsChange) {
             for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
-                mDispatchInsetsChanged.accept(insetsChangedWindows.get(i));
+                mDispatchInsetsChanged.accept(insetsChangedWindows.valueAt(i));
             }
         }
     }
@@ -245,7 +214,7 @@
     void computeSimulatedState(WindowState win, DisplayFrames displayFrames, Rect winFrame) {
         final InsetsState state = displayFrames.mInsetsState;
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            final InsetsSourceProvider provider = mProviders.valueAt(i);
+            final WindowContainerInsetsSourceProvider provider = mProviders.valueAt(i);
             if (provider.mWindowContainer == win) {
                 state.addSource(provider.createSimulatedSource(displayFrames, winFrame));
             }
@@ -302,7 +271,7 @@
         if (target == previous) {
             return;
         }
-        final InsetsSourceProvider provider = mProviders.get(type);
+        final WindowContainerInsetsSourceProvider provider = mProviders.get(type);
         if (provider == null) {
             return;
         }
@@ -333,7 +302,7 @@
         if (fakeTarget == previous) {
             return;
         }
-        final InsetsSourceProvider provider = mProviders.get(type);
+        final WindowContainerInsetsSourceProvider provider = mProviders.get(type);
         if (provider == null) {
             return;
         }
@@ -388,7 +357,7 @@
         }
         mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             for (int i = mProviders.size() - 1; i >= 0; i--) {
-                final InsetsSourceProvider provider = mProviders.valueAt(i);
+                final WindowContainerInsetsSourceProvider provider = mProviders.valueAt(i);
                 provider.onSurfaceTransactionApplied();
             }
             final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
diff --git a/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java b/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java
new file mode 100644
index 0000000..6e8beee
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RectInsetsSourceProvider.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.graphics.Rect;
+import android.util.Slog;
+import android.view.InsetsSource;
+
+/**
+ * An {@link InsetsSourceProvider} which doesn't have a backing window or a window container.
+ */
+public class RectInsetsSourceProvider extends InsetsSourceProvider {
+    private static final String TAG = TAG_WITH_CLASS_NAME
+            ? RectInsetsSourceProvider.class.getSimpleName()
+            : TAG_WM;
+
+    RectInsetsSourceProvider(InsetsSource source,
+            InsetsStateController stateController, DisplayContent displayContent) {
+        super(source, stateController, displayContent);
+    }
+
+    /**
+     * Sets the given {@code rect} as the frame of the underlying {@link InsetsSource}.
+     */
+    void setRect(Rect rect) {
+        mSource.setFrame(rect);
+        mSource.setVisible(true);
+    }
+
+    @Override
+    void onPostLayout() {
+        if (WindowManagerDebugConfig.DEBUG) {
+            Slog.d(TAG, "onPostLayout(), not calling super.onPostLayout().");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 358a615..ffa1a60 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2824,7 +2824,7 @@
         TaskTransitionSpec spec = mWmService.mTaskTransitionSpec;
         if (spec != null) {
             for (@InsetsState.InternalInsetsType int insetType : spec.animationBoundInsets) {
-                InsetsSourceProvider insetProvider = getDisplayContent()
+                WindowContainerInsetsSourceProvider insetProvider = getDisplayContent()
                         .getInsetsStateController()
                         .getSourceProvider(insetType);
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 45fdc04..bbc8462 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -57,6 +57,7 @@
 import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
 import static com.android.server.wm.WindowContainerProto.SURFACE_CONTROL;
 import static com.android.server.wm.WindowContainerProto.VISIBLE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -85,6 +86,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.InsetsSource;
+import android.view.InsetsState;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
@@ -148,12 +150,24 @@
     // onParentChanged() notification.
     boolean mReparenting;
 
-    protected @Nullable InsetsSourceProvider mControllableInsetProvider;
+    /**
+     * Map of {@link InsetsState.InternalInsetsType} to the {@link InsetsSourceProvider} that
+     * provides local insets for all children of the current {@link WindowContainer}.
+     *
+     * Note that these InsetsSourceProviders are not part of the {@link InsetsStateController} and
+     * live here. These are supposed to provide insets only to the subtree of the current
+     * {@link WindowContainer}.
+     */
+    @Nullable
+    SparseArray<InsetsSourceProvider> mLocalInsetsSourceProviders = null;
+
+    @Nullable
+    protected InsetsSourceProvider mControllableInsetProvider;
 
     /**
      * The insets sources provided by this windowContainer.
      */
-    private SparseArray<InsetsSource> mProvidedInsetsSources = null;
+    protected SparseArray<InsetsSource> mProvidedInsetsSources = null;
 
     // List of children for this window container. List is in z-order as the children appear on
     // screen with the top-most window container at the tail of the list.
@@ -340,14 +354,135 @@
     }
 
     /**
-     * Set's an {@link InsetsSourceProvider} to be associated with this window, but only if the
-     * provider itself is controllable, as one window can be the provider of more than one inset
-     * type (i.e. gesture insets). If this window is controllable, all its animations must be
-     * controlled by its control target, and the visibility of this window should be taken account
-     * into the state of the control target.
+     * Updates the {@link WindowState#mAboveInsetsState} and
+     * {@link WindowState#mMergedLocalInsetsSources} by visiting the entire hierarchy.
+     *
+     * {@link WindowState#mAboveInsetsState} is updated by visiting all the windows in z-order
+     * top-to-bottom manner and considering the {@link WindowContainer#mProvidedInsetsSources}
+     * provided by the {@link WindowState}s at the top.
+     * {@link WindowState#updateAboveInsetsState(InsetsState, SparseArray, ArraySet)} visits the
+     * IME container in the correct order to make sure the IME insets are passed correctly to the
+     * {@link WindowState}s below it.
+     *
+     * {@link WindowState#mMergedLocalInsetsSources} is updated by considering
+     * {@link WindowContainer#mLocalInsetsSourceProviders} provided by all the parents of the
+     * window.
+     * A given insetsType can be provided as a LocalInsetsSourceProvider only once in a
+     * Parent-to-leaf path.
+     *
+     * Examples: Please take a look at
+     * {@link WindowContainerTests#testAddLocalInsetsSourceProvider()}
+     * {@link
+     * WindowContainerTests#testAddLocalInsetsSourceProvider_windowSkippedIfProvidingOnParent()}
+     * {@link WindowContainerTests#testRemoveLocalInsetsSourceProvider()}.
+     *
+     * @param aboveInsetsState The InsetsState of all the Windows above the current container.
+     * @param localInsetsSourceProvidersFromParent The local InsetsSourceProviders provided by all
+     *                                             the parents in the hierarchy of the current
+     *                                             container.
+     * @param insetsChangedWindows The windows which the insets changed have changed for.
+     */
+    void updateAboveInsetsState(InsetsState aboveInsetsState,
+            SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+            ArraySet<WindowState> insetsChangedWindows) {
+        SparseArray<InsetsSourceProvider> mergedLocalInsetsSourceProviders =
+                localInsetsSourceProvidersFromParent;
+        if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+            mergedLocalInsetsSourceProviders = createShallowCopy(mergedLocalInsetsSourceProviders);
+            for (int i = 0; i < mLocalInsetsSourceProviders.size(); i++) {
+                mergedLocalInsetsSourceProviders.put(
+                        mLocalInsetsSourceProviders.keyAt(i),
+                        mLocalInsetsSourceProviders.valueAt(i));
+            }
+        }
+
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            mChildren.get(i).updateAboveInsetsState(aboveInsetsState,
+                    mergedLocalInsetsSourceProviders, insetsChangedWindows);
+        }
+    }
+
+    static <T> SparseArray<T> createShallowCopy(SparseArray<T> inputArray) {
+        SparseArray<T> copyOfInput = new SparseArray<>(inputArray.size());
+        for (int i = 0; i < inputArray.size(); i++) {
+            copyOfInput.append(inputArray.keyAt(i), inputArray.valueAt(i));
+        }
+        return copyOfInput;
+    }
+
+    /**
+     * Sets the given {@code providerFrame} as one of the insets provider for this window
+     * container. These insets are only passed to the subtree of the current WindowContainer.
+     * For a given WindowContainer-to-Leaf path, one insetsType can't be overridden more than once.
+     * If that happens, only the latest one will be chosen.
+     *
+     * @param providerFrame the frame that will act as one of the insets providers for this window
+     *                      container
+     * @param insetsTypes the insets type which the providerFrame provides
+     */
+    void addLocalRectInsetsSourceProvider(Rect providerFrame,
+            @InsetsState.InternalInsetsType int[] insetsTypes) {
+        if (insetsTypes == null || insetsTypes.length == 0) {
+            throw new IllegalArgumentException("Insets type not specified.");
+        }
+        if (mLocalInsetsSourceProviders == null) {
+            mLocalInsetsSourceProviders = new SparseArray<>();
+        }
+        for (int i = 0; i < insetsTypes.length; i++) {
+            InsetsSourceProvider insetsSourceProvider =
+                    mLocalInsetsSourceProviders.get(insetsTypes[i]);
+            if (insetsSourceProvider != null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "The local insets provider for this type " + insetsTypes[i]
+                            + " already exists. Overwriting");
+                }
+            }
+            if (insetsSourceProvider == null
+                    || !(insetsSourceProvider instanceof RectInsetsSourceProvider)) {
+                insetsSourceProvider =
+                        new RectInsetsSourceProvider(
+                                new InsetsSource(insetsTypes[i]),
+                                mDisplayContent.getInsetsStateController(),
+                                mDisplayContent);
+                mLocalInsetsSourceProviders.put(insetsTypes[i], insetsSourceProvider);
+            }
+            ((RectInsetsSourceProvider) insetsSourceProvider).setRect(providerFrame);
+        }
+        mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+    }
+
+    void removeLocalInsetsSourceProvider(@InsetsState.InternalInsetsType int[] insetsTypes) {
+        if (insetsTypes == null || insetsTypes.length == 0) {
+            throw new IllegalArgumentException("Insets type not specified.");
+        }
+        if (mLocalInsetsSourceProviders == null) {
+            return;
+        }
+
+        for (int i = 0; i < insetsTypes.length; i++) {
+            InsetsSourceProvider insetsSourceProvider =
+                    mLocalInsetsSourceProviders.get(insetsTypes[i]);
+            if (insetsSourceProvider == null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Given insets type " + insetsTypes[i] + " doesn't have a "
+                            + "local insetsSourceProvider.");
+                }
+                continue;
+            }
+            mLocalInsetsSourceProviders.remove(insetsTypes[i]);
+        }
+        mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+    }
+
+    /**
+     * Sets an {@link InsetsSourceProvider} to be associated with this {@code WindowContainer},
+     * but only if the provider itself is controllable, as one window can be the provider of more
+     * than one inset type (i.e. gesture insets). If this {code WindowContainer} is controllable,
+     * all its animations must be controlled by its control target, and the visibility of this
+     * {code WindowContainer} should be taken account into the state of the control target.
      *
      * @param insetProvider the provider which should not be visible to the client.
-     * @see #getInsetsState()
+     * @see WindowState#getInsetsState()
      */
     void setControllableInsetProvider(InsetsSourceProvider insetProvider) {
         mControllableInsetProvider = insetProvider;
@@ -3682,8 +3817,8 @@
                     new ArrayList<>(insetTypes.size());
 
             for (int insetType : insetTypes) {
-                InsetsSourceProvider insetProvider = getDisplayContent().getInsetsStateController()
-                        .getSourceProvider(insetType);
+                WindowContainerInsetsSourceProvider insetProvider = getDisplayContent()
+                        .getInsetsStateController().getSourceProvider(insetType);
 
                 // Will apply it immediately to current leash and to all future inset animations
                 // until we disable it.
diff --git a/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java b/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java
new file mode 100644
index 0000000..aa2e8f5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowContainerInsetsSourceProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.view.InsetsSource;
+
+/**
+ * Controller for a specific inset source on the server. It's called provider as it provides the
+ * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
+ */
+class WindowContainerInsetsSourceProvider extends InsetsSourceProvider {
+    // TODO(b/218734524): Move the window container specific stuff from InsetsSourceProvider to
+    //  this class.
+
+    WindowContainerInsetsSourceProvider(InsetsSource source,
+            InsetsStateController stateController, DisplayContent displayContent) {
+        super(source, stateController, displayContent);
+    }
+}
+
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4ff2b6a..5b1021e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1823,7 +1823,7 @@
 
             // This window doesn't have a frame yet. Don't let this window cause the insets change.
             displayContent.getInsetsStateController().updateAboveInsetsState(
-                    win, false /* notifyInsetsChanged */);
+                    false /* notifyInsetsChanged */);
 
             outInsetsState.set(win.getCompatInsetsState(), win.isClientLocal());
             getInsetsSourceControls(win, outActiveControls);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1ab8cbf..87ef09e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -214,9 +214,11 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
@@ -680,6 +682,11 @@
     final InsetsState mAboveInsetsState = new InsetsState();
 
     /**
+     * The insets state of sources provided by the overrides set on any parent up the hierarchy.
+     */
+    SparseArray<InsetsSource> mMergedLocalInsetsSources = null;
+
+    /**
      * Surface insets from the previous call to relayout(), used to track
      * if we are changing the Surface insets.
      */
@@ -1662,13 +1669,10 @@
             return insetsPolicy.adjustInsetsForWindow(this, rotatedState);
         }
         final InsetsSourceProvider provider = getControllableInsetProvider();
-        final InsetsStateController insetsStateController = getDisplayContent()
-                .getInsetsStateController();
         final @InternalInsetsType int insetTypeProvidedByWindow = provider != null
                 ? provider.getSource().getType() : ITYPE_INVALID;
-        final InsetsState rawInsetsState = getFrozenInsetsState() != null
-                ? getFrozenInsetsState() : (mAttrs.receiveInsetsIgnoringZOrder
-                ? insetsStateController.getRawInsetsState() : mAboveInsetsState);
+        final InsetsState rawInsetsState =
+                mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState();
         final InsetsState insetsStateForWindow = insetsPolicy
                 .enforceInsetsPolicyForTarget(insetTypeProvidedByWindow,
                         getWindowingMode(), isAlwaysOnTop(), rawInsetsState);
@@ -1676,6 +1680,21 @@
                 includeTransient);
     }
 
+    private InsetsState getMergedInsetsState() {
+        final InsetsState globalInsetsState = mAttrs.receiveInsetsIgnoringZOrder
+                ? getDisplayContent().getInsetsStateController().getRawInsetsState()
+                : mAboveInsetsState;
+        if (mMergedLocalInsetsSources == null) {
+            return globalInsetsState;
+        }
+
+        final InsetsState mergedInsetsState = new InsetsState(globalInsetsState);
+        for (int i = 0; i < mMergedLocalInsetsSources.size(); i++) {
+            mergedInsetsState.addSource(mMergedLocalInsetsSources.valueAt(i));
+        }
+        return mergedInsetsState;
+    }
+
     /**
      * Returns the insets state for the client and scales the frames if the client is in the size
      * compatible mode.
@@ -4725,6 +4744,58 @@
         return false;
     }
 
+    @Override
+    void updateAboveInsetsState(InsetsState aboveInsetsState,
+            SparseArray<InsetsSourceProvider> localInsetsSourceProvidersFromParent,
+            ArraySet<WindowState> insetsChangedWindows) {
+        SparseArray<InsetsSourceProvider> mergedLocalInsetsSourceProviders =
+                localInsetsSourceProvidersFromParent;
+        if (mLocalInsetsSourceProviders != null && mLocalInsetsSourceProviders.size() != 0) {
+            mergedLocalInsetsSourceProviders = createShallowCopy(mergedLocalInsetsSourceProviders);
+            for (int i = 0; i < mLocalInsetsSourceProviders.size(); i++) {
+                mergedLocalInsetsSourceProviders.put(
+                        mLocalInsetsSourceProviders.keyAt(i),
+                        mLocalInsetsSourceProviders.valueAt(i));
+            }
+        }
+        final SparseArray<InsetsSource> mergedLocalInsetsSourcesFromParent =
+                toInsetsSources(mergedLocalInsetsSourceProviders);
+
+        // Insets provided by the IME window can effect all the windows below it and hence it needs
+        // to be visited in the correct order. Because of which updateAboveInsetsState() can't be
+        // used here and instead forAllWindows() is used.
+        forAllWindows(w -> {
+            if (!w.mAboveInsetsState.equals(aboveInsetsState)) {
+                w.mAboveInsetsState.set(aboveInsetsState);
+                insetsChangedWindows.add(w);
+            }
+
+            if (!mergedLocalInsetsSourcesFromParent.contentEquals(w.mMergedLocalInsetsSources)) {
+                w.mMergedLocalInsetsSources = createShallowCopy(
+                        mergedLocalInsetsSourcesFromParent);
+                insetsChangedWindows.add(w);
+            }
+
+            final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources;
+            if (providedSources != null) {
+                for (int i = providedSources.size() - 1; i >= 0; i--) {
+                    aboveInsetsState.addSource(providedSources.valueAt(i));
+                }
+            }
+        }, true /* traverseTopToBottom */);
+    }
+
+    private static SparseArray<InsetsSource> toInsetsSources(
+            SparseArray<InsetsSourceProvider> insetsSourceProviders) {
+        final SparseArray<InsetsSource> insetsSources = new SparseArray<>(
+                insetsSourceProviders.size());
+        for (int i = 0; i < insetsSourceProviders.size(); i++) {
+            insetsSources.append(insetsSourceProviders.keyAt(i),
+                    insetsSourceProviders.valueAt(i).getSource());
+        }
+        return insetsSources;
+    }
+
     private boolean forAllWindowTopToBottom(ToBooleanFunction<WindowState> callback) {
         // We want to consume the positive sublayer children first because they need to appear
         // above the parent, then this window (the parent), and then the negative sublayer children
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2e801ab..e895d37 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2707,15 +2707,6 @@
             systemTheme.rebase();
         }
 
-        t.traceBegin("MakePowerManagerServiceReady");
-        try {
-            // TODO: use boot phase
-            mPowerManagerService.systemReady();
-        } catch (Throwable e) {
-            reportWtf("making Power Manager Service ready", e);
-        }
-        t.traceEnd();
-
         // Permission policy service
         t.traceBegin("StartPermissionPolicyService");
         mSystemServiceManager.startService(PermissionPolicyService.class);
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index f72b23c..bcdbc5d 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -63,6 +63,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.UUID;
@@ -128,6 +129,19 @@
 
     private final PackageManager mPackageManager;
 
+    private static final String MIDI_LEGACY_STRING = "MIDI 1.0";
+    private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0";
+
+    // Used to lock mUsbMidiLegacyDeviceOpenCount and mUsbMidiUniversalDeviceInUse.
+    private final Object mUsbMidiLock = new Object();
+
+    // Number of times a USB MIDI 1.0 device has opened, based on the device name.
+    private final HashMap<String, Integer> mUsbMidiLegacyDeviceOpenCount =
+            new HashMap<String, Integer>();
+
+    // Whether a USB MIDI device has opened, based on the device name.
+    private final HashSet<String> mUsbMidiUniversalDeviceInUse = new HashSet<String>();
+
     // UID of BluetoothMidiService
     private int mBluetoothServiceUid;
 
@@ -271,6 +285,12 @@
             }
 
             for (DeviceConnection connection : mDeviceConnections.values()) {
+                if (connection.getDevice().getDeviceInfo().getType()
+                        == MidiDeviceInfo.TYPE_USB) {
+                    synchronized (mUsbMidiLock) {
+                        removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo());
+                    }
+                }
                 connection.getDevice().removeDeviceConnection(connection);
             }
         }
@@ -784,6 +804,15 @@
             }
         }
 
+        if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) {
+            synchronized (mUsbMidiLock) {
+                if (isUsbMidiDeviceInUseLocked(deviceInfo)) {
+                    throw new IllegalArgumentException("device already in use: " + deviceInfo);
+                }
+                addUsbMidiDeviceLocked(deviceInfo);
+            }
+        }
+
         // clear calling identity so bindService does not fail
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -1216,4 +1245,82 @@
         }
         pw.decreaseIndent();
     }
+
+    // hold mUsbMidiLock before calling this
+    private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return false;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        // Only one MIDI 2.0 device can be used at once.
+        // Multiple MIDI 1.0 devices can be used at once.
+        if (mUsbMidiUniversalDeviceInUse.contains(deviceName)
+                || ((tagName).equals(MIDI_UNIVERSAL_STRING)
+                && (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)))) {
+            return true;
+        }
+        return false;
+    }
+
+    // hold mUsbMidiLock before calling this
+    void addUsbMidiDeviceLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+            mUsbMidiUniversalDeviceInUse.add(deviceName);
+        } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+            int count = mUsbMidiLegacyDeviceOpenCount.getOrDefault(deviceName, 0) + 1;
+            mUsbMidiLegacyDeviceOpenCount.put(deviceName, count);
+        }
+    }
+
+    // hold mUsbMidiLock before calling this
+    void removeUsbMidiDeviceLocked(MidiDeviceInfo info) {
+        String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
+        if (name.length() < MIDI_LEGACY_STRING.length()) {
+            return;
+        }
+        String deviceName = extractUsbDeviceName(name);
+        String tagName = extractUsbDeviceTag(name);
+
+        if ((tagName).equals(MIDI_UNIVERSAL_STRING)) {
+            mUsbMidiUniversalDeviceInUse.remove(deviceName);
+        } else if ((tagName).equals(MIDI_LEGACY_STRING)) {
+            if (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)) {
+                int count = mUsbMidiLegacyDeviceOpenCount.get(deviceName);
+                if (count > 1) {
+                    mUsbMidiLegacyDeviceOpenCount.put(deviceName, count - 1);
+                } else {
+                    mUsbMidiLegacyDeviceOpenCount.remove(deviceName);
+                }
+            }
+        }
+    }
+
+    // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+    // This is defined in UsbDirectMidiDevice.java.
+    // This function extracts out the "manufacturer product#Id " part.
+    // Two devices would have the same device name if they had the following property name:
+    // "manufacturer product#Id MIDI 1.0"
+    // "manufacturer product#Id MIDI 2.0"
+    // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+    String extractUsbDeviceName(String propertyName) {
+        return propertyName.substring(0, propertyName.length() - MIDI_LEGACY_STRING.length());
+    }
+
+    // The USB property name is in the form "manufacturer product#Id MIDI 1.0".
+    // This is defined in UsbDirectMidiDevice.java.
+    // This function extracts the "MIDI 1.0" part.
+    // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length.
+    String extractUsbDeviceTag(String propertyName) {
+        return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length());
+    }
 }
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index c5709fc..43b2e1e 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -29,6 +29,7 @@
 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 com.android.server.pm.resolution.ComponentResolver
 import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
 import com.android.server.testutils.TestHandler
 import com.android.server.testutils.mock
@@ -46,6 +47,8 @@
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.intThat
 import org.mockito.Mockito.never
 import org.mockito.Mockito.same
@@ -337,8 +340,8 @@
         val mockSettings = Settings(mockedPkgSettings)
         val mockComponentResolver: ComponentResolver = mockThrowOnUnmocked {
             params.componentName?.let {
-                whenever(this.componentExists(same(it))) { mockActivity != null }
-                whenever(this.getActivity(same(it))) { mockActivity }
+                doReturn(mockActivity != null).`when`(this).componentExists(same(it))
+                doReturn(mockActivity).`when`(this).getActivity(same(it))
             }
             whenever(this.snapshot()) { this@mockThrowOnUnmocked }
             whenever(registerObserver(any())).thenCallRealMethod()
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 4ec1641..ca5bf20 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -68,6 +68,7 @@
 import com.android.server.pm.permission.PermissionManagerServiceInternal
 import com.android.server.pm.pkg.parsing.ParsingPackage
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils
+import com.android.server.pm.resolution.ComponentResolver
 import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
 import com.android.server.supplementalprocess.SupplementalProcessManagerLocal
 import com.android.server.testutils.TestHandler
@@ -632,7 +633,7 @@
     }
 
     private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) {
-        whenever(mocks.componentResolver.queryActivities(
+        whenever(mocks.componentResolver.queryActivities(any(),
                 argThat { intent: Intent? -> intent != null && (action == intent.action) },
                 nullable(), anyLong(), anyInt())) {
             ArrayList(activities.asList().map { info: ActivityInfo? ->
@@ -642,7 +643,7 @@
     }
 
     private fun mockQueryServices(action: String, vararg services: ServiceInfo) {
-        whenever(mocks.componentResolver.queryServices(
+        whenever(mocks.componentResolver.queryServices(any(),
                 argThat { intent: Intent? -> intent != null && (action == intent.action) },
                 nullable(), anyLong(), anyInt())) {
             ArrayList(services.asList().map { info ->
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
index 0411b94..9cf6c03 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
@@ -263,7 +263,7 @@
     @Test
     public void testUserActivityOnDeviceStateChange() {
         createService();
-        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
 
         final DisplayInfo info = new DisplayInfo();
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
index 64e8613..8b7cc74 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -18,8 +18,8 @@
 
 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
 import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.HARDWARE;
-import static android.hardware.SensorPrivacyManager.ToggleTypes.SOFTWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
+import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -102,106 +102,106 @@
     public void testMigration1() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE1);
 
-        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
-        assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     @Test
     public void testMigration2() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE2);
 
-        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
-        assertTrue(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertTrue(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
-        assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 11, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 11, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 11, CAMERA));
 
-        assertTrue(ps.getState(SOFTWARE, 12, MICROPHONE).isEnabled());
-        assertNull(ps.getState(SOFTWARE, 12, CAMERA));
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 12, MICROPHONE).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 12, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
-        assertNull(ps.getState(HARDWARE, 11, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 11, CAMERA));
-        assertNull(ps.getState(HARDWARE, 12, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 12, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 11, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 12, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 12, CAMERA));
     }
 
     @Test
     public void testMigration3() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE3);
 
-        assertFalse(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
-        assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     @Test
     public void testMigration4() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE4);
 
-        assertTrue(ps.getState(SOFTWARE, 0, MICROPHONE).isEnabled());
-        assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertTrue(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).isEnabled());
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertFalse(ps.getState(SOFTWARE, 10, MICROPHONE).isEnabled());
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     @Test
     public void testMigration5() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE5);
 
-        assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
-        assertFalse(ps.getState(SOFTWARE, 0, CAMERA).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).isEnabled());
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertFalse(ps.getState(SOFTWARE, 10, CAMERA).isEnabled());
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertFalse(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA).isEnabled());
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     @Test
     public void testMigration6() throws IOException {
         PersistedState ps = migrateFromFile(PERSISTENCE_FILE6);
 
-        assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA));
 
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     private PersistedState migrateFromFile(String fileName) throws IOException {
@@ -233,32 +233,32 @@
     public void testPersistence1Version2() throws IOException {
         PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE7);
 
-        assertEquals(1, ps.getState(SOFTWARE, 0, MICROPHONE).getState());
-        assertEquals(123L, ps.getState(SOFTWARE, 0, MICROPHONE).getLastChange());
-        assertEquals(2, ps.getState(SOFTWARE, 0, CAMERA).getState());
-        assertEquals(123L, ps.getState(SOFTWARE, 0, CAMERA).getLastChange());
+        assertEquals(1, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getState());
+        assertEquals(123L, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE).getLastChange());
+        assertEquals(2, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).getState());
+        assertEquals(123L, ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA).getLastChange());
 
-        assertNull(ps.getState(HARDWARE, 0, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 0, CAMERA));
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
     }
 
     @Test
     public void testPersistence2Version2() throws IOException {
         PersistedState ps = getPersistedStateV2(PERSISTENCE_FILE8);
 
-        assertEquals(1, ps.getState(HARDWARE, 0, MICROPHONE).getState());
-        assertEquals(1234L, ps.getState(HARDWARE, 0, MICROPHONE).getLastChange());
-        assertEquals(2, ps.getState(HARDWARE, 0, CAMERA).getState());
-        assertEquals(1234L, ps.getState(HARDWARE, 0, CAMERA).getLastChange());
+        assertEquals(1, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getState());
+        assertEquals(1234L, ps.getState(TOGGLE_TYPE_HARDWARE, 0, MICROPHONE).getLastChange());
+        assertEquals(2, ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA).getState());
+        assertEquals(1234L, ps.getState(TOGGLE_TYPE_HARDWARE, 0, CAMERA).getLastChange());
 
-        assertNull(ps.getState(SOFTWARE, 0, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 0, CAMERA));
-        assertNull(ps.getState(SOFTWARE, 10, MICROPHONE));
-        assertNull(ps.getState(SOFTWARE, 10, CAMERA));
-        assertNull(ps.getState(HARDWARE, 10, MICROPHONE));
-        assertNull(ps.getState(HARDWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_SOFTWARE, 10, CAMERA));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, MICROPHONE));
+        assertNull(ps.getState(TOGGLE_TYPE_HARDWARE, 10, CAMERA));
     }
 
     private PersistedState getPersistedStateV2(String version2FilePath) throws IOException {
@@ -296,13 +296,15 @@
             SensorPrivacyStateController sensorPrivacyStateController =
                     getSensorPrivacyStateControllerImpl();
 
-            SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
-            SensorState camState = sensorPrivacyStateController.getState(SOFTWARE, 0, CAMERA);
+            SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    MICROPHONE);
+            SensorState camState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    CAMERA);
 
             assertEquals(SensorPrivacyManager.StateTypes.DISABLED, micState.getState());
             assertEquals(SensorPrivacyManager.StateTypes.DISABLED, camState.getState());
-            verify(persistedState, times(1)).getState(SOFTWARE, 0, MICROPHONE);
-            verify(persistedState, times(1)).getState(SOFTWARE, 0, CAMERA);
+            verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE);
+            verify(persistedState, times(1)).getState(TOGGLE_TYPE_SOFTWARE, 0, CAMERA);
         } finally {
             mockitoSession.finishMocking();
         }
@@ -319,14 +321,16 @@
             PersistedState persistedState = mock(PersistedState.class);
             SensorState sensorState = mock(SensorState.class);
             doReturn(persistedState).when(() -> PersistedState.fromFile(any()));
-            doReturn(sensorState).when(persistedState).getState(SOFTWARE, 0, MICROPHONE);
+            doReturn(sensorState).when(persistedState).getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    MICROPHONE);
             doReturn(SensorPrivacyManager.StateTypes.ENABLED).when(sensorState).getState();
             doReturn(0L).when(sensorState).getLastChange();
 
             SensorPrivacyStateController sensorPrivacyStateController =
                     getSensorPrivacyStateControllerImpl();
 
-            SensorState micState = sensorPrivacyStateController.getState(SOFTWARE, 0, MICROPHONE);
+            SensorState micState = sensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, 0,
+                    MICROPHONE);
 
             assertEquals(SensorPrivacyManager.StateTypes.ENABLED, micState.getState());
             assertEquals(0L, micState.getLastChange());
@@ -349,13 +353,13 @@
             SensorPrivacyStateController sensorPrivacyStateController =
                     getSensorPrivacyStateControllerImpl();
 
-            sensorPrivacyStateController.setState(SOFTWARE, 0, MICROPHONE, true,
+            sensorPrivacyStateController.setState(TOGGLE_TYPE_SOFTWARE, 0, MICROPHONE, true,
                     mock(Handler.class), changed -> {});
 
             ArgumentCaptor<SensorState> captor = ArgumentCaptor.forClass(SensorState.class);
 
-            verify(persistedState, times(1)).setState(eq(SOFTWARE), eq(0), eq(MICROPHONE),
-                    captor.capture());
+            verify(persistedState, times(1)).setState(eq(TOGGLE_TYPE_SOFTWARE), eq(0),
+                    eq(MICROPHONE), captor.capture());
             assertEquals(SensorPrivacyManager.StateTypes.ENABLED, captor.getValue().getState());
         } finally {
             mockitoSession.finishMocking();
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index 68994e6..177d72b 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -1,5 +1,6 @@
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
+per-file *BinaryTransparency* = file:/core/java/android/transparency/OWNERS
 per-file *Bluetooth* = file:platform/packages/modules/Bluetooth:master:/framework/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 8873f42..6789af4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -225,7 +225,11 @@
         assertThat(packageUserState1.isSuspended(), is(true));
         assertThat(packageUserState1.getSuspendParams().size(), is(1));
         assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
-        assertThat(packageUserState1.getSuspendParams().valueAt(0), is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
+                is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getLauncherExtras(),
+                is(nullValue()));
 
         // Verify that the snapshot returns the same answers
         ps1 = snapshot.mPackages.get(PACKAGE_NAME_1);
@@ -233,7 +237,11 @@
         assertThat(packageUserState1.isSuspended(), is(true));
         assertThat(packageUserState1.getSuspendParams().size(), is(1));
         assertThat(packageUserState1.getSuspendParams().keyAt(0), is("android"));
-        assertThat(packageUserState1.getSuspendParams().valueAt(0), is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getAppExtras(), is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getDialogInfo(),
+                is(nullValue()));
+        assertThat(packageUserState1.getSuspendParams().valueAt(0).getLauncherExtras(),
+                is(nullValue()));
 
         PackageSetting ps2 = settingsUnderTest.mPackages.get(PACKAGE_NAME_2);
         PackageUserStateInternal packageUserState2 = ps2.readUserState(0);
@@ -315,14 +323,14 @@
                 .build();
 
         ps1.modifyUserState(0).putSuspendParams( "suspendingPackage1",
-                SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1));
+                new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
         ps1.modifyUserState(0).putSuspendParams( "suspendingPackage2",
-                SuspendParams.getInstanceOrNull(dialogInfo2, appExtras2, launcherExtras2));
+                new SuspendParams(dialogInfo2, appExtras2, launcherExtras2));
         settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
         watcher.verifyChangeReported("put package 1");
 
         ps2.modifyUserState(0).putSuspendParams( "suspendingPackage3",
-                SuspendParams.getInstanceOrNull(null, appExtras1, null));
+                new SuspendParams(null, appExtras1, null));
         settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
         watcher.verifyChangeReported("put package 2");
 
@@ -686,7 +694,7 @@
                 .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS)
                 .build();
         origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1",
-                SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1));
+                new SuspendParams(dialogInfo1, appExtras1, launcherExtras1));
         final PackageSetting testPkgSetting01 = new PackageSetting(
                 PACKAGE_NAME /*pkgName*/,
                 REAL_PACKAGE_NAME /*realPkgName*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 4dc9612..9ad503c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -85,7 +85,7 @@
 
         oldUserState = new PackageUserStateImpl();
         oldUserState.putSuspendParams("suspendingPackage",
-                SuspendParams.getInstanceOrNull(null, new PersistableBundle(), null));
+                new SuspendParams(null, new PersistableBundle(), null));
         assertThat(testUserState.equals(oldUserState), is(false));
 
         oldUserState = new PackageUserStateImpl();
@@ -185,7 +185,7 @@
 
     private static SuspendParams createSuspendParams(SuspendDialogInfo dialogInfo,
             PersistableBundle appExtras, PersistableBundle launcherExtras) {
-        return SuspendParams.getInstanceOrNull(dialogInfo, appExtras, launcherExtras);
+        return new SuspendParams(dialogInfo, appExtras, launcherExtras);
     }
 
     private static PersistableBundle createPersistableBundle(String lKey, long lValue, String sKey,
diff --git a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
index 95af1e1..6e03569 100644
--- a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java
@@ -145,7 +145,7 @@
         IntentFilter i = new IntentFilter("TEST_ACTION");
         PreferredActivity a1 = new PreferredActivity(i, 1, components, component, true);
 
-        r.addFilter(a1);
+        r.addFilter(null, a1);
         watcher.verifyChangeReported("addFilter");
         i.setPriority(i.getPriority() + 1);
         watcher.verifyNoChangeReported("indepenent intent");
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index c94168c..1b92017 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -321,7 +321,7 @@
     }
 
     private void startSystem() {
-        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         // Grab the BatteryReceiver
         ArgumentCaptor<BatteryReceiver> batCaptor = ArgumentCaptor.forClass(BatteryReceiver.class);
@@ -439,7 +439,7 @@
     @Test
     public void testGetDesiredScreenPolicy_WithVR() {
         createService();
-        mService.systemReady();
+        startSystem();
         // Brighten up the screen
         mService.setWakefulnessLocked(Display.DEFAULT_DISPLAY_GROUP, WAKEFULNESS_AWAKE, 0, 0, 0, 0,
                 null, null);
@@ -627,8 +627,7 @@
     public void testWasDeviceIdleFor_true() {
         int interval = 1000;
         createService();
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
         mService.onUserActivity();
         advanceTime(interval + 1 /* just a little more */);
         assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue();
@@ -638,8 +637,7 @@
     public void testWasDeviceIdleFor_false() {
         int interval = 1000;
         createService();
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
         mService.onUserActivity();
         assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse();
     }
@@ -647,8 +645,7 @@
     @Test
     public void testForceSuspend_putsDeviceToSleep() {
         createService();
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
 
         // Verify that we start awake
         assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -693,8 +690,7 @@
         //
         // TEST STARTS HERE
         //
-        mService.systemReady();
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        startSystem();
 
         // Verify that we start awake
         assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
@@ -792,7 +788,7 @@
         createService();
         assertTrue(isAcquired[0]);
 
-        mService.systemReady();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         assertTrue(isAcquired[0]);
 
         mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -1231,7 +1227,7 @@
     public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted() {
         when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
         createService();
-        mService.systemReady();
+        startSystem();
 
         mService.getBinderServiceInstance().wakeUp(mClock.now(),
                 PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
@@ -1444,7 +1440,7 @@
     @Test
     public void testSetPowerBoost_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         mService.getBinderServiceInstance().setPowerBoost(Boost.INTERACTION, 1234);
 
@@ -1454,7 +1450,7 @@
     @Test
     public void testSetPowerMode_redirectsCallToNativeWrapper() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         // Enabled launch boost in BatterySaverController to allow setting launch mode.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(false);
@@ -1470,7 +1466,7 @@
     @Test
     public void testSetPowerMode_withLaunchBoostDisabledAndModeLaunch_ignoresCallToEnable() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1486,7 +1482,7 @@
     @Test
     public void testSetPowerModeChecked_returnsNativeCallResult() {
         createService();
-        mService.systemReady();
+        startSystem();
 
         // Disables launch boost in BatterySaverController.
         when(mBatterySaverControllerMock.isLaunchBoostDisabled()).thenReturn(true);
@@ -1649,7 +1645,7 @@
     @Test
     public void testGetFullPowerSavePolicy_returnsStateMachineResult() {
         createService();
-        mService.systemReady();
+        startSystem();
         BatterySaverPolicyConfig mockReturnConfig = new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.getFullBatterySaverPolicy())
                 .thenReturn(mockReturnConfig);
@@ -1664,7 +1660,7 @@
     @Test
     public void testSetFullPowerSavePolicy_callsStateMachine() {
         createService();
-        mService.systemReady();
+        startSystem();
         BatterySaverPolicyConfig mockSetPolicyConfig =
                 new BatterySaverPolicyConfig.Builder().build();
         when(mBatterySaverStateMachineMock.setFullBatterySaverPolicy(any())).thenReturn(true);
@@ -1678,7 +1674,7 @@
     @Test
     public void testLowPowerStandby_whenInactive_FgsWakeLockEnabled() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
         mService.setDeviceIdleModeInternal(true);
@@ -1689,7 +1685,7 @@
     @Test
     public void testLowPowerStandby_whenActive_FgsWakeLockDisabled() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
         mService.setDeviceIdleModeInternal(true);
@@ -1701,7 +1697,7 @@
     @Test
     public void testLowPowerStandby_whenActive_FgsWakeLockEnabledIfAllowlisted() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("fgsWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_FOREGROUND_SERVICE);
         mService.setDeviceIdleModeInternal(true);
@@ -1714,7 +1710,7 @@
     @Test
     public void testLowPowerStandby_whenActive_BoundTopWakeLockDisabled() {
         createService();
-        mService.systemReady();
+        startSystem();
         WakeLock wakeLock = acquireWakeLock("BoundTopWakeLock", PowerManager.PARTIAL_WAKE_LOCK);
         mService.updateUidProcStateInternal(wakeLock.mOwnerUid, PROCESS_STATE_BOUND_TOP);
         mService.setDeviceIdleModeInternal(true);
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
index 16cfd13..6bdd88c 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundHw2CompatTest.java
@@ -31,7 +31,6 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -49,7 +48,6 @@
 import android.os.IHwBinder;
 import android.os.IHwInterface;
 import android.os.RemoteException;
-import android.system.OsConstants;
 
 import org.junit.After;
 import org.junit.Before;
@@ -60,6 +58,7 @@
 
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 @RunWith(Parameterized.class)
 public class SoundHw2CompatTest {
@@ -240,14 +239,15 @@
                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
 
         final int handle = 29;
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
-                ArgumentCaptor.forClass(
-                        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
+        AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> model =
+                new AtomicReference<>();
         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
                 ArgumentCaptor.forClass(
                         android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
 
         doAnswer(invocation -> {
+            // We need to dup the model, as it gets invalidated after the call returns.
+            model.set(TestUtil.dupModel_2_1(invocation.getArgument(0)));
             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
                     resultCallback = invocation.getArgument(3);
 
@@ -259,10 +259,9 @@
         assertEquals(handle,
                 mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
 
-        verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
-                anyInt(), any());
+        verify(driver_2_1).loadSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(), any());
 
-        TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
+        TestUtil.validateGenericSoundModel_2_1(model.get());
         validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
         return handle;
     }
@@ -355,14 +354,16 @@
                 (android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
 
         final int handle = 29;
-        ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
-                modelCaptor = ArgumentCaptor.forClass(
-                android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
+        AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel> model =
+                new AtomicReference<>();
         ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
                 ArgumentCaptor.forClass(
                         android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
 
         doAnswer(invocation -> {
+            // We need to dup the model, as it gets invalidated after the call returns.
+            model.set(TestUtil.dupPhraseModel_2_1(invocation.getArgument(0)));
+
             android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
                     resultCallback = invocation.getArgument(3);
 
@@ -374,10 +375,10 @@
         assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
                 canonicalCallback));
 
-        verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
-                anyInt(), any());
+        verify(driver_2_1).loadPhraseSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(),
+                any());
 
-        TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
+        TestUtil.validatePhraseSoundModel_2_1(model.get());
         validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
         return handle;
     }
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index e687a2a..30b4a59 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -47,6 +47,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.SharedMemory;
 
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.List;
 
@@ -82,6 +83,19 @@
                 HidlMemoryUtil.hidlMemoryToByteArray(model.data));
     }
 
+    static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dupModel_2_1(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel model) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dup =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel();
+        dup.header = model.header;
+        try {
+            dup.data = model.data.dup();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return dup;
+    }
+
     private static void validateSoundModel_2_0(
             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
         assertEquals(type, model.type);
@@ -121,6 +135,15 @@
         validatePhrases_2_0(model.phrases);
     }
 
+    static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dupPhraseModel_2_1(
+            android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel model) {
+        android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dup =
+                new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel();
+        dup.common = dupModel_2_1(model.common);
+        dup.phrases = model.phrases;
+        return dup;
+    }
+
     static void validatePhraseSoundModel_2_0(
             android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
         validateSoundModel_2_0(model.common,
@@ -190,31 +213,27 @@
         properties.maxKeyPhrases = 567;
         properties.maxUsers = 678;
         properties.recognitionModes =
-                RecognitionMode.VOICE_TRIGGER
-                        | RecognitionMode.USER_IDENTIFICATION
-                        | RecognitionMode.USER_AUTHENTICATION
-                        | RecognitionMode.GENERIC_TRIGGER;
+                RecognitionMode.VOICE_TRIGGER | RecognitionMode.USER_IDENTIFICATION
+                        | RecognitionMode.USER_AUTHENTICATION | RecognitionMode.GENERIC_TRIGGER;
         properties.captureTransition = true;
         properties.maxBufferMs = 321;
         properties.concurrentCapture = supportConcurrentCapture;
         properties.triggerInEvent = true;
         properties.powerConsumptionMw = 432;
         properties.supportedModelArch = "supportedModelArch";
-        properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION;
+        properties.audioCapabilities =
+                AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
         return properties;
     }
 
-    static void validateDefaultProperties(Properties properties,
-            boolean supportConcurrentCapture) {
+    static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture) {
         validateDefaultProperties(properties, supportConcurrentCapture,
                 AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
                 "supportedModelArch");
     }
 
-    static void validateDefaultProperties(Properties properties,
-            boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
-            @NonNull String supportedModelArch) {
+    static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture,
+            @AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch) {
         assertEquals("implementor", properties.implementor);
         assertEquals("description", properties.description);
         assertEquals(123, properties.version);
@@ -222,10 +241,9 @@
         assertEquals(456, properties.maxSoundModels);
         assertEquals(567, properties.maxKeyPhrases);
         assertEquals(678, properties.maxUsers);
-        assertEquals(RecognitionMode.GENERIC_TRIGGER
-                | RecognitionMode.USER_AUTHENTICATION
-                | RecognitionMode.USER_IDENTIFICATION
-                | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
+        assertEquals(RecognitionMode.GENERIC_TRIGGER | RecognitionMode.USER_AUTHENTICATION
+                        | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.VOICE_TRIGGER,
+                properties.recognitionModes);
         assertTrue(properties.captureTransition);
         assertEquals(321, properties.maxBufferMs);
         assertEquals(supportConcurrentCapture, properties.concurrentCapture);
@@ -246,8 +264,8 @@
         config.phraseRecognitionExtras[0].levels[0].userId = 234;
         config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
         config.data = new byte[]{5, 4, 3, 2, 1};
-        config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION;
+        config.audioCapabilities =
+                AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
         return config;
     }
 
@@ -295,13 +313,12 @@
             int captureHandle) {
         validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
 
-        assertEquals(AudioCapabilities.ECHO_CANCELLATION
-                | AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
+        assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
+                config.audioCapabilities);
     }
 
     static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
-            int hwHandle,
-            int status) {
+            int hwHandle, int status) {
         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
                 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
         halEvent.status = status;
@@ -351,8 +368,7 @@
         return event;
     }
 
-    static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
-            int hwHandle,
+    static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(int hwHandle,
             int status) {
         ISoundTriggerHwCallback.RecognitionEvent halEvent =
                 new ISoundTriggerHwCallback.RecognitionEvent();
@@ -386,8 +402,7 @@
         PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
         extra.id = 123;
         extra.confidenceLevel = 52;
-        extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
-                | RecognitionMode.GENERIC_TRIGGER;
+        extra.recognitionModes = RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER;
         ConfidenceLevel level = new ConfidenceLevel();
         level.userId = 31;
         level.levelPercent = 43;
@@ -396,8 +411,8 @@
         return event;
     }
 
-    static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
-    createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
+    static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
+            int hwHandle, int status) {
         android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
                 new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
         halEvent.common = createRecognitionEvent_2_0(hwHandle, status);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index ffc10d7..b3d6b3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -3125,7 +3125,7 @@
         assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
         verify(app2.mClient, atLeastOnce()).insetsChanged(insetsStateCaptor.capture(), anyBoolean(),
                 anyBoolean());
-        assertFalse(insetsStateCaptor.getAllValues().get(0).peekSource(ITYPE_IME).isVisible());
+        assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index c7a1b07..e387615 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -274,7 +274,7 @@
         // IME cannot be the IME target.
         ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
 
-        InsetsSourceProvider statusBarProvider =
+        WindowContainerInsetsSourceProvider statusBarProvider =
                 getController().getSourceProvider(ITYPE_STATUS_BAR);
         statusBarProvider.setWindowContainer(statusBar, null, ((displayFrames, windowState, rect) ->
                 rect.set(0, 1, 2, 3)));
@@ -336,7 +336,8 @@
     public void testTransientVisibilityOfFixedRotationState() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        final InsetsSourceProvider provider = getController().getSourceProvider(ITYPE_STATUS_BAR);
+        final WindowContainerInsetsSourceProvider provider = getController()
+                .getSourceProvider(ITYPE_STATUS_BAR);
         provider.setWindowContainer(statusBar, null, null);
 
         final InsetsState rotatedState = new InsetsState(app.getInsetsState(),
@@ -366,7 +367,7 @@
         assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
 
-        getController().updateAboveInsetsState(statusBar, true /* notifyInsetsChange */);
+        getController().updateAboveInsetsState(true /* notifyInsetsChange */);
 
         assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
@@ -389,7 +390,7 @@
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
 
-        getController().updateAboveInsetsState(app, true /* notifyInsetsChange */);
+        getController().updateAboveInsetsState(true /* notifyInsetsChange */);
 
         assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
         assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
@@ -410,9 +411,8 @@
                 null);
         getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
                 null);
-        getController().updateAboveInsetsState(ime, false /* notifyInsetsChange */);
-        getController().updateAboveInsetsState(statusBar, false /* notifyInsetsChange */);
-        getController().updateAboveInsetsState(navBar, false /* notifyInsetsChange */);
+
+        getController().updateAboveInsetsState(false /* notifyInsetsChange */);
 
         // ime is below others.
         assertNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
@@ -422,7 +422,7 @@
         assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
 
         ime.getParent().positionChildAt(POSITION_TOP, ime, true /* includingParents */);
-        getController().updateAboveInsetsState(ime, true /* notifyInsetsChange */);
+        getController().updateAboveInsetsState(true /* notifyInsetsChange */);
 
         // ime is above others.
         assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index b815c38..7b38a95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -31,6 +31,7 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
@@ -2128,6 +2129,136 @@
                 APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
     }
 
+    @Test
+    public void testIsEligibleForLetterboxEducation_educationNotEnabled_returnsFalse() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_notEligibleForFixedOrientation_returnsFalse() {
+        setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_windowingModeMultiWindow_returnsFalse() {
+        // Support non resizable in multi window
+        mAtm.mDevEnableNonResizableMultiWindow = true;
+        setUpDisplaySizeWithApp(1000, 1200);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+        final TestSplitOrganizer organizer =
+                new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+        // Non-resizable landscape activity
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        final Rect originalBounds = new Rect(mActivity.getBounds());
+
+        // Move activity to split screen which takes half of the screen.
+        mTask.reparent(organizer.mPrimary, POSITION_TOP,
+                false /*moveParents*/, "test");
+        organizer.mPrimary.setBounds(0, 0, 1000, 600);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+        assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_fixedOrientationLandscape_returnsFalse() {
+        setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_hasStartingWindow_returnsFalseUntilRemoved() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mStartingData = mock(StartingData.class);
+        mActivity.attachStartingWindow(
+                createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
+                        mActivity));
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+
+        // Verify that after removing the starting window isEligibleForLetterboxEducation returns
+        // true and mTask.dispatchTaskInfoChangedIfNeeded is called.
+        spyOn(mTask);
+        mActivity.removeStartingWindow();
+
+        assertTrue(mActivity.isEligibleForLetterboxEducation());
+        verify(mTask).dispatchTaskInfoChangedIfNeeded(true);
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_hasStartingWindowAndEducationNotEnabled() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(false);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mStartingData = mock(StartingData.class);
+        mActivity.attachStartingWindow(
+                createWindowState(new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING),
+                        mActivity));
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+
+        // Verify that after removing the starting window isEligibleForLetterboxEducation still
+        // returns false and mTask.dispatchTaskInfoChangedIfNeeded isn't called.
+        spyOn(mTask);
+        mActivity.removeStartingWindow();
+
+        assertFalse(mActivity.isEligibleForLetterboxEducation());
+        verify(mTask, never()).dispatchTaskInfoChangedIfNeeded(true);
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_letterboxedForFixedOrientation_returnsTrue() {
+        setUpDisplaySizeWithApp(2500, 1000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        assertTrue(mActivity.isEligibleForLetterboxEducation());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+    }
+
+    @Test
+    public void testIsEligibleForLetterboxEducation_sizeCompatAndEligibleForFixedOrientation() {
+        setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setIsEducationEnabled(true);
+
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+
+        assertTrue(mActivity.isEligibleForLetterboxEducation());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+        assertTrue(mActivity.inSizeCompatMode());
+    }
+
     /**
      * Tests that all three paths in which aspect ratio logic can be applied yield the same
      * result, which is that aspect ratio is respected on app bounds. The three paths are
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
similarity index 96%
rename from services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
index c61b88b..10e4292 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerInsetsSourceProviderTest.java
@@ -42,19 +42,19 @@
 @SmallTest
 @Presubmit
 @RunWith(WindowTestRunner.class)
-public class InsetsSourceProviderTest extends WindowTestsBase {
+public class WindowContainerInsetsSourceProviderTest extends WindowTestsBase {
 
     private InsetsSource mSource = new InsetsSource(ITYPE_STATUS_BAR);
-    private InsetsSourceProvider mProvider;
+    private WindowContainerInsetsSourceProvider mProvider;
     private InsetsSource mImeSource = new InsetsSource(ITYPE_IME);
-    private InsetsSourceProvider mImeProvider;
+    private WindowContainerInsetsSourceProvider mImeProvider;
 
     @Before
     public void setUp() throws Exception {
         mSource.setVisible(true);
-        mProvider = new InsetsSourceProvider(mSource,
+        mProvider = new WindowContainerInsetsSourceProvider(mSource,
                 mDisplayContent.getInsetsStateController(), mDisplayContent);
-        mImeProvider = new InsetsSourceProvider(mImeSource,
+        mImeProvider = new WindowContainerInsetsSourceProvider(mImeSource,
                 mDisplayContent.getInsetsStateController(), mDisplayContent);
     }
 
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 41a59eb..a442de5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,8 @@
 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.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_1;
+import static android.view.InsetsState.ITYPE_LOCAL_NAVIGATION_BAR_2;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -68,6 +70,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
+import android.view.InsetsSource;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -478,7 +481,7 @@
                 windowState.mSurfaceAnimator).getAnimationType();
         assertTrue(parent.isAnimating(CHILDREN));
 
-        windowState.setControllableInsetProvider(mock(InsetsSourceProvider.class));
+        windowState.setControllableInsetProvider(mock(WindowContainerInsetsSourceProvider.class));
         assertFalse(parent.isAnimating(CHILDREN));
     }
 
@@ -1267,6 +1270,140 @@
         verify(prevSnapshot).destroy(t);
     }
 
+    @Test
+    public void testAddLocalInsetsSourceProvider() {
+         /*
+                ___ rootTask _______________________________________________
+               |        |                |                                  |
+          activity0    container     navigationBarInsetsProvider1    navigationBarInsetsProvider2
+                       /       \
+               activity1    activity2
+         */
+        final Task rootTask = createTask(mDisplayContent);
+
+        final ActivityRecord activity0 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(rootTask, 0 /* userId */));
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs.setTitle("AppWindow0");
+        activity0.addWindow(createWindowState(attrs, activity0));
+
+        final Task container = createTaskInRootTask(rootTask, 0);
+        final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(container, 0 /* userId */));
+        final WindowManager.LayoutParams attrs1 = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs1.setTitle("AppWindow1");
+        activity1.addWindow(createWindowState(attrs1, activity1));
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(container, 0 /* userId */));
+        final WindowManager.LayoutParams attrs2 = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs2.setTitle("AppWindow2");
+        activity2.addWindow(createWindowState(attrs2, activity2));
+        Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700);
+        Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
+
+        rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1,
+                new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+        container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2,
+                new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2});
+
+        InsetsSource navigationBarInsetsProvider1Source = new InsetsSource(
+                ITYPE_LOCAL_NAVIGATION_BAR_1);
+        navigationBarInsetsProvider1Source.setFrame(navigationBarInsetsRect1);
+        navigationBarInsetsProvider1Source.setVisible(true);
+        InsetsSource navigationBarInsetsProvider2Source = new InsetsSource(
+                ITYPE_LOCAL_NAVIGATION_BAR_2);
+        navigationBarInsetsProvider2Source.setFrame(navigationBarInsetsRect2);
+        navigationBarInsetsProvider2Source.setVisible(true);
+
+        activity0.forAllWindows(window -> {
+            assertEquals(navigationBarInsetsRect1,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2));
+        }, true);
+        activity1.forAllWindows(window -> {
+            assertEquals(navigationBarInsetsRect1,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+            assertEquals(navigationBarInsetsRect2,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+        }, true);
+        activity2.forAllWindows(window -> {
+            assertEquals(navigationBarInsetsRect1,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1).getFrame());
+            assertEquals(navigationBarInsetsRect2,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+        }, true);
+    }
+
+    @Test
+    public void testRemoveLocalInsetsSourceProvider() {
+         /*
+                ___ rootTask _______________________________________________
+               |        |                |                                  |
+          activity0    container    navigationBarInsetsProvider1     navigationBarInsetsProvider2
+                       /       \
+               activity1    activity2
+         */
+        final Task rootTask = createTask(mDisplayContent);
+
+        final ActivityRecord activity0 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(rootTask, 0 /* userId */));
+        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs.setTitle("AppWindow0");
+        activity0.addWindow(createWindowState(attrs, activity0));
+
+        final Task container = createTaskInRootTask(rootTask, 0);
+        final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(container, 0 /* userId */));
+        final WindowManager.LayoutParams attrs1 = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs1.setTitle("AppWindow1");
+        activity1.addWindow(createWindowState(attrs1, activity1));
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent,
+                createTaskInRootTask(container, 0 /* userId */));
+        final WindowManager.LayoutParams attrs2 = new WindowManager.LayoutParams(
+                TYPE_BASE_APPLICATION);
+        attrs2.setTitle("AppWindow2");
+        activity2.addWindow(createWindowState(attrs2, activity2));
+
+        activity2.addWindow(createWindowState(attrs2, activity2));
+        Rect navigationBarInsetsRect1 = new Rect(0, 200, 1080, 700);
+        Rect navigationBarInsetsRect2 = new Rect(0, 0, 1080, 200);
+
+        rootTask.addLocalRectInsetsSourceProvider(navigationBarInsetsRect1,
+                new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+        container.addLocalRectInsetsSourceProvider(navigationBarInsetsRect2,
+                new int[]{ITYPE_LOCAL_NAVIGATION_BAR_2});
+        mDisplayContent.getInsetsStateController().onPostLayout();
+        rootTask.removeLocalInsetsSourceProvider(new int[]{ITYPE_LOCAL_NAVIGATION_BAR_1});
+        mDisplayContent.getInsetsStateController().onPostLayout();
+
+        activity0.forAllWindows(window -> {
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2));
+        }, true);
+        activity1.forAllWindows(window -> {
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+            assertEquals(navigationBarInsetsRect2,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+        }, true);
+        activity2.forAllWindows(window -> {
+            assertEquals(null,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_1));
+            assertEquals(navigationBarInsetsRect2,
+                    window.getInsetsState().peekSource(ITYPE_LOCAL_NAVIGATION_BAR_2).getFrame());
+        }, true);
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
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 459e3a5..ef600f0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -899,7 +899,7 @@
         assertTrue(mDisplayContent.shouldImeAttachedToApp());
         controller.getImeSourceProvider().scheduleShowImePostLayout(app);
         controller.getImeSourceProvider().getSource().setVisible(true);
-        controller.updateAboveInsetsState(imeWindow, false);
+        controller.updateAboveInsetsState(false);
 
         // Expect all app windows behind IME can receive IME insets visible.
         assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index fd9b995..42a5af7 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -36,7 +36,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -107,12 +106,6 @@
         return false;
     }
 
-    /**
-     * List of connected MIDI devices
-     */
-    private final HashMap<String, UsbMidiDevice>
-            mMidiDevices = new HashMap<String, UsbMidiDevice>();
-
     // UsbMidiDevice for USB peripheral mode (gadget) device
     private UsbMidiDevice mPeripheralMidiDevice = null;
 
@@ -256,45 +249,6 @@
             }
         }
 
-        // look for MIDI devices
-        boolean hasMidi = parser.hasMIDIInterface();
-        int midiNumInputs = parser.calculateNumLegacyMidiInputs();
-        int midiNumOutputs = parser.calculateNumLegacyMidiOutputs();
-        if (DEBUG) {
-            Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
-            Slog.d(TAG, "midiNumInputs: " + midiNumInputs + " midiNumOutputs:" + midiNumOutputs);
-        }
-        if (hasMidi && mHasMidiFeature) {
-            int device = 0;
-            Bundle properties = new Bundle();
-            String manufacturer = usbDevice.getManufacturerName();
-            String product = usbDevice.getProductName();
-            String version = usbDevice.getVersion();
-            String name;
-            if (manufacturer == null || manufacturer.isEmpty()) {
-                name = product;
-            } else if (product == null || product.isEmpty()) {
-                name = manufacturer;
-            } else {
-                name = manufacturer + " " + product;
-            }
-            properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
-            properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
-            properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
-            properties.putString(MidiDeviceInfo.PROPERTY_VERSION, version);
-            properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
-                    usbDevice.getSerialNumber());
-            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, cardRec.getCardNum());
-            properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, 0 /*deviceNum*/);
-            properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
-
-            UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
-                    cardRec.getCardNum(), 0 /*device*/, midiNumInputs, midiNumOutputs);
-            if (usbMidiDevice != null) {
-                mMidiDevices.put(deviceAddress, usbMidiDevice);
-            }
-        }
-
         logDevices("deviceAdded()");
 
         if (DEBUG) {
@@ -315,13 +269,6 @@
             selectDefaultDevice(); // if there any external devices left, select one of them
         }
 
-        // MIDI
-        UsbMidiDevice usbMidiDevice = mMidiDevices.remove(deviceAddress);
-        if (usbMidiDevice != null) {
-            Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
-            IoUtils.closeQuietly(usbMidiDevice);
-        }
-
         logDevices("usbDeviceRemoved()");
 
     }
@@ -377,12 +324,6 @@
             usbAlsaDevice.dump(dump, "alsa_devices", UsbAlsaManagerProto.ALSA_DEVICES);
         }
 
-        for (String deviceAddr : mMidiDevices.keySet()) {
-            // A UsbMidiDevice does not have a handle to the UsbDevice anymore
-            mMidiDevices.get(deviceAddr).dump(deviceAddr, dump, "midi_devices",
-                    UsbAlsaManagerProto.MIDI_DEVICES);
-        }
-
         dump.end(token);
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
similarity index 65%
rename from services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
rename to services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index 13d404c..0fa79df 100644
--- a/services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -20,6 +20,7 @@
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbDeviceConnection;
 import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceServer;
@@ -43,16 +44,20 @@
 import java.util.ArrayList;
 
 /**
- * A MIDI device that opens device connections to MIDI 2.0 endpoints.
+ * Opens device connections to MIDI 1.0 or MIDI 2.0 endpoints.
+ * This endpoint will not use ALSA and opens a UsbDeviceConnection directly.
  */
-public final class UsbUniversalMidiDevice implements Closeable {
-    private static final String TAG = "UsbUniversalMidiDevice";
+public final class UsbDirectMidiDevice implements Closeable {
+    private static final String TAG = "UsbDirectMidiDevice";
     private static final boolean DEBUG = false;
 
     private Context mContext;
     private UsbDevice mUsbDevice;
     private UsbDescriptorParser mParser;
     private ArrayList<UsbInterfaceDescriptor> mUsbInterfaces;
+    private final boolean mIsUniversalMidiDevice;
+    private final String mUniqueUsbDeviceIdentifier;
+    private final boolean mShouldCallSetInterface;
 
     // USB outputs are MIDI inputs
     private final InputReceiverProxy[] mMidiInputPortReceivers;
@@ -64,6 +69,11 @@
     // event schedulers for each input port of the physical device
     private MidiEventScheduler[] mEventSchedulers;
 
+    // Arbitrary number for timeout to not continue sending/receiving number from
+    // an inactive device. This number tries to balances the number of cycles and
+    // not being permanently stuck.
+    private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 100;
+
     private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
     private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
     private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
@@ -74,6 +84,8 @@
     private final Object mLock = new Object();
     private boolean mIsOpen;
 
+    private final UsbMidiPacketConverter mUsbMidiPacketConverter = new UsbMidiPacketConverter();
+
     private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
 
         @Override
@@ -140,13 +152,15 @@
     }
 
     /**
-     * Creates an UsbUniversalMidiDevice based on the input parameters. Read/Write streams
+     * Creates an UsbDirectMidiDevice based on the input parameters. Read/Write streams
      * will be created individually as some devices don't have the same number of
      * inputs and outputs.
      */
-    public static UsbUniversalMidiDevice create(Context context, UsbDevice usbDevice,
-            UsbDescriptorParser parser) {
-        UsbUniversalMidiDevice midiDevice = new UsbUniversalMidiDevice(usbDevice, parser);
+    public static UsbDirectMidiDevice create(Context context, UsbDevice usbDevice,
+            UsbDescriptorParser parser, boolean isUniversalMidiDevice,
+            String uniqueUsbDeviceIdentifier) {
+        UsbDirectMidiDevice midiDevice = new UsbDirectMidiDevice(usbDevice, parser,
+                isUniversalMidiDevice, uniqueUsbDeviceIdentifier);
         if (!midiDevice.register(context)) {
             IoUtils.closeQuietly(midiDevice);
             Log.e(TAG, "createDeviceServer failed");
@@ -155,11 +169,22 @@
         return midiDevice;
     }
 
-    private UsbUniversalMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser) {
+    private UsbDirectMidiDevice(UsbDevice usbDevice, UsbDescriptorParser parser,
+            boolean isUniversalMidiDevice, String uniqueUsbDeviceIdentifier) {
         mUsbDevice = usbDevice;
         mParser = parser;
+        mUniqueUsbDeviceIdentifier = uniqueUsbDeviceIdentifier;
+        mIsUniversalMidiDevice = isUniversalMidiDevice;
 
-        mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+        // Set interface should only be called when alternate interfaces exist.
+        // Otherwise, USB devices may not handle this gracefully.
+        mShouldCallSetInterface = (parser.calculateMidiInterfaceDescriptorsCount() > 1);
+
+        if (isUniversalMidiDevice) {
+            mUsbInterfaces = parser.findUniversalMidiInterfaceDescriptors();
+        } else {
+            mUsbInterfaces = parser.findLegacyMidiInterfaceDescriptors();
+        }
 
         int numInputs = 0;
         int numOutputs = 0;
@@ -182,8 +207,8 @@
         mNumInputs = numInputs;
         mNumOutputs = numOutputs;
 
-        Log.d(TAG, "Created UsbUniversalMidiDevice with " + numInputs + " inputs and "
-                + numOutputs + " outputs");
+        Log.d(TAG, "Created UsbDirectMidiDevice with " + numInputs + " inputs and "
+                + numOutputs + " outputs. isUniversalMidiDevice: " + isUniversalMidiDevice);
 
         // Create MIDI port receivers based on the number of output ports. The
         // output of USB is the input of MIDI.
@@ -218,23 +243,25 @@
             if (doesInterfaceContainInput
                     && doesInterfaceContainOutput) {
                 UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-
-                // The ALSA does not handle switching to the MIDI 2.0 interface correctly
-                // and stops exposing /dev/snd/midiC1D0 after calling connection.setInterface().
-                // Thus, simply use the control interface (interface zero).
+                UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+                if (!updateUsbInterface(usbInterface, connection)) {
+                    continue;
+                }
                 int defaultMidiProtocol = mMidiBlockParser.calculateMidiType(connection,
-                        0,
+                        interfaceDescriptor.getInterfaceNumber(),
                         interfaceDescriptor.getAlternateSetting());
+
                 connection.close();
                 return defaultMidiProtocol;
             }
         }
 
-        Log.d(TAG, "Cannot find interface with both input and output endpoints");
+        Log.w(TAG, "Cannot find interface with both input and output endpoints");
         return MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
     }
 
     private boolean openLocked() {
+        Log.d(TAG, "openLocked()");
         UsbManager manager = mContext.getSystemService(UsbManager.class);
 
         mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
@@ -258,8 +285,10 @@
             }
             if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) {
                 UsbDeviceConnection connection = manager.openDevice(mUsbDevice);
-                connection.setInterface(interfaceDescriptor.toAndroid(mParser));
-                connection.claimInterface(interfaceDescriptor.toAndroid(mParser), true);
+                UsbInterface usbInterface = interfaceDescriptor.toAndroid(mParser);
+                if (!updateUsbInterface(usbInterface, connection)) {
+                    continue;
+                }
                 mUsbDeviceConnections.add(connection);
                 mInputUsbEndpoints.add(inputEndpoints);
                 mOutputUsbEndpoints.add(outputEndpoints);
@@ -283,14 +312,17 @@
             for (int endpointIndex = 0;
                     endpointIndex < mInputUsbEndpoints.get(connectionIndex).size();
                     endpointIndex++) {
-                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
-                final UsbEndpoint epF = mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
-                final int portF = portNumber;
+                final UsbDeviceConnection connectionFinal =
+                        mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint endpointFinal =
+                        mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
+                final int portFinal = portNumber;
 
-                new Thread("UsbUniversalMidiDevice input thread " + portF) {
+                new Thread("UsbDirectMidiDevice input thread " + portFinal) {
                     @Override
                     public void run() {
-                        byte[] inputBuffer = new byte[epF.getMaxPacketSize()];
+                        byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()];
+                        Log.d(TAG, "input buffer size: " + inputBuffer.length);
                         try {
                             while (true) {
                                 // Record time of event immediately after waking.
@@ -298,20 +330,34 @@
                                 synchronized (mLock) {
                                     if (!mIsOpen) break;
 
-                                    int nRead = connectionF.bulkTransfer(epF, inputBuffer,
-                                            inputBuffer.length, 0);
-
-                                    // For USB, each 32 bit word of a UMP is
-                                    // sent with the least significant byte first.
-                                    swapEndiannessPerWord(inputBuffer, inputBuffer.length);
+                                    int nRead = connectionFinal.bulkTransfer(endpointFinal,
+                                            inputBuffer, inputBuffer.length,
+                                            BULK_TRANSFER_TIMEOUT_MILLISECONDS);
 
                                     if (nRead > 0) {
                                         if (DEBUG) {
-                                            logByteArray("Input ", inputBuffer, 0,
-                                                    nRead);
+                                            logByteArray("Input before conversion ", inputBuffer,
+                                                    0, nRead);
                                         }
-                                        outputReceivers[portF].send(inputBuffer, 0, nRead,
-                                                timestamp);
+                                        byte[] convertedArray;
+                                        if (mIsUniversalMidiDevice) {
+                                            // For USB, each 32 bit word of a UMP is
+                                            // sent with the least significant byte first.
+                                            convertedArray = swapEndiannessPerWord(inputBuffer,
+                                                    nRead);
+                                        } else {
+                                            convertedArray =
+                                                    mUsbMidiPacketConverter.usbMidiToRawMidi(
+                                                             inputBuffer, nRead);
+                                        }
+
+                                        if (DEBUG) {
+                                            logByteArray("Input after conversion ", convertedArray,
+                                                    0, convertedArray.length);
+                                        }
+
+                                        outputReceivers[portFinal].send(convertedArray, 0,
+                                                convertedArray.length, timestamp);
                                     }
                                 }
                             }
@@ -333,19 +379,20 @@
             for (int endpointIndex = 0;
                     endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size();
                     endpointIndex++) {
-                final UsbDeviceConnection connectionF = mUsbDeviceConnections.get(connectionIndex);
-                final UsbEndpoint epF =
+                final UsbDeviceConnection connectionFinal =
+                        mUsbDeviceConnections.get(connectionIndex);
+                final UsbEndpoint endpointFinal =
                         mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex);
-                final int portF = portNumber;
-                final MidiEventScheduler eventSchedulerF = mEventSchedulers[portF];
+                final int portFinal = portNumber;
+                final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal];
 
-                new Thread("UsbUniversalMidiDevice output thread " + portF) {
+                new Thread("UsbDirectMidiDevice output thread " + portFinal) {
                     @Override
                     public void run() {
                         while (true) {
                             MidiEvent event;
                             try {
-                                event = (MidiEvent) eventSchedulerF.waitNextEvent();
+                                event = (MidiEvent) eventSchedulerFinal.waitNextEvent();
                             } catch (InterruptedException e) {
                                 // try again
                                 continue;
@@ -354,16 +401,32 @@
                                 break;
                             }
 
-                            // For USB, each 32 bit word of a UMP is
-                            // sent with the least significant byte first.
-                            swapEndiannessPerWord(event.data, event.count);
-
                             if (DEBUG) {
-                                logByteArray("Output ", event.data, 0,
+                                logByteArray("Output before conversion ", event.data, 0,
                                         event.count);
                             }
-                            connectionF.bulkTransfer(epF, event.data, event.count, 0);
-                            eventSchedulerF.addEventToPool(event);
+
+                            byte[] convertedArray;
+                            if (mIsUniversalMidiDevice) {
+                                // For USB, each 32 bit word of a UMP is
+                                // sent with the least significant byte first.
+                                convertedArray = swapEndiannessPerWord(event.data,
+                                        event.count);
+                            } else {
+                                convertedArray =
+                                        mUsbMidiPacketConverter.rawMidiToUsbMidi(
+                                                 event.data, event.count);
+                            }
+
+                            if (DEBUG) {
+                                logByteArray("Output after conversion ", convertedArray, 0,
+                                        convertedArray.length);
+                            }
+
+                            connectionFinal.bulkTransfer(endpointFinal, convertedArray,
+                                    convertedArray.length,
+                                    BULK_TRANSFER_TIMEOUT_MILLISECONDS);
+                            eventSchedulerFinal.addEventToPool(event);
                         }
                         Log.d(TAG, "output thread exit");
                     }
@@ -381,11 +444,15 @@
         mContext = context;
         MidiManager midiManager = context.getSystemService(MidiManager.class);
         if (midiManager == null) {
-            Log.e(TAG, "No MidiManager in UsbUniversalMidiDevice.create()");
+            Log.e(TAG, "No MidiManager in UsbDirectMidiDevice.create()");
             return false;
         }
 
-        mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+        if (mIsUniversalMidiDevice) {
+            mDefaultMidiProtocol = calculateDefaultMidiProtocol();
+        } else {
+            mDefaultMidiProtocol = MidiDeviceInfo.PROTOCOL_UNKNOWN;
+        }
 
         Bundle properties = new Bundle();
         String manufacturer = mUsbDevice.getManufacturerName();
@@ -397,8 +464,15 @@
         } else if (product == null || product.isEmpty()) {
             name = manufacturer;
         } else {
-            name = manufacturer + " " + product + " MIDI 2.0";
+            name = manufacturer + " " + product;
         }
+        name += "#" + mUniqueUsbDeviceIdentifier;
+        if (mIsUniversalMidiDevice) {
+            name += " MIDI 2.0";
+        } else {
+            name += " MIDI 1.0";
+        }
+        Log.e(TAG, name);
         properties.putString(MidiDeviceInfo.PROPERTY_NAME, name);
         properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, manufacturer);
         properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, product);
@@ -430,10 +504,13 @@
     }
 
     private void closeLocked() {
+        Log.d(TAG, "closeLocked()");
         for (int i = 0; i < mEventSchedulers.length; i++) {
             mMidiInputPortReceivers[i].setReceiver(null);
             mEventSchedulers[i].close();
         }
+        mEventSchedulers = null;
+
         for (UsbDeviceConnection connection : mUsbDeviceConnections) {
             connection.close();
         }
@@ -444,15 +521,19 @@
         mIsOpen = false;
     }
 
-    private void swapEndiannessPerWord(byte[] array, int size) {
-        for (int i = 0; i + 3 < size; i += 4) {
-            byte tmp = array[i];
-            array[i] = array[i + 3];
-            array[i + 3] = tmp;
-            tmp = array[i + 1];
-            array[i + 1] = array[i + 2];
-            array[i + 2] = tmp;
+    private byte[] swapEndiannessPerWord(byte[] inputArray, int size) {
+        int numberOfExcessBytes = size & 3;
+        if (numberOfExcessBytes != 0) {
+            Log.e(TAG, "size not multiple of 4: " + size);
         }
+        byte[] outputArray = new byte[size - numberOfExcessBytes];
+        for (int i = 0; i + 3 < size; i += 4) {
+            outputArray[i] = inputArray[i + 3];
+            outputArray[i + 1] = inputArray[i + 2];
+            outputArray[i + 2] = inputArray[i + 1];
+            outputArray[i + 3] = inputArray[i];
+        }
+        return outputArray;
     }
 
     private static void logByteArray(String prefix, byte[] value, int offset, int count) {
@@ -465,4 +546,24 @@
         }
         Log.d(TAG, builder.toString());
     }
+
+    private boolean updateUsbInterface(UsbInterface usbInterface,
+            UsbDeviceConnection connection) {
+        if (usbInterface == null) {
+            Log.e(TAG, "Usb Interface is null");
+            return false;
+        }
+        if (!connection.claimInterface(usbInterface, true)) {
+            Log.e(TAG, "Can't claim interface");
+            return false;
+        }
+        if (mShouldCallSetInterface) {
+            if (!connection.setInterface(usbInterface)) {
+                Log.w(TAG, "Can't set interface");
+            }
+        } else {
+            Log.w(TAG, "no alternate interface");
+        }
+        return true;
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 94cc826..a70b0332 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -50,9 +50,12 @@
 import libcore.io.IoUtils;
 
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.Random;
 
 /**
  * UsbHostManager manages USB state in host mode.
@@ -94,10 +97,12 @@
     private final ArrayMap<String, ConnectionRecord> mConnected = new ArrayMap<>();
 
     /**
-     * List of connected MIDI devices
+     * List of connected MIDI devices. Key on deviceAddress.
      */
-    private final HashMap<String, UsbUniversalMidiDevice>
-            mMidiDevices = new HashMap<String, UsbUniversalMidiDevice>();
+    private final HashMap<String, ArrayList<UsbDirectMidiDevice>>
+            mMidiDevices = new HashMap<String, ArrayList<UsbDirectMidiDevice>>();
+    private final HashSet<String> mMidiUniqueCodes = new HashSet<String>();
+    private final Random mRandom = new Random();
     private final boolean mHasMidiFeature;
 
     /*
@@ -423,15 +428,35 @@
                 mUsbAlsaManager.usbDeviceAdded(deviceAddress, newDevice, parser);
 
                 if (mHasMidiFeature) {
+                    // Use a 3 digit code to associate MIDI devices with one another.
+                    // Each MIDI device already has mId for uniqueness. mId is generated
+                    // sequentially. For clarity, this code is not generated sequentially.
+                    String uniqueUsbDeviceIdentifier = generateNewUsbDeviceIdentifier();
+
+                    ArrayList<UsbDirectMidiDevice> midiDevices =
+                            new ArrayList<UsbDirectMidiDevice>();
                     if (parser.containsUniversalMidiDeviceEndpoint()) {
-                        UsbUniversalMidiDevice midiDevice = UsbUniversalMidiDevice.create(mContext,
-                                newDevice, parser);
+                        UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+                                newDevice, parser, true, uniqueUsbDeviceIdentifier);
                         if (midiDevice != null) {
-                            mMidiDevices.put(deviceAddress, midiDevice);
+                            midiDevices.add(midiDevice);
                         } else {
                             Slog.e(TAG, "Universal Midi Device is null.");
                         }
                     }
+                    if (parser.containsLegacyMidiDeviceEndpoint()) {
+                        UsbDirectMidiDevice midiDevice = UsbDirectMidiDevice.create(mContext,
+                                newDevice, parser, false, uniqueUsbDeviceIdentifier);
+                        if (midiDevice != null) {
+                            midiDevices.add(midiDevice);
+                        } else {
+                            Slog.e(TAG, "Legacy Midi Device is null.");
+                        }
+                    }
+
+                    if (!midiDevices.isEmpty()) {
+                        mMidiDevices.put(deviceAddress, midiDevices);
+                    }
                 }
 
                 // Tracking
@@ -469,10 +494,13 @@
                 mPermissionManager.usbDeviceRemoved(device);
 
                 // MIDI
-                UsbUniversalMidiDevice midiDevice = mMidiDevices.remove(deviceAddress);
-                if (midiDevice != null) {
-                    Slog.i(TAG, "USB Universal MIDI Device Removed: " + deviceAddress);
-                    IoUtils.closeQuietly(midiDevice);
+                ArrayList<UsbDirectMidiDevice> midiDevices =
+                        mMidiDevices.remove(deviceAddress);
+                for (UsbDirectMidiDevice midiDevice : midiDevices) {
+                    if (midiDevice != null) {
+                        Slog.i(TAG, "USB MIDI Device Removed: " + deviceAddress);
+                        IoUtils.closeQuietly(midiDevice);
+                    }
                 }
 
                 getCurrentUserSettings().usbDeviceRemoved(device);
@@ -606,6 +634,19 @@
         return true;
     }
 
+    // Generate a 3 digit code.
+    private String generateNewUsbDeviceIdentifier() {
+        String code;
+        do {
+            code = "";
+            for (int i = 0; i < 3; i++) {
+                code += mRandom.nextInt(10);
+            }
+        } while (mMidiUniqueCodes.contains(code));
+        mMidiUniqueCodes.add(code);
+        return code;
+    }
+
     private native void monitorUsbHostBus();
     private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
 }
diff --git a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
new file mode 100644
index 0000000..7c93c76
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2022 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.usb;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Converts between MIDI packets and USB MIDI 1.0 packets.
+ */
+public class UsbMidiPacketConverter {
+
+    // Refer to Table 4-1 in USB MIDI 1.0 spec.
+    private static final int[] PAYLOAD_SIZE = new int[]{
+            /* 0x00 */ -1, // Miscellaneous function codes. Reserved for future extensions.
+            /* 0x01 */ -1, // Cable events. Reserved for future expansion.
+            /* 0x02 */  2, // Two-byte System Common messages like MTC, SongSelect, etc
+            /* 0x03 */  3, // Three-byte System Common messages like SPP, etc.
+            /* 0x04 */  3, // SysEx starts or continues
+            /* 0x05 */  1, // Single-byte System Common Message or single-byte SysEx ends.
+            /* 0x06 */  2, // SysEx ends with following two bytes.
+            /* 0x07 */  3, // SysEx ends with following three bytes.
+            /* 0x08 */  3, // Note-off
+            /* 0x09 */  3, // Note-on
+            /* 0x0a */  3, // Poly-KeyPress
+            /* 0x0b */  3, // Control Change
+            /* 0x0c */  2, // Program Change
+            /* 0x0d */  2, // Channel Pressure
+            /* 0x0e */  3, // PitchBend Change
+            /* 0x0f */  1  // Single Byte
+    };
+
+    // Each System MIDI message is a certain size. These can be mapped to a
+    // Code Index number defined in Table 4-1 of USB MIDI 1.0.
+    private static final int[] CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE = new int[]{
+            /* 0x00 */ -1, // Start of Exclusive. Special case.
+            /* 0x01 */  2, // MIDI Time Code. Two byte message
+            /* 0x02 */  3, // Song Point Pointer. Three byte message
+            /* 0x03 */  2, // Song Select. Two byte message
+            /* 0x04 */ -1, // Undefined MIDI System Common
+            /* 0x05 */ -1, // Undefined MIDI System Common
+            /* 0x06 */  5, // Tune Request. One byte message
+            /* 0x07 */ -1, // End of Exclusive. Special case.
+            /* 0x08 */  5, // Timing clock. One byte message
+            /* 0x09 */ -1, // Undefined MIDI System Real-time
+            /* 0x0a */  5, // Start. One byte message
+            /* 0x0b */  5, // Continue. One byte message
+            /* 0x0c */  5, // Stop. One byte message
+            /* 0x0d */ -1, // Undefined MIDI System Real-time
+            /* 0x0e */  5, // Active Sensing. One byte message
+            /* 0x0f */  5  // System Reset. One byte message
+    };
+
+    // These code index numbers also come from Table 4-1 in USB MIDI 1.0 spec.
+    private static final byte CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES = 0x4;
+    private static final byte CODE_INDEX_NUMBER_SINGLE_BYTE = 0xF;
+    private static final byte CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE = (byte) 0x5;
+
+    // System messages are defined in MIDI.
+    private static final byte FIRST_SYSTEM_MESSAGE_VALUE = (byte) 0xF0;
+    private static final byte SYSEX_START_EXCLUSIVE = (byte) 0xF0;
+    private static final byte SYSEX_END_EXCLUSIVE = (byte) 0xF7;
+
+    private UsbMidiEncoder mUsbMidiEncoder = new UsbMidiEncoder();
+    private UsbMidiDecoder mUsbMidiDecoder = new UsbMidiDecoder();
+
+    /**
+     * Converts a USB MIDI array into a raw MIDI array.
+     *
+     * @param usbMidiBytes the USB MIDI bytes to convert
+     * @param size the size of usbMidiBytes
+     * @return byte array of raw MIDI packets
+     */
+    public byte[] usbMidiToRawMidi(byte[] usbMidiBytes, int size) {
+        return mUsbMidiDecoder.decode(usbMidiBytes, size);
+    }
+
+    /**
+     * Converts a raw MIDI array into a USB MIDI array.
+     *
+     * @param midiBytes the raw MIDI bytes to convert
+     * @param size the size of usbMidiBytes
+     * @return byte array of USB MIDI packets
+     */
+    public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size) {
+        return mUsbMidiEncoder.encode(midiBytes, size);
+    }
+
+    private class UsbMidiDecoder {
+        // Decodes the data from USB MIDI to raw MIDI.
+        // Each valid 4 byte input maps to a 1-3 byte output.
+        // Reference the USB MIDI 1.0 spec for more info.
+        public byte[] decode(byte[] usbMidiBytes, int size) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            for (int i = 0; i + 3 < size; i += 4) {
+                int codeIndex = usbMidiBytes[i] & 0x0f;
+                int numPayloadBytes = PAYLOAD_SIZE[codeIndex];
+                if (numPayloadBytes < 0) {
+                    continue;
+                }
+                outputStream.write(usbMidiBytes, i + 1, numPayloadBytes);
+            }
+            return outputStream.toByteArray();
+        }
+    }
+
+    private class UsbMidiEncoder {
+        // In order to facilitate large scale transfers, SysEx can be sent in multiple packets.
+        // If encode() is called without an SysEx end, we must continue SysEx for the next packet.
+        // All other packets should be 3 bytes or less and must be not be broken between packets.
+        private byte[] mStoredSystemExclusiveBytes = new byte[3];
+        private int mNumStoredSystemExclusiveBytes = 0;
+        private boolean mHasSystemExclusiveStarted = false;
+
+        private byte[] mEmptyBytes = new byte[3]; // Used to fill out extra data
+
+        // Encodes the data from raw MIDI to USB MIDI.
+        // Each valid 1-3 byte input maps to a 4 byte output.
+        // Reference the USB MIDI 1.0 spec for more info.
+        // MidiFramer is not needed here as this code handles partial packets.
+        // Long SysEx messages split between packets will encode and return a
+        // byte stream even if the SysEx end has not been sent.
+        // If there are less than 3 remaining data bytes in a SysEx message left,
+        // these bytes will be combined with the next set of packets.
+        public byte[] encode(byte[] midiBytes, int size) {
+            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+            int curLocation = 0;
+            while (curLocation < size) {
+                if (midiBytes[curLocation] >= 0) { // Data byte
+                    if (mHasSystemExclusiveStarted) {
+                        mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+                                midiBytes[curLocation];
+                        mNumStoredSystemExclusiveBytes++;
+                        if (mNumStoredSystemExclusiveBytes == 3) {
+                            outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES);
+                            outputStream.write(mStoredSystemExclusiveBytes, 0, 3);
+                            mNumStoredSystemExclusiveBytes = 0;
+                        }
+                    } else {
+                        writeSingleByte(outputStream, midiBytes[curLocation]);
+                    }
+                    curLocation++;
+                    continue;
+                } else if (midiBytes[curLocation] != SYSEX_END_EXCLUSIVE) {
+                    // SysEx operation was interrupted. Pass the data directly down.
+                    if (mHasSystemExclusiveStarted) {
+                        int index = 0;
+                        while (index < mNumStoredSystemExclusiveBytes) {
+                            writeSingleByte(outputStream, mStoredSystemExclusiveBytes[index]);
+                            index++;
+                        }
+                        mNumStoredSystemExclusiveBytes = 0;
+                        mHasSystemExclusiveStarted = false;
+                    }
+                }
+
+                if (midiBytes[curLocation] < FIRST_SYSTEM_MESSAGE_VALUE) { // Channel message
+                    byte codeIndexNumber = (byte) ((midiBytes[curLocation] >> 4) & 0x0f);
+                    int channelMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+                    if (curLocation + channelMessageSize <= size) {
+                        outputStream.write(codeIndexNumber);
+                        outputStream.write(midiBytes, curLocation, channelMessageSize);
+                        // Fill in the rest of the bytes with 0.
+                        outputStream.write(mEmptyBytes, 0, 3 - channelMessageSize);
+                        curLocation += channelMessageSize;
+                    } else { // The packet is missing data. Use single byte messages.
+                        while (curLocation < size) {
+                            writeSingleByte(outputStream, midiBytes[curLocation]);
+                            curLocation++;
+                        }
+                    }
+                } else if (midiBytes[curLocation] == SYSEX_START_EXCLUSIVE) {
+                    mHasSystemExclusiveStarted = true;
+                    mStoredSystemExclusiveBytes[0] = midiBytes[curLocation];
+                    mNumStoredSystemExclusiveBytes = 1;
+                    curLocation++;
+                } else if (midiBytes[curLocation] == SYSEX_END_EXCLUSIVE) {
+                    // 1 byte is 0x05, 2 bytes is 0x06, and 3 bytes is 0x07
+                    outputStream.write(CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE
+                            + mNumStoredSystemExclusiveBytes);
+                    mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] =
+                            midiBytes[curLocation];
+                    mNumStoredSystemExclusiveBytes++;
+                    outputStream.write(mStoredSystemExclusiveBytes, 0,
+                             mNumStoredSystemExclusiveBytes);
+                    // Fill in the rest of the bytes with 0.
+                    outputStream.write(mEmptyBytes, 0, 3 - mNumStoredSystemExclusiveBytes);
+                    mHasSystemExclusiveStarted = false;
+                    mNumStoredSystemExclusiveBytes = 0;
+                    curLocation++;
+                } else {
+                    int systemType = midiBytes[curLocation] & 0x0f;
+                    int codeIndexNumber = CODE_INDEX_NUMBER_FROM_SYSTEM_TYPE[systemType];
+                    if (codeIndexNumber < 0) { // Unknown type. Use single byte messages.
+                        writeSingleByte(outputStream, midiBytes[curLocation]);
+                        curLocation++;
+                    } else {
+                        int systemMessageSize = PAYLOAD_SIZE[codeIndexNumber];
+                        if (curLocation + systemMessageSize <= size) {
+                            outputStream.write(codeIndexNumber);
+                            outputStream.write(midiBytes, curLocation, systemMessageSize);
+                            // Fill in the rest of the bytes with 0.
+                            outputStream.write(mEmptyBytes, 0, 3 - systemMessageSize);
+                            curLocation += systemMessageSize;
+                        } else { // The packet is missing data. Use single byte messages.
+                            while (curLocation < size) {
+                                writeSingleByte(outputStream, midiBytes[curLocation]);
+                                curLocation++;
+                            }
+                        }
+                    }
+                }
+            }
+            return outputStream.toByteArray();
+        }
+
+        private void writeSingleByte(ByteArrayOutputStream outputStream, byte byteToWrite) {
+            outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE);
+            outputStream.write(byteToWrite);
+            outputStream.write(0);
+            outputStream.write(0);
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 6e68a91..cd6ea68 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -198,14 +198,20 @@
                 if (mCurInterfaceDescriptor != null) {
                     switch (mCurInterfaceDescriptor.getUsbClass()) {
                         case UsbDescriptor.CLASSID_AUDIO:
-                            descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+                            descriptor =
+                                    UsbACInterface.allocDescriptor(this, stream, length, type);
+                            if (descriptor instanceof UsbMSMidiHeader) {
+                                mCurInterfaceDescriptor.setMidiHeaderInterfaceDescriptor(
+                                        descriptor);
+                            }
                             break;
 
                         case UsbDescriptor.CLASSID_VIDEO:
                             if (DEBUG) {
                                 Log.d(TAG, "  UsbDescriptor.CLASSID_VIDEO");
                             }
-                            descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
+                            descriptor =
+                                    UsbVCInterface.allocDescriptor(this, stream, length, type);
                             break;
 
                         case UsbDescriptor.CLASSID_AUDIOVIDEO:
@@ -218,7 +224,6 @@
                             Log.w(TAG, "  Unparsed Class-specific");
                             break;
                     }
-                    mCurInterfaceDescriptor.setClassSpecificInterfaceDescriptor(descriptor);
                 }
                 break;
 
@@ -674,6 +679,23 @@
     public boolean containsUniversalMidiDeviceEndpoint() {
         ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
                 findUniversalMidiInterfaceDescriptors();
+        return doesInterfaceContainEndpoint(interfaceDescriptors);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean containsLegacyMidiDeviceEndpoint() {
+        ArrayList<UsbInterfaceDescriptor> interfaceDescriptors =
+                findLegacyMidiInterfaceDescriptors();
+        return doesInterfaceContainEndpoint(interfaceDescriptors);
+    }
+
+    /**
+     * @hide
+     */
+    public boolean doesInterfaceContainEndpoint(
+            ArrayList<UsbInterfaceDescriptor> interfaceDescriptors) {
         int outputCount = 0;
         int inputCount = 0;
         for (int interfaceIndex = 0; interfaceIndex < interfaceDescriptors.size();
@@ -698,10 +720,24 @@
      * @hide
      */
     public ArrayList<UsbInterfaceDescriptor> findUniversalMidiInterfaceDescriptors() {
+        return findMidiInterfaceDescriptors(MS_MIDI_2_0);
+    }
+
+    /**
+     * @hide
+     */
+    public ArrayList<UsbInterfaceDescriptor> findLegacyMidiInterfaceDescriptors() {
+        return findMidiInterfaceDescriptors(MS_MIDI_1_0);
+    }
+
+    /**
+     * @hide
+     */
+    private ArrayList<UsbInterfaceDescriptor> findMidiInterfaceDescriptors(int type) {
         int count = 0;
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
-        ArrayList<UsbInterfaceDescriptor> universalMidiInterfaces =
+        ArrayList<UsbInterfaceDescriptor> midiInterfaces =
                 new ArrayList<UsbInterfaceDescriptor>();
 
         for (UsbDescriptor descriptor : descriptors) {
@@ -709,14 +745,14 @@
             if (descriptor instanceof UsbInterfaceDescriptor) {
                 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
                 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
-                    UsbDescriptor classSpecificDescriptor =
-                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
-                    if (classSpecificDescriptor != null) {
-                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                    UsbDescriptor midiHeaderDescriptor =
+                            interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+                    if (midiHeaderDescriptor != null) {
+                        if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
                             UsbMSMidiHeader midiHeader =
-                                    (UsbMSMidiHeader) classSpecificDescriptor;
-                            if (midiHeader.getMidiStreamingClass() == MS_MIDI_2_0) {
-                                universalMidiInterfaces.add(interfaceDescriptor);
+                                    (UsbMSMidiHeader) midiHeaderDescriptor;
+                            if (midiHeader.getMidiStreamingClass() == type) {
+                                midiInterfaces.add(interfaceDescriptor);
                             }
                         }
                     }
@@ -726,10 +762,13 @@
                         + " t:0x" + Integer.toHexString(descriptor.getType()));
             }
         }
-        return universalMidiInterfaces;
+        return midiInterfaces;
     }
 
-    private int calculateNumLegacyMidiPorts(boolean isOutput) {
+    /**
+     * @hide
+     */
+    public int calculateMidiInterfaceDescriptorsCount() {
         int count = 0;
         ArrayList<UsbDescriptor> descriptors =
                 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
@@ -738,22 +777,12 @@
             if (descriptor instanceof UsbInterfaceDescriptor) {
                 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor;
                 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
-                    UsbDescriptor classSpecificDescriptor =
-                            interfaceDescriptor.getClassSpecificInterfaceDescriptor();
-                    if (classSpecificDescriptor != null) {
-                        if (classSpecificDescriptor instanceof UsbMSMidiHeader) {
+                    UsbDescriptor midiHeaderDescriptor =
+                            interfaceDescriptor.getMidiHeaderInterfaceDescriptor();
+                    if (midiHeaderDescriptor != null) {
+                        if (midiHeaderDescriptor instanceof UsbMSMidiHeader) {
                             UsbMSMidiHeader midiHeader =
-                                    (UsbMSMidiHeader) classSpecificDescriptor;
-                            if (midiHeader.getMidiStreamingClass() != MS_MIDI_1_0) {
-                                continue;
-                            }
-                        }
-                    }
-                    for (int i = 0; i < interfaceDescriptor.getNumEndpoints(); i++) {
-                        UsbEndpointDescriptor endpoint =
-                                interfaceDescriptor.getEndpointDescriptor(i);
-                        // 0 is output, 1 << 7 is input.
-                        if ((endpoint.getDirection() == 0) == isOutput) {
+                                    (UsbMSMidiHeader) midiHeaderDescriptor;
                             count++;
                         }
                     }
@@ -769,20 +798,6 @@
     /**
      * @hide
      */
-    public int calculateNumLegacyMidiInputs() {
-        return calculateNumLegacyMidiPorts(false /*isOutput*/);
-    }
-
-    /**
-     * @hide
-     */
-    public int calculateNumLegacyMidiOutputs() {
-        return calculateNumLegacyMidiPorts(true /*isOutput*/);
-    }
-
-    /**
-     * @hide
-     */
     public float getInputHeadsetProbability() {
         if (hasMIDIInterface()) {
             return 0.0f;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index ca4613b..9ddcb10 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -42,7 +42,8 @@
     private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
             new ArrayList<UsbEndpointDescriptor>();
 
-    private UsbDescriptor mClassSpecificInterfaceDescriptor;
+    // Used for MIDI only.
+    private UsbDescriptor mMidiHeaderInterfaceDescriptor;
 
     UsbInterfaceDescriptor(int length, byte type) {
         super(length, type);
@@ -107,12 +108,12 @@
         mEndpointDescriptors.add(endpoint);
     }
 
-    public void setClassSpecificInterfaceDescriptor(UsbDescriptor descriptor) {
-        mClassSpecificInterfaceDescriptor = descriptor;
+    public void setMidiHeaderInterfaceDescriptor(UsbDescriptor descriptor) {
+        mMidiHeaderInterfaceDescriptor = descriptor;
     }
 
-    public UsbDescriptor getClassSpecificInterfaceDescriptor() {
-        return mClassSpecificInterfaceDescriptor;
+    public UsbDescriptor getMidiHeaderInterfaceDescriptor() {
+        return mMidiHeaderInterfaceDescriptor;
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f57c32c..ad04952 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7664,7 +7664,7 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * TODO: remove this one. use {@link #rebootModem()} for reset type 1 and
-     * {@link #resetRadioConfig()} for reset type 3
+     * {@link #resetRadioConfig()} for reset type 3 (b/116476729)
      *
      * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
      * @return true on success; false on any failure.
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 3415868..783c0d1 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -497,6 +497,8 @@
 
         try {
             return imsRcsController.isCapable(mSubId, capability, radioTech);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
             Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
             throw new ImsException("Remote IMS Service is not available",
@@ -534,6 +536,8 @@
 
         try {
             return imsRcsController.isAvailable(mSubId, capability, radioTech);
+        } catch (ServiceSpecificException e) {
+            throw new ImsException(e.getMessage(), e.errorCode);
         } catch (RemoteException e) {
             Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
             throw new ImsException("Remote IMS Service is not available",
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index c59a41e..9a9e42b 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,8 +37,6 @@
         "vts",
     ],
     data: [
-        ":NotoSerif-Regular.ttf",
-        ":NotoSerif-Bold.ttf",
         ":UpdatableSystemFontTestCertDer",
         ":UpdatableSystemFontTest_NotoColorEmoji.ttf",
         ":UpdatableSystemFontTest_NotoColorEmoji.sig",
@@ -48,7 +46,9 @@
         ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
         ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
         ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
+        ":UpdatableSystemFontTest_NotoSerif-Regular.ttf",
         ":UpdatableSystemFontTest_NotoSerif-Regular.sig",
+        ":UpdatableSystemFontTest_NotoSerif-Bold.ttf",
         ":UpdatableSystemFontTest_NotoSerif-Bold.sig",
     ],
     sdk_version: "test_current",
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 6effa7b..9e2a4b6 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -28,11 +28,7 @@
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
         <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
-        <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
-        <option name="push" value="NotoSerif-Regular.ttf->/data/local/tmp/NotoSerif-Regular.ttf" />
-        <option name="push" value="NotoSerif-Bold.ttf->/data/local/tmp/NotoSerif-Bold.ttf" />
-        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
-        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig" />
@@ -40,6 +36,10 @@
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf" />
         <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+        <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 87fda0d..cbe13d9 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -84,7 +84,7 @@
 
     private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
     private static final String NOTO_COLOR_EMOJI_TTF =
-            "/data/local/tmp/NotoColorEmoji.ttf";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf";
     private static final String NOTO_COLOR_EMOJI_SIG =
             "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig";
     // A font with revision == 0.
@@ -105,13 +105,13 @@
 
     private static final String NOTO_SERIF_REGULAR_POSTSCRIPT_NAME = "NotoSerif";
     private static final String NOTO_SERIF_REGULAR_TTF =
-            "/data/local/tmp/NotoSerif-Regular.ttf";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf";
     private static final String NOTO_SERIF_REGULAR_SIG =
             "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig";
 
     private static final String NOTO_SERIF_BOLD_POSTSCRIPT_NAME = "NotoSerif-Bold";
     private static final String NOTO_SERIF_BOLD_TTF =
-            "/data/local/tmp/NotoSerif-Bold.ttf";
+            "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf";
     private static final String NOTO_SERIF_BOLD_SIG =
             "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig";
 
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 64b698d..0bdb3a8 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -21,11 +21,19 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-// An existing module name is reused to avoid merge conflicts.
-// TODO: fix the font file name.
 filegroup {
     name: "UpdatableSystemFontTest_NotoColorEmoji.ttf",
-    srcs: ["NotoColorEmoji.ttf"],
+    srcs: ["UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+}
+
+filegroup {
+    name: "UpdatableSystemFontTest_NotoSerif-Regular.ttf",
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
+}
+
+filegroup {
+    name: "UpdatableSystemFontTest_NotoSerif-Bold.ttf",
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
 }
 
 filegroup {
@@ -43,10 +51,6 @@
     srcs: ["UpdatableSystemFontTestCert.der"],
 }
 
-genrule_defaults {
-    name: "updatable_system_font_increment_font_revision_default",
-}
-
 genrule {
     name: "UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
     srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
@@ -124,13 +128,13 @@
 genrule {
     name: "UpdatableSystemFontTest_NotoSerif-Regular.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":NotoSerif-Regular.ttf"],
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
     out: ["UpdatableSystemFontTest_NotoSerif-Regular.sig"],
 }
 
 genrule {
     name: "UpdatableSystemFontTest_NotoSerif-Bold.sig",
     defaults: ["updatable_system_font_sig_gen_default"],
-    srcs: [":NotoSerif-Bold.ttf"],
+    srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
     out: ["UpdatableSystemFontTest_NotoSerif-Bold.sig"],
 }
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
similarity index 100%
rename from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf
rename to tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
similarity index 100%
rename from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx
rename to tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
new file mode 100644
index 0000000..66c1bd2
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
new file mode 100644
index 0000000..8c4215e
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <!-- Currently NotoSerif-Bold.ttf's fontRevision is 1.xx.
+         100.0 will be sufficiently larger than that. -->
+    <fontRevision value="100.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Feb 16 12:00:00 2022"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="a" width="3000" lsb="93"/>  <!-- 3em -->
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <!-- length will be calculated by the compiler. -->
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+      <!-- The font must support at least one of the characters used
+           in OtfFontFileParser to validate the font. -->
+      <map code="0x61" name="a" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="0" y="300" on="1" />
+        <pt x="300" y="300" on="1" />
+        <pt x="300" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2022 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Bold
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      <!-- Android identifies the target font to be updated by PostScript name.
+           To test updating NotoSerif-Bold.ttf, the PostScript needs to be
+           the same as NotoSerif-Bold.ttf here. -->
+      NotoSerif-Bold
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
new file mode 100644
index 0000000..707ae28
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
new file mode 100644
index 0000000..754eae3
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="a"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <!-- Currently NotoSerif-Regular.ttf's fontRevision is 1.xx.
+         100.0 will be sufficiently larger than that. -->
+    <fontRevision value="100.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Wed Feb 16 12:00:00 2022"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="a" width="3000" lsb="93"/>  <!-- 3em -->
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <!-- length will be calculated by the compiler. -->
+    <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+      <!-- The font must support at least one of the characters used
+           in OtfFontFileParser to validate the font. -->
+      <map code="0x61" name="a" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+      <contour>
+        <pt x="0" y="0" on="1" />
+        <pt x="0" y="300" on="1" />
+        <pt x="300" y="300" on="1" />
+        <pt x="300" y="0" on="1" />
+      </contour>
+      <instructions />
+    </TTGlyph>
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2022 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      <!-- Android identifies the target font to be updated by PostScript name.
+           To test updating NotoSerif-Regular.ttf, the PostScript needs to be
+           the same as NotoSerif-Regular.ttf here. -->
+      NotoSerif
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index bd0a4bc..52c5d48 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -36,6 +36,7 @@
 
 cc_defaults {
     name: "aapt2_defaults",
+    cpp_std: "gnu++2b",
     cflags: [
         "-Wall",
         "-Werror",
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 7103944..f47d66e 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -80,7 +80,7 @@
           printer_->Print(parent_name.package);
           printer_->Print(":");
         }
-        printer_->Print(to_string(parent_name.type));
+        printer_->Print(parent_name.type.to_string());
         printer_->Print("/");
         printer_->Print(parent_name.entry);
         if (parent_ref.id) {
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 6364ccd..0bb330e 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -134,6 +134,24 @@
     {"xml", ResourceType::kXml},
 };
 
+ResourceNamedTypeRef ResourceNamedTypeWithDefaultName(ResourceType t) {
+  return {to_string(t), t};
+}
+
+std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s) {
+  auto colon = std::find(s.begin(), s.end(), ':');
+  const ResourceType* parsedType;
+  if (colon != s.end() && colon != std::prev(s.end())) {
+    parsedType = ParseResourceType(s.substr(s.begin(), colon));
+  } else {
+    parsedType = ParseResourceType(s);
+  }
+  if (parsedType == nullptr) {
+    return std::nullopt;
+  }
+  return ResourceNamedTypeRef(s, *parsedType);
+}
+
 const ResourceType* ParseResourceType(const StringPiece& str) {
   auto iter = sResourceTypeMap.find(str);
   if (iter == std::end(sResourceTypeMap)) {
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 307c21d..b41d851 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -19,22 +19,21 @@
 
 #include <iomanip>
 #include <limits>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <tuple>
 #include <vector>
 
+#include "Source.h"
 #include "androidfw/ConfigDescription.h"
 #include "androidfw/StringPiece.h"
 #include "utils/JenkinsHash.h"
 
-#include "Source.h"
-
 namespace aapt {
 
 /**
- * The various types of resource types available. Corresponds
- * to the 'type' in package:type/entry.
+ * The various types of resource types available.
  */
 enum class ResourceType {
   kAnim,
@@ -78,15 +77,63 @@
 const ResourceType* ParseResourceType(const android::StringPiece& str);
 
 /**
+ * Pair of type name as in ResourceTable and actual resource type.
+ * Corresponds to the 'type' in package:type/entry.
+ *
+ * This is to support resource types with custom names inside resource tables.
+ */
+struct ResourceNamedType {
+  std::string name;
+  ResourceType type = ResourceType::kRaw;
+
+  ResourceNamedType() = default;
+  ResourceNamedType(const android::StringPiece& n, ResourceType t);
+
+  int compare(const ResourceNamedType& other) const;
+
+  const std::string& to_string() const;
+};
+
+/**
+ * Same as ResourceNamedType, but uses StringPieces instead.
+ * Use this if you need to avoid copying and know that
+ * the lifetime of this object is shorter than that
+ * of the original string.
+ */
+struct ResourceNamedTypeRef {
+  android::StringPiece name;
+  ResourceType type = ResourceType::kRaw;
+
+  ResourceNamedTypeRef() = default;
+  ResourceNamedTypeRef(const ResourceNamedTypeRef&) = default;
+  ResourceNamedTypeRef(ResourceNamedTypeRef&&) = default;
+  ResourceNamedTypeRef(const ResourceNamedType& rhs);  // NOLINT(google-explicit-constructor)
+  ResourceNamedTypeRef(const android::StringPiece& n, ResourceType t);
+  ResourceNamedTypeRef& operator=(const ResourceNamedTypeRef& rhs) = default;
+  ResourceNamedTypeRef& operator=(ResourceNamedTypeRef&& rhs) = default;
+  ResourceNamedTypeRef& operator=(const ResourceNamedType& rhs);
+
+  ResourceNamedType ToResourceNamedType() const;
+
+  std::string to_string() const;
+};
+
+ResourceNamedTypeRef ResourceNamedTypeWithDefaultName(ResourceType t);
+
+std::optional<ResourceNamedTypeRef> ParseResourceNamedType(const android::StringPiece& s);
+
+/**
  * A resource's name. This can uniquely identify
  * a resource in the ResourceTable.
  */
 struct ResourceName {
   std::string package;
-  ResourceType type = ResourceType::kRaw;
+  ResourceNamedType type;
   std::string entry;
 
   ResourceName() = default;
+  ResourceName(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+               const android::StringPiece& e);
   ResourceName(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
 
   int compare(const ResourceName& other) const;
@@ -103,13 +150,15 @@
  */
 struct ResourceNameRef {
   android::StringPiece package;
-  ResourceType type = ResourceType::kRaw;
+  ResourceNamedTypeRef type;
   android::StringPiece entry;
 
   ResourceNameRef() = default;
   ResourceNameRef(const ResourceNameRef&) = default;
   ResourceNameRef(ResourceNameRef&&) = default;
   ResourceNameRef(const ResourceName& rhs);  // NOLINT(google-explicit-constructor)
+  ResourceNameRef(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+                  const android::StringPiece& e);
   ResourceNameRef(const android::StringPiece& p, ResourceType t, const android::StringPiece& e);
   ResourceNameRef& operator=(const ResourceNameRef& rhs) = default;
   ResourceNameRef& operator=(ResourceNameRef&& rhs) = default;
@@ -295,17 +344,98 @@
 }
 
 //
+// ResourceNamedType implementation.
+//
+inline ResourceNamedType::ResourceNamedType(const android::StringPiece& n, ResourceType t)
+    : name(n.to_string()), type(t) {
+}
+
+inline int ResourceNamedType::compare(const ResourceNamedType& other) const {
+  int cmp = static_cast<int>(type) - static_cast<int>(other.type);
+  if (cmp != 0) return cmp;
+  cmp = name.compare(other.name);
+  return cmp;
+}
+
+inline const std::string& ResourceNamedType::to_string() const {
+  return name;
+}
+
+inline bool operator<(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+  return lhs.compare(rhs) < 0;
+}
+
+inline bool operator==(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+  return lhs.compare(rhs) == 0;
+}
+
+inline bool operator!=(const ResourceNamedType& lhs, const ResourceNamedType& rhs) {
+  return lhs.compare(rhs) != 0;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNamedType& val) {
+  return out << val.to_string();
+}
+
+//
+// ResourceNamedTypeRef implementation.
+//
+inline ResourceNamedTypeRef::ResourceNamedTypeRef(const android::StringPiece& n, ResourceType t)
+    : name(n), type(t) {
+}
+
+inline ResourceNamedTypeRef::ResourceNamedTypeRef(const ResourceNamedType& rhs)
+    : name(rhs.name), type(rhs.type) {
+}
+
+inline ResourceNamedTypeRef& ResourceNamedTypeRef::operator=(const ResourceNamedType& rhs) {
+  name = rhs.name;
+  type = rhs.type;
+  return *this;
+}
+
+inline ResourceNamedType ResourceNamedTypeRef::ToResourceNamedType() const {
+  return ResourceNamedType(name, type);
+}
+
+inline std::string ResourceNamedTypeRef::to_string() const {
+  return name.to_string();
+}
+
+inline bool operator<(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+  return std::tie(lhs.type, lhs.name) < std::tie(rhs.type, rhs.name);
+}
+
+inline bool operator==(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+  return std::tie(lhs.type, lhs.name) == std::tie(rhs.type, rhs.name);
+}
+
+inline bool operator!=(const ResourceNamedTypeRef& lhs, const ResourceNamedTypeRef& rhs) {
+  return std::tie(lhs.type, lhs.name) != std::tie(rhs.type, rhs.name);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ResourceNamedTypeRef& val) {
+  return out << val.name;
+}
+
+//
 // ResourceName implementation.
 //
 
+inline ResourceName::ResourceName(const android::StringPiece& p, const ResourceNamedTypeRef& t,
+                                  const android::StringPiece& e)
+    : package(p.to_string()), type(t.ToResourceNamedType()), entry(e.to_string()) {
+}
+
 inline ResourceName::ResourceName(const android::StringPiece& p, ResourceType t,
                                   const android::StringPiece& e)
-    : package(p.to_string()), type(t), entry(e.to_string()) {}
+    : ResourceName(p, ResourceNamedTypeWithDefaultName(t), e) {
+}
 
 inline int ResourceName::compare(const ResourceName& other) const {
   int cmp = package.compare(other.package);
   if (cmp != 0) return cmp;
-  cmp = static_cast<int>(type) - static_cast<int>(other.type);
+  cmp = type.compare(other.type);
   if (cmp != 0) return cmp;
   cmp = entry.compare(other.entry);
   return cmp;
@@ -341,9 +471,16 @@
 inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs)
     : package(rhs.package), type(rhs.type), entry(rhs.entry) {}
 
+inline ResourceNameRef::ResourceNameRef(const android::StringPiece& p,
+                                        const ResourceNamedTypeRef& t,
+                                        const android::StringPiece& e)
+    : package(p), type(t), entry(e) {
+}
+
 inline ResourceNameRef::ResourceNameRef(const android::StringPiece& p, ResourceType t,
                                         const android::StringPiece& e)
-    : package(p), type(t), entry(e) {}
+    : ResourceNameRef(p, ResourceNamedTypeWithDefaultName(t), e) {
+}
 
 inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) {
   package = rhs.package;
@@ -400,7 +537,7 @@
   size_t operator()(const aapt::ResourceName& name) const {
     android::hash_t h = 0;
     h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.package)));
-    h = android::JenkinsHashMix(h, static_cast<uint32_t>(name.type));
+    h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.type.name)));
     h = android::JenkinsHashMix(h, static_cast<uint32_t>(hash<string>()(name.entry)));
     return static_cast<size_t>(h);
   }
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 792a306..42715f9 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -604,7 +604,8 @@
       return false;
     }
 
-    out_resource->name.type = ResourceType::kId;
+    out_resource->name.type =
+        ResourceNamedTypeWithDefaultName(ResourceType::kId).ToResourceNamedType();
     out_resource->name.entry = maybe_name.value().to_string();
 
     // Ids either represent a unique resource id or reference another resource id
@@ -623,7 +624,7 @@
         // A null reference also means there is no inner element when ids are in the form:
         //    <id name="name"/>
         out_resource->value = util::make_unique<Id>();
-      } else if (!ref || ref->name.value().type != ResourceType::kId) {
+      } else if (!ref || ref->name.value().type.type != ResourceType::kId) {
         // If an inner element exists, the inner element must be a reference to another resource id
         diag_->Error(DiagMessage(out_resource->source)
                          << "<" << parser->element_name()
@@ -640,7 +641,8 @@
       return false;
     }
 
-    out_resource->name.type = ResourceType::kMacro;
+    out_resource->name.type =
+        ResourceNamedTypeWithDefaultName(ResourceType::kMacro).ToResourceNamedType();
     out_resource->name.entry = maybe_name.value().to_string();
     return ParseMacro(parser, out_resource);
   }
@@ -656,7 +658,8 @@
         return false;
       }
 
-      out_resource->name.type = item_iter->second.type;
+      out_resource->name.type =
+          ResourceNamedTypeWithDefaultName(item_iter->second.type).ToResourceNamedType();
       out_resource->name.entry = maybe_name.value().to_string();
 
       // Only use the implied format of the type when there is no explicit format.
@@ -699,7 +702,7 @@
   if (can_be_item) {
     // Try parsing the elementName (or type) as a resource. These shall only be
     // resources like 'layout' or 'xml' and they can only be references.
-    const ResourceType* parsed_type = ParseResourceType(resource_type);
+    std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(resource_type);
     if (parsed_type) {
       if (!maybe_name) {
         diag_->Error(DiagMessage(out_resource->source)
@@ -708,7 +711,7 @@
         return false;
       }
 
-      out_resource->name.type = *parsed_type;
+      out_resource->name.type = parsed_type->ToResourceNamedType();
       out_resource->name.entry = maybe_name.value().to_string();
       out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString);
       if (!out_resource->value) {
@@ -933,7 +936,7 @@
     return false;
   }
 
-  const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
   if (!parsed_type) {
     diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '"
                                                    << maybe_type.value()
@@ -941,7 +944,7 @@
     return false;
   }
 
-  out_resource->name.type = *parsed_type;
+  out_resource->name.type = parsed_type->ToResourceNamedType();
 
   if (std::optional<StringPiece> maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) {
     std::optional<ResourceId> maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value());
@@ -953,7 +956,7 @@
     out_resource->id = maybe_id.value();
   }
 
-  if (*parsed_type == ResourceType::kId) {
+  if (parsed_type->type == ResourceType::kId) {
     // An ID marked as public is also the definition of an ID.
     out_resource->value = util::make_unique<Id>();
   }
@@ -978,7 +981,7 @@
     return false;
   }
 
-  const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
   if (!parsed_type) {
     diag->Error(DiagMessage(out_resource->source)
                 << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">");
@@ -1096,7 +1099,7 @@
     return false;
   }
 
-  const ResourceType* parsed_type = ParseResourceType(maybe_type.value());
+  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(maybe_type.value());
   if (!parsed_type) {
     diag_->Error(DiagMessage(out_resource->source)
                  << "invalid resource type '" << maybe_type.value() << "' in <"
@@ -1104,7 +1107,7 @@
     return false;
   }
 
-  out_resource->name.type = *parsed_type;
+  out_resource->name.type = parsed_type->ToResourceNamedType();
   return true;
 }
 
@@ -1208,8 +1211,8 @@
         continue;
       }
 
-      const ResourceType* type = ParseResourceType(item_type.value());
-      if (type == nullptr) {
+      std::optional<ResourceNamedTypeRef> type = ParseResourceNamedType(item_type.value());
+      if (!type) {
         diag_->Error(DiagMessage(element_source)
                      << "invalid resource type '" << item_type.value()
                      << "' in <item> within an <overlayable>");
@@ -1223,7 +1226,7 @@
       overlayable_item.source = element_source;
 
       ParsedResource child_resource{};
-      child_resource.name.type = *type;
+      child_resource.name.type = type->ToResourceNamedType();
       child_resource.name.entry = item_name.value().to_string();
       child_resource.overlayable_item = overlayable_item;
       out_resource->child_resources.push_back(std::move(child_resource));
@@ -1289,7 +1292,8 @@
 
 bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser,
                                    ParsedResource* out_resource, bool weak) {
-  out_resource->name.type = ResourceType::kAttr;
+  out_resource->name.type =
+      ResourceNamedTypeWithDefaultName(ResourceType::kAttr).ToResourceNamedType();
 
   // Attributes only end up in default configuration.
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
@@ -1475,7 +1479,8 @@
   }
 
   return Attribute::Symbol{
-      Reference(ResourceNameRef({}, ResourceType::kId, maybe_name.value())),
+      Reference(ResourceNameRef({}, ResourceNamedTypeWithDefaultName(ResourceType::kId),
+                                maybe_name.value())),
       val.data, val.dataType};
 }
 
@@ -1509,7 +1514,7 @@
 
 bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
                                 ParsedResource* out_resource) {
-  out_resource->name.type = type;
+  out_resource->name.type = ResourceNamedTypeWithDefaultName(type).ToResourceNamedType();
 
   std::unique_ptr<Style> style = util::make_unique<Style>();
 
@@ -1535,7 +1540,8 @@
     size_t pos = style_name.find_last_of(u'.');
     if (pos != std::string::npos) {
       style->parent_inferred = true;
-      style->parent = Reference(ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
+      style->parent = Reference(ResourceName(
+          {}, ResourceNamedTypeWithDefaultName(ResourceType::kStyle), style_name.substr(0, pos)));
     }
   }
 
@@ -1591,7 +1597,8 @@
 bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser,
                                     ParsedResource* out_resource,
                                     const uint32_t typeMask) {
-  out_resource->name.type = ResourceType::kArray;
+  out_resource->name.type =
+      ResourceNamedTypeWithDefaultName(ResourceType::kArray).ToResourceNamedType();
 
   std::unique_ptr<Array> array = util::make_unique<Array>();
 
@@ -1646,7 +1653,8 @@
 
 bool ResourceParser::ParsePlural(xml::XmlPullParser* parser,
                                  ParsedResource* out_resource) {
-  out_resource->name.type = ResourceType::kPlurals;
+  out_resource->name.type =
+      ResourceNamedTypeWithDefaultName(ResourceType::kPlurals).ToResourceNamedType();
 
   std::unique_ptr<Plural> plural = util::make_unique<Plural>();
 
@@ -1727,7 +1735,8 @@
 
 bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
                                            ParsedResource* out_resource) {
-  out_resource->name.type = ResourceType::kStyleable;
+  out_resource->name.type =
+      ResourceNamedTypeWithDefaultName(ResourceType::kStyleable).ToResourceNamedType();
 
   if (!options_.preserve_visibility_of_styleables) {
     // This was added in change Idd21b5de4d20be06c6f8c8eb5a22ccd68afc4927 to mimic aapt1, but no one
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index dae89b0..98cce26 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -473,7 +473,7 @@
   }
 
   auto package = FindOrCreatePackage(res.name.package);
-  auto type = package->FindOrCreateType(res.name.type);
+  auto type = package->FindOrCreateType(res.name.type.type);
   auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), res.name.entry,
                                    NameEqualRange<ResourceEntry>{});
   const size_t entry_count = std::distance(entry_it.first, entry_it.second);
@@ -593,7 +593,7 @@
     return {};
   }
 
-  ResourceTableType* type = package->FindType(name.type);
+  ResourceTableType* type = package->FindType(name.type.type);
   if (type == nullptr) {
     return {};
   }
@@ -612,7 +612,7 @@
     return {};
   }
 
-  ResourceTableType* type = package->FindType(name.type);
+  ResourceTableType* type = package->FindType(name.type.type);
   if (type == nullptr) {
     return {};
   }
@@ -633,7 +633,7 @@
     return {};
   }
 
-  ResourceTableType* type = package->FindType(name.type);
+  ResourceTableType* type = package->FindType(name.type.type);
   if (type == nullptr) {
     return {};
   }
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index ead06bf..23f6c88 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -50,12 +50,11 @@
   name_out.package =
       util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen));
 
-  const ResourceType* type;
+  std::optional<ResourceNamedTypeRef> type;
   if (name_in.type) {
-    type = ParseResourceType(
-        util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
+    type = ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type, name_in.typeLen)));
   } else if (name_in.type8) {
-    type = ParseResourceType(StringPiece(name_in.type8, name_in.typeLen));
+    type = ParseResourceNamedType(StringPiece(name_in.type8, name_in.typeLen));
   } else {
     return {};
   }
@@ -64,7 +63,7 @@
     return {};
   }
 
-  name_out.type = *type;
+  name_out.type = type->ToResourceNamedType();
 
   if (name_in.name) {
     name_out.entry =
@@ -85,12 +84,12 @@
 
   name_out.package = std::string(name_in.package, name_in.package_len);
 
-  const ResourceType* type;
+  std::optional<ResourceNamedTypeRef> type;
   if (name_in.type16) {
-    type = ParseResourceType(
-        util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
+    type =
+        ParseResourceNamedType(util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
   } else if (name_in.type) {
-    type = ParseResourceType(StringPiece(name_in.type, name_in.type_len));
+    type = ParseResourceNamedType(StringPiece(name_in.type, name_in.type_len));
   } else {
     return {};
   }
@@ -99,7 +98,7 @@
     return {};
   }
 
-  name_out.type = *type;
+  name_out.type = type->ToResourceNamedType();
 
   if (name_in.entry16) {
     name_out.entry =
@@ -133,7 +132,7 @@
     return false;
   }
 
-  const ResourceType* parsed_type = ParseResourceType(type);
+  std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(type);
   if (!parsed_type) {
     return false;
   }
@@ -181,7 +180,7 @@
       return false;
     }
 
-    if (create && name.type != ResourceType::kId) {
+    if (create && name.type.type != ResourceType::kId) {
       return false;
     }
 
@@ -230,7 +229,7 @@
 
     if (out_ref) {
       out_ref->package = package;
-      out_ref->type = ResourceType::kAttr;
+      out_ref->type = ResourceNamedTypeWithDefaultName(ResourceType::kAttr);
       out_ref->entry = entry;
     }
     return true;
@@ -272,7 +271,7 @@
   }
 
   ResourceNameRef ref;
-  ref.type = ResourceType::kStyle;
+  ref.type = ResourceNamedTypeWithDefaultName(ResourceType::kStyle);
 
   StringPiece type_str;
   android::ExtractResourceName(name, &ref.package, &type_str, &ref.entry);
@@ -323,7 +322,8 @@
     p++;
   }
 
-  ref.name = ResourceName(package, ResourceType::kAttr, name.empty() ? trimmed_str : name);
+  ref.name = ResourceName(package, ResourceNamedTypeWithDefaultName(ResourceType::kAttr),
+                          name.empty() ? trimmed_str : name);
   return std::optional<Reference>(std::move(ref));
 }
 
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index b3ab4ff..b796eb0 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -116,7 +116,7 @@
 }
 
 bool Reference::Flatten(android::Res_value* out_value) const {
-  if (name && name.value().type == ResourceType::kMacro) {
+  if (name && name.value().type.type == ResourceType::kMacro) {
     return false;
   }
 
@@ -192,7 +192,7 @@
     if (print_package) {
       printer->Print(name.to_string());
     } else {
-      printer->Print(to_string(name.type));
+      printer->Print(name.type.to_string());
       printer->Print("/");
       printer->Print(name.entry);
     }
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
index c557f3c..2c55d1d 100644
--- a/tools/aapt2/Resource_test.cpp
+++ b/tools/aapt2/Resource_test.cpp
@@ -18,6 +18,9 @@
 
 #include "test/Test.h"
 
+using ::testing::Eq;
+using ::testing::Optional;
+
 namespace aapt {
 
 TEST(ResourceTypeTest, ParseResourceTypes) {
@@ -125,4 +128,104 @@
   EXPECT_EQ(type, nullptr);
 }
 
+TEST(ResourceTypeTest, ParseResourceNamedType) {
+  auto type = ParseResourceNamedType("anim");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("anim", ResourceType::kAnim))));
+
+  type = ParseResourceNamedType("layout");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout", ResourceType::kLayout))));
+
+  type = ParseResourceNamedType("layout:2");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:2", ResourceType::kLayout))));
+
+  type = ParseResourceNamedType("layout:another");
+  EXPECT_THAT(type, Optional(Eq(ResourceNamedType("layout:another", ResourceType::kLayout))));
+
+  type = ParseResourceNamedType("layout:");
+  EXPECT_THAT(type, Eq(std::nullopt));
+
+  type = ParseResourceNamedType("layout2");
+  EXPECT_THAT(type, Eq(std::nullopt));
+
+  type = ParseResourceNamedType("blahaha");
+  EXPECT_THAT(type, Eq(std::nullopt));
+}
+
+TEST(ResourceTypeTest, ResourceNamedTypeWithDefaultName) {
+  auto type = ResourceNamedTypeWithDefaultName(ResourceType::kAnim);
+  EXPECT_THAT(type, Eq(ResourceNamedType("anim", ResourceType::kAnim)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kAnimator);
+  EXPECT_THAT(type, Eq(ResourceNamedType("animator", ResourceType::kAnimator)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kArray);
+  EXPECT_THAT(type, Eq(ResourceNamedType("array", ResourceType::kArray)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kAttr);
+  EXPECT_THAT(type, Eq(ResourceNamedType("attr", ResourceType::kAttr)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kAttrPrivate);
+  EXPECT_THAT(type, Eq(ResourceNamedType("^attr-private", ResourceType::kAttrPrivate)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kBool);
+  EXPECT_THAT(type, Eq(ResourceNamedType("bool", ResourceType::kBool)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kColor);
+  EXPECT_THAT(type, Eq(ResourceNamedType("color", ResourceType::kColor)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kConfigVarying);
+  EXPECT_THAT(type, Eq(ResourceNamedType("configVarying", ResourceType::kConfigVarying)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kDimen);
+  EXPECT_THAT(type, Eq(ResourceNamedType("dimen", ResourceType::kDimen)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kDrawable);
+  EXPECT_THAT(type, Eq(ResourceNamedType("drawable", ResourceType::kDrawable)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kFont);
+  EXPECT_THAT(type, Eq(ResourceNamedType("font", ResourceType::kFont)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kFraction);
+  EXPECT_THAT(type, Eq(ResourceNamedType("fraction", ResourceType::kFraction)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kId);
+  EXPECT_THAT(type, Eq(ResourceNamedType("id", ResourceType::kId)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kInteger);
+  EXPECT_THAT(type, Eq(ResourceNamedType("integer", ResourceType::kInteger)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kInterpolator);
+  EXPECT_THAT(type, Eq(ResourceNamedType("interpolator", ResourceType::kInterpolator)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kLayout);
+  EXPECT_THAT(type, Eq(ResourceNamedType("layout", ResourceType::kLayout)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kMenu);
+  EXPECT_THAT(type, Eq(ResourceNamedType("menu", ResourceType::kMenu)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kMipmap);
+  EXPECT_THAT(type, Eq(ResourceNamedType("mipmap", ResourceType::kMipmap)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kNavigation);
+  EXPECT_THAT(type, Eq(ResourceNamedType("navigation", ResourceType::kNavigation)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kPlurals);
+  EXPECT_THAT(type, Eq(ResourceNamedType("plurals", ResourceType::kPlurals)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kRaw);
+  EXPECT_THAT(type, Eq(ResourceNamedType("raw", ResourceType::kRaw)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kString);
+  EXPECT_THAT(type, Eq(ResourceNamedType("string", ResourceType::kString)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kStyle);
+  EXPECT_THAT(type, Eq(ResourceNamedType("style", ResourceType::kStyle)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kTransition);
+  EXPECT_THAT(type, Eq(ResourceNamedType("transition", ResourceType::kTransition)));
+
+  type = ResourceNamedTypeWithDefaultName(ResourceType::kXml);
+  EXPECT_THAT(type, Eq(ResourceNamedType("xml", ResourceType::kXml)));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e614a75..790f2b3 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -207,7 +207,7 @@
     }
 
     // Check to see if this is an 'id' with the target package.
-    if (name.type == ResourceType::kId && symbol->id) {
+    if (name.type.type == ResourceType::kId && symbol->id) {
       ResourceId* id = &symbol->id.value();
       if (id->package_id() > kAppPackageId) {
         // Rewrite the resource ID to be compatible pre-O.
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 339b8af..fa816be 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -275,7 +275,7 @@
     return false;
   }
 
-  auto key = ResourceTypeKey{name.type, id.type_id()};
+  auto key = ResourceTypeKey{name.type.type, id.type_id()};
   auto type = types_.find(key);
   if (type == types_.end()) {
     // The type has not been assigned an id yet. Ensure that the specified id is not being used by
@@ -291,7 +291,7 @@
 
   if (!visibility.staged_api) {
     // Ensure that non-staged resources can only exist in one type ID.
-    auto non_staged_type = non_staged_type_ids_.emplace(name.type, id.type_id());
+    auto non_staged_type = non_staged_type_ids_.emplace(name.type.type, id.type_id());
     if (!non_staged_type.second && non_staged_type.first->second != id.type_id()) {
       diag->Error(DiagMessage() << "can't assign ID " << id << " to resource " << name
                                 << " because type already has ID " << std::hex
@@ -316,14 +316,14 @@
   CHECK(name.package.empty() || name.package == package_name_);
 
   // Find the type id for non-staged resources of this type.
-  auto non_staged_type = non_staged_type_ids_.find(name.type);
+  auto non_staged_type = non_staged_type_ids_.find(name.type.type);
   if (non_staged_type == non_staged_type_ids_.end()) {
     auto next_type_id = type_id_finder_.NextId();
     CHECK(next_type_id.has_value()) << "resource type IDs allocated have exceeded maximum (256)";
-    non_staged_type = non_staged_type_ids_.emplace(name.type, *next_type_id).first;
+    non_staged_type = non_staged_type_ids_.emplace(name.type.type, *next_type_id).first;
   }
 
-  ResourceTypeKey key{name.type, non_staged_type->second};
+  ResourceTypeKey key{name.type.type, non_staged_type->second};
   auto type = types_.find(key);
   if (type == types_.end()) {
     type = types_.emplace(key, TypeGroup(package_id_, key.id)).first;
diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp
index 5054115..bb72159 100644
--- a/tools/aapt2/compile/XmlIdCollector.cpp
+++ b/tools/aapt2/compile/XmlIdCollector.cpp
@@ -46,7 +46,7 @@
       ResourceNameRef name;
       bool create = false;
       if (ResourceUtils::ParseReference(attr.value, &name, &create, nullptr)) {
-        if (create && name.type == ResourceType::kId) {
+        if (create && name.type.type == ResourceType::kId) {
           if (!text::IsValidResourceEntryName(name.entry)) {
             source_diag_->Error(DiagMessage(element->line_number)
                                    << "id '" << name << "' has an invalid entry name");
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 11d02f0..c65c550 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -556,8 +556,8 @@
 std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
                                                        const ConfigDescription& config,
                                                        const android::Res_value& value) {
-  std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(name.type, config, value_pool_,
-                                                                  value, &table_->string_pool);
+  std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(
+      name.type.type, config, value_pool_, value, &table_->string_pool);
   if (files_ != nullptr) {
     FileReference* file_ref = ValueCast<FileReference>(item.get());
     if (file_ref != nullptr) {
@@ -575,7 +575,7 @@
 std::unique_ptr<Value> BinaryResourceParser::ParseMapEntry(const ResourceNameRef& name,
                                                            const ConfigDescription& config,
                                                            const ResTable_map_entry* map) {
-  switch (name.type) {
+  switch (name.type.type) {
     case ResourceType::kStyle:
       // fallthrough
     case ResourceType::kConfigVarying:  // legacy thing used in tests
@@ -594,8 +594,8 @@
       // We can ignore the value here.
       return util::make_unique<Id>();
     default:
-      diag_->Error(DiagMessage() << "illegal map type '" << to_string(name.type) << "' ("
-                                 << (int)name.type << ")");
+      diag_->Error(DiagMessage() << "illegal map type '" << name.type << "' ("
+                                 << (int)name.type.type << ")");
       break;
   }
   return {};
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 3b3c6e1..a963d98 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -229,8 +229,8 @@
   const std::string package_name =
       name.package.empty() ? fallback_package_name.to_string() : name.package;
   const std::string entry = JavaClassGenerator::TransformToFieldName(name.entry);
-  return FieldReference(
-      StringPrintf("%s.R.%s.%s", package_name.c_str(), to_string(name.type).data(), entry.c_str()));
+  return FieldReference(StringPrintf("%s.R.%s.%s", package_name.c_str(),
+                                     name.type.to_string().data(), entry.c_str()));
 }
 
 bool JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
@@ -451,7 +451,7 @@
                                          MethodDefinition* out_rewrite_method,
                                          text::Printer* r_txt_printer) {
   ResourceId real_id = id;
-  if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
+  if (context_->GetMinSdkVersion() < SDK_O && name.type.type == ResourceType::kId &&
       id.package_id() > kAppPackageId) {
     // Workaround for feature splits using package IDs > 0x7F.
     // See b/37498913.
@@ -489,7 +489,7 @@
 
   if (r_txt_printer != nullptr) {
     r_txt_printer->Print("int ")
-        .Print(to_string(name.type))
+        .Print(name.type.to_string())
         .Print(" ")
         .Print(field_name)
         .Print(" ")
@@ -497,7 +497,7 @@
   }
 
   if (out_rewrite_method != nullptr) {
-    const StringPiece& type_str = to_string(name.type);
+    const std::string type_str = name.type.to_string();
     out_rewrite_method->AppendStatement(
         StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | packageIdBits;", type_str.data(),
                      field_name.data(), type_str.data(), field_name.data()));
@@ -561,7 +561,7 @@
       return false;
     }
 
-    if (resource_name.type == ResourceType::kStyleable) {
+    if (resource_name.type.type == ResourceType::kStyleable) {
       CHECK(!entry->values.empty());
       const auto styleable = reinterpret_cast<const Styleable*>(entry->values.front()->value.get());
       if (!ProcessStyleable(resource_name, id, *styleable, package_name_to_generate,
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 4a2d0ae..e53e220 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -358,7 +358,7 @@
     return false;
   }
 
-  switch (res->file.name.type) {
+  switch (res->file.name.type.type) {
     case ResourceType::kLayout: {
       LayoutVisitor visitor(res->file, keep_set);
       res->root->Accept(&visitor);
@@ -465,7 +465,7 @@
   locations->insert(location);
 
   // TODO: allow for more reference types if we can determine its safe.
-  if (location.name.type != ResourceType::kLayout) {
+  if (location.name.type.type != ResourceType::kLayout) {
     return false;
   }
 
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 47c804c..5372cf2 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -352,7 +352,7 @@
   }
 
   const ResourceName& ref_name = ref.name.value();
-  CHECK_EQ(ref_name.type, ResourceType::kAttr);
+  CHECK_EQ(ref_name.type.type, ResourceType::kAttr);
 
   if (!ref_name.package.empty()) {
     *out_msg << ref_name.package << ":";
@@ -385,7 +385,7 @@
   Reference transformed_reference = reference;
   xml::ResolvePackage(decls, &transformed_reference);
 
-  if (transformed_reference.name.value().type == ResourceType::kMacro) {
+  if (transformed_reference.name.value().type.type == ResourceType::kMacro) {
     if (transformed_reference.name.value().package.empty()) {
       transformed_reference.name.value().package = callsite.package;
     }
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index d094d36..d78f0ac 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -349,7 +349,7 @@
   file_ref->file = file;
 
   ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package);
-  pkg->FindOrCreateType(file_desc.name.type)
+  pkg->FindOrCreateType(file_desc.name.type.type)
       ->FindOrCreateEntry(file_desc.name.entry)
       ->FindOrCreateValue(file_desc.config, {})
       ->value = std::move(file_ref);
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 2d58cbf..92b45c3 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -185,7 +185,7 @@
     const ResourceName& name) {
   std::optional<ResourceTable::SearchResult> result = table_->FindResource(name);
   if (!result) {
-    if (name.type == ResourceType::kAttr) {
+    if (name.type.type == ResourceType::kAttr) {
       // Recurse and try looking up a private attribute.
       return FindByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
     }
@@ -203,7 +203,7 @@
         (sr.entry->id.value().package_id() == 0) || sr.entry->visibility.staged_api;
   }
 
-  if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
+  if (name.type.type == ResourceType::kAttr || name.type.type == ResourceType::kAttrPrivate) {
     const ConfigDescription kDefaultConfig;
     ResourceConfigValue* config_value = sr.entry->FindValue(kDefaultConfig);
     if (config_value) {
@@ -366,7 +366,7 @@
   }
 
   std::unique_ptr<SymbolTable::Symbol> s;
-  if (real_name.type == ResourceType::kAttr) {
+  if (real_name.type.type == ResourceType::kAttr) {
     s = LookupAttributeInTable(asset_manager_, res_id);
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
@@ -413,7 +413,7 @@
 
   ResourceName& name = maybe_name.value();
   std::unique_ptr<SymbolTable::Symbol> s;
-  if (name.type == ResourceType::kAttr) {
+  if (name.type.type == ResourceType::kAttr) {
     s = LookupAttributeInTable(asset_manager_, id);
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 65ae7be..c17837c 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -38,7 +38,7 @@
   std::hash<std::string> str_hash;
   android::hash_t hash = 0;
   hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.package));
-  hash = android::JenkinsHashMix(hash, (uint32_t)name.type);
+  hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.type.name));
   hash = android::JenkinsHashMix(hash, (uint32_t)str_hash(name.entry));
   return hash;
 }