Merge "Update StatsLogProcessor to handle BinaryPushStateChanged"
diff --git a/Android.bp b/Android.bp
index dbef611..5796fb1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -402,7 +402,7 @@
         "ext",
         "unsupportedappusage",
         "framework-media-stubs-systemapi",
-        "framework_mediaprovider_stubs",
+        "framework-mediaprovider-stubs-systemapi",
         "framework-tethering",
         "framework-telephony-stubs",
     ],
@@ -491,14 +491,11 @@
     apex_available: ["//apex_available:platform"],
     visibility: [
         "//frameworks/base",
-        // TODO(b/144149403) remove the below lines
+        // TODO(b/147128803) remove the below lines
         "//frameworks/base/apex/appsearch/framework",
         "//frameworks/base/apex/blobstore/framework",
         "//frameworks/base/apex/jobscheduler/framework",
-        "//frameworks/base/apex/permission/framework",
         "//frameworks/base/apex/statsd/service",
-        "//frameworks/base/telephony",
-        "//frameworks/opt/net/wifi/service",
     ],
 }
 
@@ -517,9 +514,9 @@
     installable: false, // this lib is a build-only library
     static_libs: [
         "framework-minus-apex",
-        "framework-media-stubs-systemapi",
-        "framework_mediaprovider_stubs",
         "framework-appsearch", // TODO(b/146218515): should be framework-appsearch-stubs
+        "framework-media-stubs-systemapi",
+        "framework-mediaprovider-stubs-systemapi",
         "framework-permission-stubs-systemapi",
         "framework-sdkextensions-stubs-systemapi",
         // TODO(b/146167933): Use framework-statsd-stubs instead.
diff --git a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
index e4ab823..22daa8e 100644
--- a/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
+++ b/apex/statsd/tests/libstatspull/jni/stats_pull_helper.cpp
@@ -46,15 +46,15 @@
     }
 }
 
-static status_pull_atom_return_t pullAtomCallback(int32_t atomTag, pulled_stats_event_list* data,
-                                                  void* /*cookie*/) {
+static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag, AStatsEventList* data,
+                                                             void* /*cookie*/) {
     sNumPulls++;
     sleep_for(std::chrono::milliseconds(sLatencyMillis));
     for (int i = 0; i < sAtomsPerPull; i++) {
-        stats_event* event = add_stats_event_to_pull_data(data);
-        stats_event_set_atom_id(event, atomTag);
-        stats_event_write_int64(event, (int64_t) sNumPulls);
-        stats_event_build(event);
+        AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+        AStatsEvent_setAtomId(event, atomTag);
+        AStatsEvent_writeInt64(event, (int64_t) sNumPulls);
+        AStatsEvent_build(event);
     }
     return sPullReturnVal;
 }
@@ -71,11 +71,12 @@
     sLatencyMillis = latencyMillis;
     sAtomsPerPull = atomsPerPull;
     sNumPulls = 0;
-    pull_atom_metadata metadata = {.cool_down_ns = coolDownNs,
-                                   .timeout_ns = timeoutNs,
-                                   .additive_fields = nullptr,
-                                   .additive_fields_size = 0};
-    register_stats_pull_atom_callback(sAtomTag, &pullAtomCallback, &metadata, nullptr);
+    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
+    AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, coolDownNs);
+    AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, timeoutNs);
+
+    AStatsManager_registerPullAtomCallback(sAtomTag, &pullAtomCallback, metadata, nullptr);
+    AStatsManager_PullAtomMetadata_release(metadata);
 }
 
 extern "C"
@@ -83,6 +84,6 @@
 Java_com_android_internal_os_statsd_libstats_LibStatsPullTests_unregisterStatsPuller(
         JNIEnv* /*env*/, jobject /* this */, jint /*atomTag*/)
 {
-    unregister_stats_pull_atom_callback(sAtomTag);
+    AStatsManager_unregisterPullAtomCallback(sAtomTag);
 }
-} // namespace
\ No newline at end of file
+} // namespace
diff --git a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
index dbd636d..e119b4c 100644
--- a/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
+++ b/apex/statsd/tests/libstatspull/src/com/android/internal/os/statsd/libstats/LibStatsPullTests.java
@@ -71,7 +71,6 @@
      */
     @Before
     public void setup() {
-//        Debug.waitForDebugger();
         mContext = InstrumentationRegistry.getTargetContext();
         assertThat(InstrumentationRegistry.getInstrumentation()).isNotNull();
         sPullReturnValue = StatsManager.PULL_SUCCESS;
diff --git a/api/current.txt b/api/current.txt
index cab81ad..87a5cd7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5919,6 +5919,7 @@
     method public long[] getVibrationPattern();
     method public boolean hasUserSetImportance();
     method public boolean hasUserSetSound();
+    method public boolean isImportantConversation();
     method public void setAllowBubbles(boolean);
     method public void setBypassDnd(boolean);
     method public void setConversationId(@Nullable String, @Nullable String);
@@ -6031,14 +6032,19 @@
   public static class NotificationManager.Policy implements android.os.Parcelable {
     ctor public NotificationManager.Policy(int, int, int);
     ctor public NotificationManager.Policy(int, int, int, int);
+    ctor public NotificationManager.Policy(int, int, int, int, int);
     method public int describeContents();
     method public static String priorityCategoriesToString(int);
     method public static String prioritySendersToString(int);
     method public static String suppressedEffectsToString(int);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CONVERSATION_SENDERS_ANYONE = 1; // 0x1
+    field public static final int CONVERSATION_SENDERS_IMPORTANT = 2; // 0x2
+    field public static final int CONVERSATION_SENDERS_NONE = 3; // 0x3
     field @NonNull public static final android.os.Parcelable.Creator<android.app.NotificationManager.Policy> CREATOR;
     field public static final int PRIORITY_CATEGORY_ALARMS = 32; // 0x20
     field public static final int PRIORITY_CATEGORY_CALLS = 8; // 0x8
+    field public static final int PRIORITY_CATEGORY_CONVERSATIONS = 256; // 0x100
     field public static final int PRIORITY_CATEGORY_EVENTS = 2; // 0x2
     field public static final int PRIORITY_CATEGORY_MEDIA = 64; // 0x40
     field public static final int PRIORITY_CATEGORY_MESSAGES = 4; // 0x4
@@ -6059,6 +6065,7 @@
     field public static final int SUPPRESSED_EFFECT_STATUS_BAR = 32; // 0x20
     field public final int priorityCallSenders;
     field public final int priorityCategories;
+    field public final int priorityConversationSenders;
     field public final int priorityMessageSenders;
     field public final int suppressedVisualEffects;
   }
@@ -7140,6 +7147,7 @@
     field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8
     field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1
     field public static final int LEAVE_ALL_SYSTEM_APPS_ENABLED = 16; // 0x10
+    field public static final int LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK = 64; // 0x40
     field public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 16; // 0x10
     field public static final int LOCK_TASK_FEATURE_HOME = 4; // 0x4
     field public static final int LOCK_TASK_FEATURE_KEYGUARD = 32; // 0x20
@@ -43776,12 +43784,14 @@
     method public int getPriorityCallSenders();
     method public int getPriorityCategoryAlarms();
     method public int getPriorityCategoryCalls();
+    method public int getPriorityCategoryConversations();
     method public int getPriorityCategoryEvents();
     method public int getPriorityCategoryMedia();
     method public int getPriorityCategoryMessages();
     method public int getPriorityCategoryReminders();
     method public int getPriorityCategoryRepeatCallers();
     method public int getPriorityCategorySystem();
+    method public int getPriorityConversationSenders();
     method public int getPriorityMessageSenders();
     method public int getVisualEffectAmbient();
     method public int getVisualEffectBadge();
@@ -43791,6 +43801,10 @@
     method public int getVisualEffectPeek();
     method public int getVisualEffectStatusBar();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CONVERSATION_SENDERS_ANYONE = 1; // 0x1
+    field public static final int CONVERSATION_SENDERS_IMPORTANT = 2; // 0x2
+    field public static final int CONVERSATION_SENDERS_NONE = 3; // 0x3
+    field public static final int CONVERSATION_SENDERS_UNSET = 0; // 0x0
     field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.ZenPolicy> CREATOR;
     field public static final int PEOPLE_TYPE_ANYONE = 1; // 0x1
     field public static final int PEOPLE_TYPE_CONTACTS = 2; // 0x2
@@ -43807,6 +43821,7 @@
     method @NonNull public android.service.notification.ZenPolicy.Builder allowAlarms(boolean);
     method @NonNull public android.service.notification.ZenPolicy.Builder allowAllSounds();
     method @NonNull public android.service.notification.ZenPolicy.Builder allowCalls(int);
+    method @NonNull public android.service.notification.ZenPolicy.Builder allowConversations(int);
     method @NonNull public android.service.notification.ZenPolicy.Builder allowEvents(boolean);
     method @NonNull public android.service.notification.ZenPolicy.Builder allowMedia(boolean);
     method @NonNull public android.service.notification.ZenPolicy.Builder allowMessages(int);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index b4259474..90531b1 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,151 +1,4 @@
 // Signature format: 2.0
-package android.app.timedetector {
-
-  public final class PhoneTimeSuggestion implements android.os.Parcelable {
-    method public void addDebugInfo(@NonNull String);
-    method public void addDebugInfo(@NonNull java.util.List<java.lang.String>);
-    method public int describeContents();
-    method @NonNull public java.util.List<java.lang.String> getDebugInfo();
-    method public int getSlotIndex();
-    method @Nullable public android.os.TimestampedValue<java.lang.Long> getUtcTime();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.timedetector.PhoneTimeSuggestion> CREATOR;
-  }
-
-  public static final class PhoneTimeSuggestion.Builder {
-    ctor public PhoneTimeSuggestion.Builder(int);
-    method @NonNull public android.app.timedetector.PhoneTimeSuggestion.Builder addDebugInfo(@NonNull String);
-    method @NonNull public android.app.timedetector.PhoneTimeSuggestion build();
-    method @NonNull public android.app.timedetector.PhoneTimeSuggestion.Builder setUtcTime(@Nullable android.os.TimestampedValue<java.lang.Long>);
-  }
-
-  public interface TimeDetector {
-    method @RequiresPermission("android.permission.SUGGEST_PHONE_TIME_AND_ZONE") public void suggestPhoneTime(@NonNull android.app.timedetector.PhoneTimeSuggestion);
-  }
-
-}
-
-package android.app.timezonedetector {
-
-  public final class PhoneTimeZoneSuggestion implements android.os.Parcelable {
-    method public void addDebugInfo(@NonNull String);
-    method public void addDebugInfo(@NonNull java.util.List<java.lang.String>);
-    method @NonNull public static android.app.timezonedetector.PhoneTimeZoneSuggestion createEmptySuggestion(int, @NonNull String);
-    method public int describeContents();
-    method @NonNull public java.util.List<java.lang.String> getDebugInfo();
-    method public int getMatchType();
-    method public int getQuality();
-    method public int getSlotIndex();
-    method @Nullable public String getZoneId();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.timezonedetector.PhoneTimeZoneSuggestion> CREATOR;
-    field public static final int MATCH_TYPE_EMULATOR_ZONE_ID = 4; // 0x4
-    field public static final int MATCH_TYPE_NA = 0; // 0x0
-    field public static final int MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET = 3; // 0x3
-    field public static final int MATCH_TYPE_NETWORK_COUNTRY_ONLY = 2; // 0x2
-    field public static final int MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY = 5; // 0x5
-    field public static final int QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS = 3; // 0x3
-    field public static final int QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET = 2; // 0x2
-    field public static final int QUALITY_NA = 0; // 0x0
-    field public static final int QUALITY_SINGLE_ZONE = 1; // 0x1
-  }
-
-  public static final class PhoneTimeZoneSuggestion.Builder {
-    ctor public PhoneTimeZoneSuggestion.Builder(int);
-    method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder addDebugInfo(@NonNull String);
-    method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion build();
-    method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setMatchType(int);
-    method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setQuality(int);
-    method @NonNull public android.app.timezonedetector.PhoneTimeZoneSuggestion.Builder setZoneId(@Nullable String);
-  }
-
-  public interface TimeZoneDetector {
-    method @RequiresPermission("android.permission.SUGGEST_PHONE_TIME_AND_ZONE") public void suggestPhoneTimeZone(@NonNull android.app.timezonedetector.PhoneTimeZoneSuggestion);
-  }
-
-}
-
-package android.os {
-
-  public final class TimestampedValue<T> implements android.os.Parcelable {
-    ctor public TimestampedValue(long, @Nullable T);
-    method public int describeContents();
-    method public long getReferenceTimeMillis();
-    method @Nullable public T getValue();
-    method public static long referenceTimeDifference(@NonNull android.os.TimestampedValue<?>, @NonNull android.os.TimestampedValue<?>);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.os.TimestampedValue<?>> CREATOR;
-  }
-
-}
-
-package android.timezone {
-
-  public final class CountryTimeZones {
-    method @Nullable public android.icu.util.TimeZone getDefaultTimeZone();
-    method @Nullable public String getDefaultTimeZoneId();
-    method @NonNull public java.util.List<android.timezone.CountryTimeZones.TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long);
-    method public boolean hasUtcZone(long);
-    method public boolean isDefaultTimeZoneBoosted();
-    method @Nullable public android.timezone.CountryTimeZones.OffsetResult lookupByOffsetWithBias(int, @Nullable Boolean, @Nullable Integer, long, @Nullable android.icu.util.TimeZone);
-    method public boolean matchesCountryCode(@NonNull String);
-  }
-
-  public static final class CountryTimeZones.OffsetResult {
-    ctor public CountryTimeZones.OffsetResult(@NonNull android.icu.util.TimeZone, boolean);
-    method @NonNull public android.icu.util.TimeZone getTimeZone();
-    method public boolean isOnlyMatch();
-  }
-
-  public static final class CountryTimeZones.TimeZoneMapping {
-    method @NonNull public android.icu.util.TimeZone getTimeZone();
-    method @NonNull public String getTimeZoneId();
-  }
-
-  public final class TelephonyLookup {
-    method @NonNull public static android.timezone.TelephonyLookup getInstance();
-    method @Nullable public android.timezone.TelephonyNetworkFinder getTelephonyNetworkFinder();
-  }
-
-  public final class TelephonyNetwork {
-    method @NonNull public String getCountryIsoCode();
-    method @NonNull public String getMcc();
-    method @NonNull public String getMnc();
-  }
-
-  public final class TelephonyNetworkFinder {
-    method @Nullable public android.timezone.TelephonyNetwork findNetworkByMccMnc(@NonNull String, @NonNull String);
-  }
-
-  public final class TimeZoneFinder {
-    method @Nullable public String getIanaVersion();
-    method @NonNull public static android.timezone.TimeZoneFinder getInstance();
-    method @Nullable public android.timezone.CountryTimeZones lookupCountryTimeZones(@NonNull String);
-  }
-
-  public final class TzDataSetVersion {
-    method public static int currentFormatMajorVersion();
-    method public static int currentFormatMinorVersion();
-    method public int getFormatMajorVersion();
-    method public int getFormatMinorVersion();
-    method public int getRevision();
-    method @NonNull public String getRulesVersion();
-    method public static boolean isCompatibleWithThisDevice(android.timezone.TzDataSetVersion);
-    method @NonNull public static android.timezone.TzDataSetVersion read() throws java.io.IOException, android.timezone.TzDataSetVersion.TzDataSetException;
-  }
-
-  public static final class TzDataSetVersion.TzDataSetException extends java.lang.Exception {
-    ctor public TzDataSetVersion.TzDataSetException(String);
-    ctor public TzDataSetVersion.TzDataSetException(String, Throwable);
-  }
-
-  public final class ZoneInfoDb {
-    method @NonNull public static android.timezone.ZoneInfoDb getInstance();
-    method @NonNull public String getVersion();
-  }
-
-}
-
 package android.util {
 
   public final class Log {
diff --git a/api/system-current.txt b/api/system-current.txt
index dc126d8..b229506 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -218,7 +218,6 @@
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
     field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
-    field public static final String SUGGEST_PHONE_TIME_AND_ZONE = "android.permission.SUGGEST_PHONE_TIME_AND_ZONE";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
     field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
@@ -4318,7 +4317,7 @@
   }
 
   public final class AudioRecordingConfiguration implements android.os.Parcelable {
-    method public int getClientUid();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getClientUid();
   }
 
   public class HwAudioSource {
@@ -7518,9 +7517,6 @@
     method @Deprecated public boolean isNoInternetAccessExpected();
     method @Deprecated public void setIpConfiguration(@Nullable android.net.IpConfiguration);
     method @Deprecated public void setNetworkSelectionStatus(@NonNull android.net.wifi.WifiConfiguration.NetworkSelectionStatus);
-    field @Deprecated public static final int AP_BAND_2GHZ = 0; // 0x0
-    field @Deprecated public static final int AP_BAND_5GHZ = 1; // 0x1
-    field @Deprecated public static final int AP_BAND_ANY = -1; // 0xffffffff
     field @Deprecated public static final int INVALID_NETWORK_ID = -1; // 0xffffffff
     field @Deprecated public static final int METERED_OVERRIDE_METERED = 1; // 0x1
     field @Deprecated public static final int METERED_OVERRIDE_NONE = 0; // 0x0
@@ -7530,7 +7526,6 @@
     field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
     field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
     field @Deprecated public boolean allowAutojoin;
-    field @Deprecated public int apBand;
     field @Deprecated public int carrierId;
     field @Deprecated public String creatorName;
     field @Deprecated public int creatorUid;
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 2f1889c..a57e178 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -200,8 +200,6 @@
     
 MutableBareField: android.net.wifi.WifiConfiguration#allowAutojoin:
     
-MutableBareField: android.net.wifi.WifiConfiguration#apBand:
-    
 MutableBareField: android.net.wifi.WifiConfiguration#carrierId:
     
 MutableBareField: android.net.wifi.WifiConfiguration#fromWifiNetworkSpecifier:
diff --git a/api/test-current.txt b/api/test-current.txt
index 9472ce2..d3e7ea1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -416,6 +416,7 @@
     method public void setFgServiceShown(boolean);
     method public void setImportanceLockedByCriticalDeviceFunction(boolean);
     method public void setImportanceLockedByOEM(boolean);
+    method public void setImportantConversation(boolean);
     method public void setOriginalImportance(int);
   }
 
@@ -885,6 +886,7 @@
     method public abstract boolean arePermissionsIndividuallyControlled();
     method @Nullable public String getContentCaptureServicePackageName();
     method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract String getDefaultBrowserPackageNameAsUser(int);
+    method @Nullable public String getDefaultTextClassifierPackageName();
     method @Nullable public String getIncidentReportApproverPackageName();
     method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
     method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
@@ -894,6 +896,7 @@
     method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
     method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
+    method @Nullable public String getSystemTextClassifierPackageName();
     method @Nullable public String[] getTelephonyPackageNames();
     method @Nullable public String getWellbeingPackageName();
     method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 286fc69..e6cc1da 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -111,7 +111,7 @@
     ],
 
     cflags: [
-        "-DNEW_ENCODING_SCHEME",
+        // "-DNEW_ENCODING_SCHEME",
     ],
 
     local_include_dirs: [
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index 30dfe32..8b68743 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -23,14 +23,14 @@
 namespace statsd {
 
 static size_t createAndParseStatsEvent(uint8_t* msg) {
-    struct stats_event* event = stats_event_obtain();
-    stats_event_set_atom_id(event, 100);
-    stats_event_write_int32(event, 2);
-    stats_event_write_float(event, 2.0);
-    stats_event_build(event);
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, 100);
+    AStatsEvent_writeInt32(event, 2);
+    AStatsEvent_writeFloat(event, 2.0);
+    AStatsEvent_build(event);
 
     size_t size;
-    uint8_t* buf = stats_event_get_buffer(event, &size);
+    uint8_t* buf = AStatsEvent_getBuffer(event, &size);
     memcpy(msg, buf, size);
     return size;
 }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3be1ef8..183d741 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -202,7 +202,8 @@
         DocsUIStartupMsReported docs_ui_startup_ms = 111 [(module) = "docsui"];
         DocsUIUserActionReported docs_ui_user_action_reported = 112 [(module) = "docsui"];
         WifiEnabledStateChanged wifi_enabled_state_changed = 113 [(module) = "framework"];
-        WifiRunningStateChanged wifi_running_state_changed = 114 [(module) = "framework"];
+        WifiRunningStateChanged wifi_running_state_changed = 114
+                [(module) = "framework", deprecated = true];
         AppCompacted app_compacted = 115 [(module) = "framework"];
         NetworkDnsEventReported network_dns_event_reported = 116 [(module) = "resolv"];
         DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported =
@@ -1258,6 +1259,8 @@
 }
 
 /**
+ * This atom is deprecated starting in R.
+ *
  * Logs when an app causes Wifi to run. In this context, 'to run' means to use Wifi Client Mode.
  * TODO: Include support for Hotspot, perhaps by using an extra field to denote 'mode'.
  * Note that Wifi Scanning is monitored separately in WifiScanStateChanged.
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 656743b..e8fc603 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -35,6 +35,34 @@
 using std::string;
 using std::vector;
 
+// stats_event.h socket types. Keep in sync.
+/* ERRORS */
+#define ERROR_NO_TIMESTAMP 0x1
+#define ERROR_NO_ATOM_ID 0x2
+#define ERROR_OVERFLOW 0x4
+#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
+#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
+#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
+#define ERROR_INVALID_ANNOTATION_ID 0x40
+#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
+#define ERROR_TOO_MANY_ANNOTATIONS 0x100
+#define ERROR_TOO_MANY_FIELDS 0x200
+#define ERROR_INVALID_VALUE_TYPE 0x400
+#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
+
+/* TYPE IDS */
+#define INT32_TYPE 0x00
+#define INT64_TYPE 0x01
+#define STRING_TYPE 0x02
+#define LIST_TYPE 0x03
+#define FLOAT_TYPE 0x04
+#define BOOL_TYPE 0x05
+#define BYTE_ARRAY_TYPE 0x06
+#define OBJECT_TYPE 0x07
+#define KEY_VALUE_PAIRS_TYPE 0x08
+#define ATTRIBUTION_CHAIN_TYPE 0x09
+#define ERROR_TYPE 0x0F
+
 // Msg is expected to begin at the start of the serialized atom -- it should not
 // include the android_log_header_t or the StatsEventTag.
 LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid)
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 35b0396..f624e12 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -46,16 +46,16 @@
 }
 
 TEST(LogEventTest, TestPrimitiveParsing) {
-    struct stats_event* event = stats_event_obtain();
-    stats_event_set_atom_id(event, 100);
-    stats_event_write_int32(event, 10);
-    stats_event_write_int64(event, 0x123456789);
-    stats_event_write_float(event, 2.0);
-    stats_event_write_bool(event, true);
-    stats_event_build(event);
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, 100);
+    AStatsEvent_writeInt32(event, 10);
+    AStatsEvent_writeInt64(event, 0x123456789);
+    AStatsEvent_writeFloat(event, 2.0);
+    AStatsEvent_writeBool(event, true);
+    AStatsEvent_build(event);
 
     size_t size;
-    uint8_t* buf = stats_event_get_buffer(event, &size);
+    uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
     LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
@@ -90,20 +90,20 @@
     EXPECT_EQ(Type::INT, boolItem.mValue.getType()); // FieldValue does not support boolean type
     EXPECT_EQ(1, boolItem.mValue.int_value);
 
-    stats_event_release(event);
+    AStatsEvent_release(event);
 }
 
 
 TEST(LogEventTest, TestStringAndByteArrayParsing) {
-    struct stats_event* event = stats_event_obtain();
-    stats_event_set_atom_id(event, 100);
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, 100);
     string str = "test";
-    stats_event_write_string8(event, str.c_str());
-    stats_event_write_byte_array(event, (uint8_t*)str.c_str(), str.length());
-    stats_event_build(event);
+    AStatsEvent_writeString(event, str.c_str());
+    AStatsEvent_writeByteArray(event, (uint8_t*)str.c_str(), str.length());
+    AStatsEvent_build(event);
 
     size_t size;
-    uint8_t* buf = stats_event_get_buffer(event, &size);
+    uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
     LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
@@ -127,18 +127,18 @@
     vector<uint8_t> expectedValue = {'t', 'e', 's', 't'};
     EXPECT_EQ(expectedValue, storageItem.mValue.storage_value);
 
-    stats_event_release(event);
+    AStatsEvent_release(event);
 }
 
 TEST(LogEventTest, TestEmptyString) {
-    struct stats_event* event = stats_event_obtain();
-    stats_event_set_atom_id(event, 100);
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, 100);
     string empty = "";
-    stats_event_write_string8(event, empty.c_str());
-    stats_event_build(event);
+    AStatsEvent_writeString(event, empty.c_str());
+    AStatsEvent_build(event);
 
     size_t size;
-    uint8_t* buf = stats_event_get_buffer(event, &size);
+    uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
     LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
@@ -155,18 +155,18 @@
     EXPECT_EQ(Type::STRING, item.mValue.getType());
     EXPECT_EQ(empty, item.mValue.str_value);
 
-    stats_event_release(event);
+    AStatsEvent_release(event);
 }
 
 TEST(LogEventTest, TestByteArrayWithNullCharacter) {
-    struct stats_event* event = stats_event_obtain();
-    stats_event_set_atom_id(event, 100);
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, 100);
     uint8_t message[] = {'\t', 'e', '\0', 's', 't'};
-    stats_event_write_byte_array(event, message, 5);
-    stats_event_build(event);
+    AStatsEvent_writeByteArray(event, message, 5);
+    AStatsEvent_build(event);
 
     size_t size;
-    uint8_t* buf = stats_event_get_buffer(event, &size);
+    uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
     LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
@@ -184,79 +184,12 @@
     vector<uint8_t> expectedValue(message, message + 5);
     EXPECT_EQ(expectedValue, item.mValue.storage_value);
 
-    stats_event_release(event);
-}
-
-TEST(LogEventTest, TestKeyValuePairs) {
-    struct stats_event* event = stats_event_obtain();
-    stats_event_set_atom_id(event, 100);
-
-    struct key_value_pair pairs[4];
-    pairs[0] = {.key = 0, .valueType = INT32_TYPE, .int32Value = 1};
-    pairs[1] = {.key = 1, .valueType = INT64_TYPE, .int64Value = 0x123456789};
-    pairs[2] = {.key = 2, .valueType = FLOAT_TYPE, .floatValue = 2.0};
-    string str = "test";
-    pairs[3] = {.key = 3, .valueType = STRING_TYPE, .stringValue = str.c_str()};
-
-    stats_event_write_key_value_pairs(event, pairs, 4);
-    stats_event_build(event);
-
-    size_t size;
-    uint8_t* buf = stats_event_get_buffer(event, &size);
-
-    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
-    EXPECT_TRUE(logEvent.isValid());
-    EXPECT_EQ(100, logEvent.GetTagId());
-    EXPECT_EQ(1000, logEvent.GetUid());
-    EXPECT_EQ(1001, logEvent.GetPid());
-
-    const vector<FieldValue>& values = logEvent.getValues();
-    EXPECT_EQ(8, values.size()); // 2 FieldValues per key-value pair
-
-    // Check the keys first
-    for (int i = 0; i < values.size() / 2; i++) {
-        const FieldValue& item = values[2 * i];
-        int32_t depth1Pos = i + 1;
-        bool depth1Last = i == (values.size() / 2 - 1);
-        Field expectedField = getField(100, {1, depth1Pos, 1}, 2, {true, depth1Last, false});
-
-        EXPECT_EQ(expectedField, item.mField);
-        EXPECT_EQ(Type::INT, item.mValue.getType());
-        EXPECT_EQ(i, item.mValue.int_value);
-    }
-
-    // Check the values now
-    // Note: pos[2] = index of type in KeyValuePair in atoms.proto
-    const FieldValue& int32Item = values[1];
-    Field expectedField = getField(100, {1, 1, 2}, 2, {true, false, true});
-    EXPECT_EQ(expectedField, int32Item.mField);
-    EXPECT_EQ(Type::INT, int32Item.mValue.getType());
-    EXPECT_EQ(1, int32Item.mValue.int_value);
-
-    const FieldValue& int64Item = values[3];
-    expectedField = getField(100, {1, 2, 3}, 2, {true, false, true});
-    EXPECT_EQ(expectedField, int64Item.mField);
-    EXPECT_EQ(Type::LONG, int64Item.mValue.getType());
-    EXPECT_EQ(0x123456789, int64Item.mValue.long_value);
-
-    const FieldValue& floatItem = values[5];
-    expectedField = getField(100, {1, 3, 5}, 2, {true, false, true});
-    EXPECT_EQ(expectedField, floatItem.mField);
-    EXPECT_EQ(Type::FLOAT, floatItem.mValue.getType());
-    EXPECT_EQ(2.0, floatItem.mValue.float_value);
-
-    const FieldValue& stringItem = values[7];
-    expectedField = getField(100, {1, 4, 4}, 2, {true, true, true});
-    EXPECT_EQ(expectedField, stringItem.mField);
-    EXPECT_EQ(Type::STRING, stringItem.mValue.getType());
-    EXPECT_EQ(str, stringItem.mValue.str_value);
-
-    stats_event_release(event);
+    AStatsEvent_release(event);
 }
 
 TEST(LogEventTest, TestAttributionChain) {
-    struct stats_event* event = stats_event_obtain();
-    stats_event_set_atom_id(event, 100);
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, 100);
 
     string tag1 = "tag1";
     string tag2 = "tag2";
@@ -264,11 +197,11 @@
     uint32_t uids[] = {1001, 1002};
     const char* tags[] = {tag1.c_str(), tag2.c_str()};
 
-    stats_event_write_attribution_chain(event, uids, tags, 2);
-    stats_event_build(event);
+    AStatsEvent_writeAttributionChain(event, uids, tags, 2);
+    AStatsEvent_build(event);
 
     size_t size;
-    uint8_t* buf = stats_event_get_buffer(event, &size);
+    uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
     LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
@@ -305,7 +238,7 @@
     EXPECT_EQ(Type::STRING, tag2Item.mValue.getType());
     EXPECT_EQ(tag2, tag2Item.mValue.str_value);
 
-    stats_event_release(event);
+    AStatsEvent_release(event);
 }
 
 #else // NEW_ENCODING_SCHEME
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index 2576cf5..a011692e 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -50,11 +50,11 @@
 int64_t pullCoolDownNs;
 std::thread pullThread;
 
-stats_event* createSimpleEvent(int64_t value) {
-    stats_event* event = stats_event_obtain();
-    stats_event_set_atom_id(event, pullTagId);
-    stats_event_write_int64(event, value);
-    stats_event_build(event);
+AStatsEvent* createSimpleEvent(int64_t value) {
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, pullTagId);
+    AStatsEvent_writeInt64(event, value);
+    AStatsEvent_build(event);
     return event;
 }
 
@@ -62,16 +62,16 @@
     // Convert stats_events into StatsEventParcels.
     std::vector<android::util::StatsEventParcel> parcels;
     for (int i = 0; i < values.size(); i++) {
-        stats_event* event = createSimpleEvent(values[i]);
+        AStatsEvent* event = createSimpleEvent(values[i]);
         size_t size;
-        uint8_t* buffer = stats_event_get_buffer(event, &size);
+        uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
 
         android::util::StatsEventParcel p;
         // vector.assign() creates a copy, but this is inevitable unless
         // stats_event.h/c uses a vector as opposed to a buffer.
         p.buffer.assign(buffer, buffer + size);
         parcels.push_back(std::move(p));
-        stats_event_release(event);
+        AStatsEvent_release(event);
     }
 
     sleep_for(std::chrono::nanoseconds(pullDelayNs));
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 6e1890a..db09ee9 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -953,24 +953,24 @@
     // Convert stats_events into StatsEventParcels.
     std::vector<android::util::StatsEventParcel> parcels;
     for (int i = 1; i < 3; i++) {
-        stats_event* event = stats_event_obtain();
-        stats_event_set_atom_id(event, atomTag);
+        AStatsEvent* event = AStatsEvent_obtain();
+        AStatsEvent_setAtomId(event, atomTag);
         std::string subsystemName = "subsystem_name_";
         subsystemName = subsystemName + std::to_string(i);
-        stats_event_write_string8(event, subsystemName.c_str());
-        stats_event_write_string8(event, "subsystem_subname foo");
-        stats_event_write_int64(event, /*count= */ i);
-        stats_event_write_int64(event, /*time_millis= */ i * 100);
-        stats_event_build(event);
+        AStatsEvent_writeString(event, subsystemName.c_str());
+        AStatsEvent_writeString(event, "subsystem_subname foo");
+        AStatsEvent_writeInt64(event, /*count= */ i);
+        AStatsEvent_writeInt64(event, /*time_millis= */ i * 100);
+        AStatsEvent_build(event);
         size_t size;
-        uint8_t* buffer = stats_event_get_buffer(event, &size);
+        uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
 
         android::util::StatsEventParcel p;
         // vector.assign() creates a copy, but this is inevitable unless
         // stats_event.h/c uses a vector as opposed to a buffer.
         p.buffer.assign(buffer, buffer + size);
         parcels.push_back(std::move(p));
-        stats_event_release(event);
+        AStatsEvent_write(event);
     }
     resultReceiver->pullFinished(atomTag, /*success=*/true, parcels);
     return binder::Status::ok();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 46f8669..861d41d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -88,10 +88,15 @@
 import java.util.function.Supplier;
 
 /**
- * API for interacting with "application operation" tracking.
+ * AppOps are mappings of [package/uid, op-name] -> [mode]. The list of existing appops is defined
+ * by the system and cannot be amended by apps. Only system apps can change appop-modes.
  *
- * <p>This API is not generally intended for third party application developers; most
- * features are only available to system applications.
+ * <p>Beside a mode the system tracks when an op was {@link #noteOp noted}. The tracked data can
+ * only be read by system components.
+ *
+ * <p>Installed apps can usually only listen to changes and events on their own ops. E.g.
+ * {@link AppOpsCollector} allows to get a callback each time an app called {@link #noteOp} or
+ * {@link #startOp} for an op belonging to the app.
  */
 @SystemService(Context.APP_OPS_SERVICE)
 public class AppOpsManager {
@@ -6774,6 +6779,9 @@
      * succeeds, the last execution time of the operation for this app will be updated to
      * the current time.
      *
+     * <p>If this is a check that is not preceding the protected operation, use
+     * {@link #unsafeCheckOp} instead.
+     *
      * @param op The operation to note.  One of the OPSTR_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
@@ -6798,6 +6806,9 @@
      * succeeds, the last execution time of the operation for this app will be updated to
      * the current time.
      *
+     * <p>If this is a check that is not preceding the protected operation, use
+     * {@link #unsafeCheckOp} instead.
+     *
      * @param op The operation to note.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
@@ -7757,9 +7768,38 @@
 
     /**
      * Callback an app can choose to {@link #setNotedAppOpsCollector register} to monitor it's noted
-     * appops.
+     * appops. I.e. each time any app calls {@link #noteOp} or {@link #startOp} one of the callback
+     * methods of this object is called.
      *
      * <p><b>Only appops related to dangerous permissions are collected.</b>
+     *
+     * <pre>
+     * setNotedAppOpsCollector(new AppOpsCollector() {
+     *     ArraySet<Pair<String, String>> opsNotedForThisProcess = new ArraySet<>();
+     *
+     *     private synchronized void addAccess(String op, String accessLocation) {
+     *         // Ops are often noted when permission protected APIs were called.
+     *         // In this case permissionToOp() allows to resolve the permission<->op
+     *         opsNotedForThisProcess.add(new Pair(accessType, accessLocation));
+     *     }
+     *
+     *     public void onNoted(SyncNotedAppOp op) {
+     *         // Accesses is currently happening, hence stack trace describes location of access
+     *         addAccess(op.getOp(), Arrays.toString(Thread.currentThread().getStackTrace()));
+     *     }
+     *
+     *     public void onSelfNoted(SyncNotedAppOp op) {
+     *         onNoted(op);
+     *     }
+     *
+     *     public void onAsyncNoted(AsyncNotedAppOp asyncOp) {
+     *         // Stack trace is not useful for async ops as accessed happened on different thread
+     *         addAccess(asyncOp.getOp(), asyncOp.getMessage());
+     *     }
+     * });
+     * </pre>
+     *
+     * @see #setNotedAppOpsCollector
      */
     public abstract static class AppOpsCollector {
         /** Callback registered with the system. This will receive the async notes ops */
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 71cb4a4..cd05e2c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3229,18 +3229,18 @@
     }
 
     @Override
-    public String getSystemTextClassifierPackageName() {
+    public String getDefaultTextClassifierPackageName() {
         try {
-            return mPM.getSystemTextClassifierPackageName();
+            return mPM.getDefaultTextClassifierPackageName();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     @Override
-    public String[] getSystemTextClassifierPackages() {
+    public String getSystemTextClassifierPackageName() {
         try {
-            return mPM.getSystemTextClassifierPackages();
+            return mPM.getSystemTextClassifierPackageName();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 80ba464..8c3180b 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -39,6 +39,7 @@
     boolean injectInputEvent(in InputEvent event, boolean sync);
     void syncInputTransactions();
     boolean setRotation(int rotation);
+    Bitmap takeScreenshot(in Rect crop, int rotation);
     boolean clearWindowContentFrameStats(int windowId);
     WindowContentFrameStats getWindowContentFrameStats(int windowId);
     void clearWindowAnimationFrameStats();
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 4b24e09..7212be8 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -31,7 +31,6 @@
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.text.TextUtils;
-import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.Preconditions;
@@ -105,6 +104,7 @@
     private static final String ATT_ORIG_IMP = "orig_imp";
     private static final String ATT_PARENT_CHANNEL = "parent";
     private static final String ATT_CONVERSATION_ID = "conv_id";
+    private static final String ATT_IMP_CONVERSATION = "imp_conv";
     private static final String ATT_DEMOTE = "dem";
     private static final String DELIMITER = ",";
 
@@ -196,6 +196,7 @@
     private String mParentId = null;
     private String mConversationId = null;
     private boolean mDemoted = false;
+    private boolean mImportantConvo = false;
 
     /**
      * Creates a notification channel.
@@ -263,6 +264,7 @@
         mParentId = in.readString();
         mConversationId = in.readString();
         mDemoted = in.readBoolean();
+        mImportantConvo = in.readBoolean();
     }
 
     @Override
@@ -321,6 +323,7 @@
         dest.writeString(mParentId);
         dest.writeString(mConversationId);
         dest.writeBoolean(mDemoted);
+        dest.writeBoolean(mImportantConvo);
     }
 
     /**
@@ -354,6 +357,14 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public void setImportantConversation(boolean importantConvo) {
+        mImportantConvo = importantConvo;
+    }
+
+    /**
      * Allows users to block notifications sent through this channel, if this channel belongs to
      * a package that is signed with the system signature. If the channel does not belong to a
      * package that is signed with the system signature, this method does nothing.
@@ -601,6 +612,18 @@
     }
 
     /**
+     * Whether or not notifications in this conversation are considered important.
+     *
+     * <p>Important conversations may get special visual treatment, and might be able to bypass DND.
+     *
+     * <p>This is only valid for channels that represent conversations, that is, those with a valid
+     * {@link #getConversationId() conversation id}.
+     */
+    public boolean isImportantConversation() {
+        return mImportantConvo;
+    }
+
+    /**
      * Returns the notification sound for this channel.
      */
     public Uri getSound() {
@@ -852,6 +875,7 @@
         setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL),
                 parser.getAttributeValue(null, ATT_CONVERSATION_ID));
         setDemoted(safeBool(parser, ATT_DEMOTE, false));
+        setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false));
     }
 
     @Nullable
@@ -985,6 +1009,9 @@
         if (isDemoted()) {
             out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted()));
         }
+        if (isImportantConversation()) {
+            out.attribute(null, ATT_IMP_CONVERSATION, Boolean.toString(isImportantConversation()));
+        }
 
         // mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
         // truth and so aren't written to this xml file
@@ -1145,7 +1172,8 @@
                 && mOriginalImportance == that.mOriginalImportance
                 && Objects.equals(getParentChannelId(), that.getParentChannelId())
                 && Objects.equals(getConversationId(), that.getConversationId())
-                && isDemoted() == that.isDemoted();
+                && isDemoted() == that.isDemoted()
+                && isImportantConversation() == that.isImportantConversation();
     }
 
     @Override
@@ -1156,7 +1184,7 @@
                 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
                 getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
                 mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
-                mParentId, mConversationId, mDemoted);
+                mParentId, mConversationId, mDemoted, mImportantConvo);
         result = 31 * result + Arrays.hashCode(mVibration);
         return result;
     }
@@ -1204,7 +1232,8 @@
                 + ", mOriginalImp=" + mOriginalImportance
                 + ", mParent=" + mParentId
                 + ", mConversationId=" + mConversationId
-                + ", mDemoted=" + mDemoted;
+                + ", mDemoted=" + mDemoted
+                + ", mImportantConvo=" + mImportantConvo;
     }
 
     /** @hide */
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 1a8e15c..528b508 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -46,6 +46,7 @@
 import android.service.notification.Condition;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenPolicy;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
@@ -1555,19 +1556,24 @@
         public static final int PRIORITY_CATEGORY_MEDIA = 1 << 6;
         /**System (catch-all for non-never suppressible sounds) are prioritized */
         public static final int PRIORITY_CATEGORY_SYSTEM = 1 << 7;
+        /**
+         * Conversations are allowed through DND.
+         */
+        public static final int PRIORITY_CATEGORY_CONVERSATIONS = 1 << 8;
 
         /**
          * @hide
          */
         public static final int[] ALL_PRIORITY_CATEGORIES = {
-            PRIORITY_CATEGORY_ALARMS,
-            PRIORITY_CATEGORY_MEDIA,
-            PRIORITY_CATEGORY_SYSTEM,
-            PRIORITY_CATEGORY_REMINDERS,
-            PRIORITY_CATEGORY_EVENTS,
-            PRIORITY_CATEGORY_MESSAGES,
-            PRIORITY_CATEGORY_CALLS,
-            PRIORITY_CATEGORY_REPEAT_CALLERS,
+                PRIORITY_CATEGORY_ALARMS,
+                PRIORITY_CATEGORY_MEDIA,
+                PRIORITY_CATEGORY_SYSTEM,
+                PRIORITY_CATEGORY_REMINDERS,
+                PRIORITY_CATEGORY_EVENTS,
+                PRIORITY_CATEGORY_MESSAGES,
+                PRIORITY_CATEGORY_CALLS,
+                PRIORITY_CATEGORY_REPEAT_CALLERS,
+                PRIORITY_CATEGORY_CONVERSATIONS,
         };
 
         /** Any sender is prioritized. */
@@ -1577,6 +1583,31 @@
         /** Only starred contacts are prioritized. */
         public static final int PRIORITY_SENDERS_STARRED = 2;
 
+
+        /** @hide */
+        @IntDef(prefix = { "CONVERSATION_SENDERS_" }, value = {
+                CONVERSATION_SENDERS_ANYONE,
+                CONVERSATION_SENDERS_IMPORTANT,
+                CONVERSATION_SENDERS_NONE,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface ConversationSenders {}
+        /**
+         * Used to indicate all conversations can bypass dnd.
+         */
+        public static final int CONVERSATION_SENDERS_ANYONE = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
+
+        /**
+         * Used to indicate important conversations can bypass dnd.
+         */
+        public static final int CONVERSATION_SENDERS_IMPORTANT =
+                ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+
+        /**
+         * Used to indicate no conversations can bypass dnd.
+         */
+        public static final int CONVERSATION_SENDERS_NONE = ZenPolicy.CONVERSATION_SENDERS_NONE;
+
         /** Notification categories to prioritize. Bitmask of PRIORITY_CATEGORY_* constants. */
         public final int priorityCategories;
 
@@ -1589,6 +1620,18 @@
         public final int priorityMessageSenders;
 
         /**
+         * Notification senders to prioritize for conversations. One of:
+         * {@link #CONVERSATION_SENDERS_NONE}, {@link #CONVERSATION_SENDERS_IMPORTANT},
+         * {@link #CONVERSATION_SENDERS_ANYONE}.
+         */
+        public final int priorityConversationSenders;
+
+        /**
+         * @hide
+         */
+        public static final int CONVERSATION_SENDERS_UNSET = -1;
+
+        /**
          * @hide
          */
         public static final int SUPPRESSED_EFFECTS_UNSET = -1;
@@ -1665,21 +1708,6 @@
                 SUPPRESSED_EFFECT_NOTIFICATION_LIST
         };
 
-        private static final int[] SCREEN_OFF_SUPPRESSED_EFFECTS = {
-                SUPPRESSED_EFFECT_SCREEN_OFF,
-                SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
-                SUPPRESSED_EFFECT_LIGHTS,
-                SUPPRESSED_EFFECT_AMBIENT,
-        };
-
-        private static final int[] SCREEN_ON_SUPPRESSED_EFFECTS = {
-                SUPPRESSED_EFFECT_SCREEN_ON,
-                SUPPRESSED_EFFECT_PEEK,
-                SUPPRESSED_EFFECT_STATUS_BAR,
-                SUPPRESSED_EFFECT_BADGE,
-                SUPPRESSED_EFFECT_NOTIFICATION_LIST
-        };
-
         /**
          * Visual effects to suppress for a notification that is filtered by Do Not Disturb mode.
          * Bitmask of SUPPRESSED_EFFECT_* constants.
@@ -1718,17 +1746,16 @@
          */
         public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders) {
             this(priorityCategories, priorityCallSenders, priorityMessageSenders,
-                    SUPPRESSED_EFFECTS_UNSET, STATE_UNSET);
+                    SUPPRESSED_EFFECTS_UNSET, STATE_UNSET, CONVERSATION_SENDERS_UNSET);
         }
 
         /**
          * Constructs a policy for Do Not Disturb priority mode behavior.
          *
          * <p>
-         *     Apps that target API levels below {@link Build.VERSION_CODES#P} cannot
+         *     Apps that target API levels below {@link Build.VERSION_CODES#R} cannot
          *     change user-designated values to allow or disallow
-         *     {@link Policy#PRIORITY_CATEGORY_ALARMS}, {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and
-         *     {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd.
+         *     {@link Policy#PRIORITY_CATEGORY_CONVERSATIONS}, from bypassing dnd.
          * <p>
          *     Additionally, apps that target API levels below {@link Build.VERSION_CODES#P} can
          *     only modify the {@link #SUPPRESSED_EFFECT_SCREEN_ON} and
@@ -1752,27 +1779,68 @@
          */
         public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
                 int suppressedVisualEffects) {
-            this.priorityCategories = priorityCategories;
-            this.priorityCallSenders = priorityCallSenders;
-            this.priorityMessageSenders = priorityMessageSenders;
-            this.suppressedVisualEffects = suppressedVisualEffects;
-            this.state = STATE_UNSET;
+            this(priorityCategories, priorityCallSenders, priorityMessageSenders,
+                    suppressedVisualEffects, STATE_UNSET, CONVERSATION_SENDERS_UNSET);
+        }
+
+        /**
+         * Constructs a policy for Do Not Disturb priority mode behavior.
+         *
+         * <p>
+         *     Apps that target API levels below {@link Build.VERSION_CODES#P} cannot
+         *     change user-designated values to allow or disallow
+         *     {@link Policy#PRIORITY_CATEGORY_CONVERSATIONS} from bypassing dnd. If you do need
+         *     to change them, use a {@link ZenPolicy} associated with an {@link AutomaticZenRule}
+         *     instead of changing the global setting.
+         * <p>
+         *     Apps that target API levels below {@link Build.VERSION_CODES#P} cannot
+         *     change user-designated values to allow or disallow
+         *     {@link Policy#PRIORITY_CATEGORY_ALARMS},
+         *     {@link Policy#PRIORITY_CATEGORY_SYSTEM}, and
+         *     {@link Policy#PRIORITY_CATEGORY_MEDIA} from bypassing dnd.
+         * <p>
+         *     Additionally, apps that target API levels below {@link Build.VERSION_CODES#P} can
+         *     only modify the {@link #SUPPRESSED_EFFECT_SCREEN_ON} and
+         *     {@link #SUPPRESSED_EFFECT_SCREEN_OFF} bits of the suppressed visual effects field.
+         *     All other suppressed effects will be ignored and reconstituted from the screen on
+         *     and screen off values.
+         * <p>
+         *     Apps that target {@link Build.VERSION_CODES#P} or above can set any
+         *     suppressed visual effects. However, if any suppressed effects >
+         *     {@link #SUPPRESSED_EFFECT_SCREEN_ON} are set, {@link #SUPPRESSED_EFFECT_SCREEN_ON}
+         *     and {@link #SUPPRESSED_EFFECT_SCREEN_OFF} will be ignored and reconstituted from
+         *     the more specific suppressed visual effect bits. Apps should migrate to targeting
+         *     specific effects instead of the deprecated {@link #SUPPRESSED_EFFECT_SCREEN_ON} and
+         *     {@link #SUPPRESSED_EFFECT_SCREEN_OFF} effects.
+         *
+         * @param priorityCategories bitmask of categories of notifications that can bypass DND.
+         * @param priorityCallSenders which callers can bypass DND.
+         * @param priorityMessageSenders which message senders can bypass DND.
+         * @param suppressedVisualEffects which visual interruptions should be suppressed from
+         *                                notifications that are filtered by DND.
+         */
+        public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
+                int suppressedVisualEffects, int priorityConversationSenders) {
+            this(priorityCategories, priorityCallSenders, priorityMessageSenders,
+                    suppressedVisualEffects, STATE_UNSET, priorityConversationSenders);
         }
 
         /** @hide */
         public Policy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders,
-                int suppressedVisualEffects, int state) {
+                int suppressedVisualEffects, int state, int priorityConversationSenders) {
             this.priorityCategories = priorityCategories;
             this.priorityCallSenders = priorityCallSenders;
             this.priorityMessageSenders = priorityMessageSenders;
             this.suppressedVisualEffects = suppressedVisualEffects;
             this.state = state;
+            this.priorityConversationSenders = priorityConversationSenders;
         }
 
+
         /** @hide */
         public Policy(Parcel source) {
             this(source.readInt(), source.readInt(), source.readInt(), source.readInt(),
-                    source.readInt());
+                    source.readInt(), source.readInt());
         }
 
         @Override
@@ -1782,6 +1850,7 @@
             dest.writeInt(priorityMessageSenders);
             dest.writeInt(suppressedVisualEffects);
             dest.writeInt(state);
+            dest.writeInt(priorityConversationSenders);
         }
 
         @Override
@@ -1792,7 +1861,7 @@
         @Override
         public int hashCode() {
             return Objects.hash(priorityCategories, priorityCallSenders, priorityMessageSenders,
-                    suppressedVisualEffects, state);
+                    suppressedVisualEffects, state, priorityConversationSenders);
         }
 
         @Override
@@ -1805,7 +1874,8 @@
                     && other.priorityMessageSenders == priorityMessageSenders
                     && suppressedVisualEffectsEqual(suppressedVisualEffects,
                     other.suppressedVisualEffects)
-                    && other.state == this.state;
+                    && other.state == this.state
+                    && other.priorityConversationSenders == this.priorityConversationSenders;
         }
 
         private boolean suppressedVisualEffectsEqual(int suppressedEffects,
@@ -1867,6 +1937,8 @@
                     + "priorityCategories=" + priorityCategoriesToString(priorityCategories)
                     + ",priorityCallSenders=" + prioritySendersToString(priorityCallSenders)
                     + ",priorityMessageSenders=" + prioritySendersToString(priorityMessageSenders)
+                    + ",priorityConvSenders="
+                    + conversationSendersToString(priorityConversationSenders)
                     + ",suppressedVisualEffects="
                     + suppressedEffectsToString(suppressedVisualEffects)
                     + ",areChannelsBypassingDnd=" + (((state & STATE_CHANNELS_BYPASSING_DND) != 0)
@@ -2003,6 +2075,7 @@
                 case PRIORITY_CATEGORY_ALARMS: return "PRIORITY_CATEGORY_ALARMS";
                 case PRIORITY_CATEGORY_MEDIA: return "PRIORITY_CATEGORY_MEDIA";
                 case PRIORITY_CATEGORY_SYSTEM: return "PRIORITY_CATEGORY_SYSTEM";
+                case PRIORITY_CATEGORY_CONVERSATIONS: return "PRIORITY_CATEGORY_CONVERSATIONS";
                 default: return "PRIORITY_CATEGORY_UNKNOWN_" + priorityCategory;
             }
         }
@@ -2016,7 +2089,25 @@
             }
         }
 
-        public static final @android.annotation.NonNull Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() {
+        /**
+         * @hide
+         */
+        public static @NonNull String conversationSendersToString(int priorityConversationSenders) {
+            switch (priorityConversationSenders) {
+                case CONVERSATION_SENDERS_ANYONE:
+                    return "anyone";
+                case CONVERSATION_SENDERS_IMPORTANT:
+                    return "important";
+                case CONVERSATION_SENDERS_NONE:
+                    return "none";
+                case CONVERSATION_SENDERS_UNSET:
+                    return "unset";
+            }
+            return "invalidConversationType{" + priorityConversationSenders + "}";
+        }
+
+        public static final @android.annotation.NonNull Parcelable.Creator<Policy> CREATOR
+                = new Parcelable.Creator<Policy>() {
             @Override
             public Policy createFromParcel(Parcel in) {
                 return new Policy(in);
@@ -2054,6 +2145,11 @@
         }
 
         /** @hide **/
+        public boolean allowConversations() {
+            return (priorityCategories & PRIORITY_CATEGORY_CONVERSATIONS) != 0;
+        }
+
+        /** @hide **/
         public boolean allowMessages() {
             return (priorityCategories & PRIORITY_CATEGORY_MESSAGES) != 0;
         }
@@ -2079,6 +2175,11 @@
         }
 
         /** @hide **/
+        public int allowConversationsFrom() {
+            return priorityConversationSenders;
+        }
+
+        /** @hide **/
         public boolean showFullScreenIntents() {
             return (suppressedVisualEffects & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0;
         }
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 35cf687..a9a06da 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -27,7 +27,10 @@
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.display.DisplayManagerGlobal;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -828,20 +831,39 @@
     }
 
     /**
-     * Takes a screenshot of the default display and returns it by {@link Bitmap.Config#HARDWARE}
-     * format.
+     * Takes a screenshot.
      *
      * @return The screenshot bitmap on success, null otherwise.
      */
     public Bitmap takeScreenshot() {
-        final int connectionId;
         synchronized (mLock) {
             throwIfNotConnectedLocked();
-            connectionId = mConnectionId;
         }
-        // Calling out without a lock held.
-        return AccessibilityInteractionClient.getInstance()
-                .takeScreenshot(connectionId, Display.DEFAULT_DISPLAY);
+        Display display = DisplayManagerGlobal.getInstance()
+                .getRealDisplay(Display.DEFAULT_DISPLAY);
+        Point displaySize = new Point();
+        display.getRealSize(displaySize);
+
+        int rotation = display.getRotation();
+
+        // Take the screenshot
+        Bitmap screenShot = null;
+        try {
+            // Calling out without a lock held.
+            screenShot = mUiAutomationConnection.takeScreenshot(
+                    new Rect(0, 0, displaySize.x, displaySize.y), rotation);
+            if (screenShot == null) {
+                return null;
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while taking screnshot!", re);
+            return null;
+        }
+
+        // Optimization
+        screenShot.setHasAlpha(false);
+
+        return screenShot;
     }
 
     /**
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 4fb9743..82e9881 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -21,6 +21,8 @@
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.os.Binder;
 import android.os.IBinder;
@@ -178,6 +180,23 @@
     }
 
     @Override
+    public Bitmap takeScreenshot(Rect crop, int rotation) {
+        synchronized (mLock) {
+            throwIfCalledByNotTrustedUidLocked();
+            throwIfShutdownLocked();
+            throwIfNotConnectedLocked();
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            int width = crop.width();
+            int height = crop.height();
+            return SurfaceControl.screenshot(crop, width, height, rotation);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public boolean clearWindowContentFrameStats(int windowId) throws RemoteException {
         synchronized (mLock) {
             throwIfCalledByNotTrustedUidLocked();
@@ -430,8 +449,7 @@
         info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
                 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
                 | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
-                | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS
-                | AccessibilityServiceInfo.CAPABILITY_CAN_TAKE_SCREENSHOT);
+                | AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS);
         try {
             // Calling out with a lock held is fine since if the system
             // process is gone the client calling in will be killed.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3676a9b..7599791 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2090,6 +2090,13 @@
     public static final int LOCK_TASK_FEATURE_KEYGUARD = 1 << 5;
 
     /**
+     * Enable blocking of non-whitelisted activities from being started into a locked task.
+     *
+     * @see #setLockTaskFeatures(ComponentName, int)
+     */
+    public static final int LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK = 1 << 6;
+
+    /**
      * Flags supplied to {@link #setLockTaskFeatures(ComponentName, int)}.
      *
      * @hide
@@ -2102,7 +2109,8 @@
             LOCK_TASK_FEATURE_HOME,
             LOCK_TASK_FEATURE_OVERVIEW,
             LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
-            LOCK_TASK_FEATURE_KEYGUARD
+            LOCK_TASK_FEATURE_KEYGUARD,
+            LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK
     })
     public @interface LockTaskFeature {}
 
@@ -3722,6 +3730,11 @@
      * requires that you request both {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and
      * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}}.
      * <p>
+     * When this policy is set by a device owner, profile owner of an organization-owned device or
+     * an admin on the primary user, the device will be factory reset after too many incorrect
+     * password attempts. When set by a profile owner or an admin on a secondary user or a managed
+     * profile, only the corresponding user or profile will be wiped.
+     * <p>
      * To implement any other policy (e.g. wiping data for a particular application only, erasing or
      * revoking credentials, or reporting the failure to a server), you should implement
      * {@link DeviceAdminReceiver#onPasswordFailed(Context, android.content.Intent)} instead. Do not
@@ -3790,10 +3803,12 @@
     }
 
     /**
-     * Returns the profile with the smallest maximum failed passwords for wipe,
-     * for the given user. So for primary user, it might return the primary or
-     * a managed profile. For a secondary user, it would be the same as the
-     * user passed in.
+     * Returns the user that will be wiped first when too many failed attempts are made to unlock
+     * user {@code userHandle}. That user is either the same as {@code userHandle} or belongs to the
+     * same profile group. When there is no such policy, returns {@code UserHandle.USER_NULL}.
+     * E.g. managed profile user may be wiped as a result of failed primary profile password
+     * attempts when using unified challenge. Primary user may be wiped as a result of failed
+     * password attempts on the managed profile on an organization-owned device.
      * @hide Used only by Keyguard
      */
     @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index 16288e8..0133a44 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.TimestampedValue;
@@ -51,11 +50,9 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class PhoneTimeSuggestion implements Parcelable {
 
     /** @hide */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR =
             new Parcelable.Creator<PhoneTimeSuggestion>() {
                 public PhoneTimeSuggestion createFromParcel(Parcel in) {
@@ -188,7 +185,6 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final class Builder {
         private final int mSlotIndex;
         @Nullable private TimestampedValue<Long> mUtcTime;
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 2412fb3..df4f513 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.SystemClock;
@@ -29,7 +28,6 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 @SystemService(Context.TIME_DETECTOR_SERVICE)
 public interface TimeDetector {
 
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
index 0544ccd..9147b44 100644
--- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
+++ b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -57,11 +56,9 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class PhoneTimeZoneSuggestion implements Parcelable {
 
     /** @hide */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @NonNull
     public static final Creator<PhoneTimeZoneSuggestion> CREATOR =
             new Creator<PhoneTimeZoneSuggestion>() {
@@ -297,7 +294,6 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final class Builder {
         private final int mSlotIndex;
         @Nullable private String mZoneId;
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index b4f6087..6a3953e 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 
@@ -27,7 +26,6 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 @SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
 public interface TimeZoneDetector {
 
@@ -49,7 +47,6 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE)
     void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion);
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 93126b8..6552d1b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -679,9 +679,9 @@
 
     boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);
 
-    String getSystemTextClassifierPackageName();
+    String getDefaultTextClassifierPackageName();
 
-    String[] getSystemTextClassifierPackages();
+    String getSystemTextClassifierPackageName();
 
     String getAttentionServicePackageName();
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6d5e8fb..51d5c3f6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -7602,14 +7602,15 @@
     }
 
     /**
-     * @return the system defined text classifier package name, or null if there's none.
+     * @return the default text classifier package name, or null if there's none.
      *
      * @hide
      */
     @Nullable
-    public String getSystemTextClassifierPackageName() {
+    @TestApi
+    public String getDefaultTextClassifierPackageName() {
         throw new UnsupportedOperationException(
-                "getSystemTextClassifierPackageName not implemented in subclass");
+                "getDefaultTextClassifierPackageName not implemented in subclass");
     }
 
     /**
@@ -7617,10 +7618,11 @@
      *
      * @hide
      */
-    @NonNull
-    public String[] getSystemTextClassifierPackages() {
+    @Nullable
+    @TestApi
+    public String getSystemTextClassifierPackageName() {
         throw new UnsupportedOperationException(
-                "getSystemTextClassifierPackages not implemented in subclass");
+                "getSystemTextClassifierPackageName not implemented in subclass");
     }
 
     /**
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 140363c..b128ea7 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -676,7 +676,8 @@
         }
 
         try {
-            mService.registerConnectivityDiagnosticsCallback(binder, request);
+            mService.registerConnectivityDiagnosticsCallback(
+                    binder, request, mContext.getOpPackageName());
         } catch (RemoteException exception) {
             exception.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1089a19..0fae607 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -222,7 +222,7 @@
     boolean isCallerCurrentAlwaysOnVpnLockdownApp();
 
     void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback,
-            in NetworkRequest request);
+            in NetworkRequest request, String callingPackageName);
     void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback);
 
     IBinder startOrGetTestNetworkService();
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 4f4e27b..cf5f225 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -858,8 +858,8 @@
      *
      * <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
      *
-     * <p>An app is granted owner privileges over Networks that it supplies. Owner privileges
-     * implicitly include administrator privileges.
+     * <p>An app is granted owner privileges over Networks that it supplies. The owner UID MUST
+     * always be included in administratorUids.
      *
      * @param administratorUids the UIDs to be set as administrators of this Network.
      * @hide
diff --git a/core/java/android/os/TimestampedValue.java b/core/java/android/os/TimestampedValue.java
index f4c87ac..4c4335b 100644
--- a/core/java/android/os/TimestampedValue.java
+++ b/core/java/android/os/TimestampedValue.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 
 import java.util.Objects;
 
@@ -36,7 +35,6 @@
  * @param <T> the type of the value with an associated timestamp
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class TimestampedValue<T> implements Parcelable {
     private final long mReferenceTimeMillis;
     @Nullable
@@ -96,7 +94,6 @@
     }
 
     /** @hide */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR =
             new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() {
 
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 368c94c..79852d3 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -31,7 +31,6 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
@@ -93,7 +92,7 @@
     // Used for metrics / debug only
     private ComponentName mServiceComponentName;
 
-    private final IAugmentedAutofillService mInterface = new IAugmentedAutofillService.Stub() {
+    private final class AugmentedAutofillServiceImpl extends IAugmentedAutofillService.Stub {
 
         @Override
         public void onConnected(boolean debug, boolean verbose) {
@@ -137,7 +136,7 @@
     public final IBinder onBind(Intent intent) {
         mServiceComponentName = intent.getComponent();
         if (SERVICE_INTERFACE.equals(intent.getAction())) {
-            return mInterface.asBinder();
+            return new AugmentedAutofillServiceImpl();
         }
         Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
         return null;
@@ -352,11 +351,13 @@
         static final int REPORT_EVENT_NO_RESPONSE = 1;
         static final int REPORT_EVENT_UI_SHOWN = 2;
         static final int REPORT_EVENT_UI_DESTROYED = 3;
+        static final int REPORT_EVENT_INLINE_RESPONSE = 4;
 
         @IntDef(prefix = { "REPORT_EVENT_" }, value = {
                 REPORT_EVENT_NO_RESPONSE,
                 REPORT_EVENT_UI_SHOWN,
-                REPORT_EVENT_UI_DESTROYED
+                REPORT_EVENT_UI_DESTROYED,
+                REPORT_EVENT_INLINE_RESPONSE
         })
         @Retention(RetentionPolicy.SOURCE)
         @interface ReportEvent{}
@@ -365,8 +366,8 @@
         private final Object mLock = new Object();
         private final IAugmentedAutofillManagerClient mClient;
         private final int mSessionId;
-        public final int taskId;
-        public final ComponentName componentName;
+        public final int mTaskId;
+        public final ComponentName mComponentName;
         // Used for metrics / debug only
         private String mServicePackageName;
         @GuardedBy("mLock")
@@ -406,8 +407,8 @@
             mSessionId = sessionId;
             mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
             mCallback = callback;
-            this.taskId = taskId;
-            this.componentName = componentName;
+            mTaskId = taskId;
+            mComponentName = componentName;
             mServicePackageName = serviceComponentName.getPackageName();
             mFocusedId = focusedId;
             mFocusedValue = focusedValue;
@@ -514,22 +515,24 @@
             }
         }
 
-        public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData,
-                @Nullable Bundle clientState) {
+        void reportResult(@Nullable List<Dataset> inlineSuggestionsData) {
             try {
-                mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}), clientState);
+                final Dataset[] inlineSuggestions = (inlineSuggestionsData != null)
+                        ? inlineSuggestionsData.toArray(new Dataset[inlineSuggestionsData.size()])
+                        : null;
+                mCallback.onSuccess(inlineSuggestions);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
             }
         }
 
-        // Used (mostly) for metrics.
-        public void report(@ReportEvent int event) {
-            if (sVerbose) Log.v(TAG, "report(): " + event);
+        void logEvent(@ReportEvent int event) {
+            if (sVerbose) Log.v(TAG, "returnAndLogResult(): " + event);
             long duration = -1;
             int type = MetricsEvent.TYPE_UNKNOWN;
+
             switch (event) {
-                case REPORT_EVENT_NO_RESPONSE:
+                case REPORT_EVENT_NO_RESPONSE: {
                     type = MetricsEvent.TYPE_SUCCESS;
                     if (mFirstOnSuccessTime == 0) {
                         mFirstOnSuccessTime = SystemClock.elapsedRealtime();
@@ -538,40 +541,49 @@
                             Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
                         }
                     }
-                    try {
-                        mCallback.onSuccess(/* inlineSuggestionsData= */null, /* clientState=*/
-                                null);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error reporting success: " + e);
+                } break;
+
+                case REPORT_EVENT_INLINE_RESPONSE: {
+                    // TODO: Define a constant and log this event
+                    // type = MetricsEvent.TYPE_SUCCESS_INLINE;
+                    if (mFirstOnSuccessTime == 0) {
+                        mFirstOnSuccessTime = SystemClock.elapsedRealtime();
+                        duration = mFirstOnSuccessTime - mFirstRequestTime;
+                        if (sDebug) {
+                            Log.d(TAG, "Service responded nothing in " + formatDuration(duration));
+                        }
                     }
-                    break;
-                case REPORT_EVENT_UI_SHOWN:
+                } break;
+
+                case REPORT_EVENT_UI_SHOWN: {
                     type = MetricsEvent.TYPE_OPEN;
                     if (mUiFirstShownTime == 0) {
                         mUiFirstShownTime = SystemClock.elapsedRealtime();
                         duration = mUiFirstShownTime - mFirstRequestTime;
                         if (sDebug) Log.d(TAG, "UI shown in " + formatDuration(duration));
                     }
-                    break;
-                case REPORT_EVENT_UI_DESTROYED:
+                } break;
+
+                case REPORT_EVENT_UI_DESTROYED: {
                     type = MetricsEvent.TYPE_CLOSE;
                     if (mUiFirstDestroyedTime == 0) {
                         mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
-                        duration =  mUiFirstDestroyedTime - mFirstRequestTime;
+                        duration = mUiFirstDestroyedTime - mFirstRequestTime;
                         if (sDebug) Log.d(TAG, "UI destroyed in " + formatDuration(duration));
                     }
-                    break;
+                } break;
+
                 default:
                     Log.w(TAG, "invalid event reported: " + event);
             }
-            logResponse(type, mServicePackageName, componentName, mSessionId, duration);
+            logResponse(type, mServicePackageName, mComponentName, mSessionId, duration);
         }
 
         public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
             pw.print(prefix); pw.print("sessionId: "); pw.println(mSessionId);
-            pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
+            pw.print(prefix); pw.print("taskId: "); pw.println(mTaskId);
             pw.print(prefix); pw.print("component: ");
-            pw.println(componentName.flattenToShortString());
+            pw.println(mComponentName.flattenToShortString());
             pw.print(prefix); pw.print("focusedId: "); pw.println(mFocusedId);
             if (mFocusedValue != null) {
                 pw.print(prefix); pw.print("focusedValue: "); pw.println(mFocusedValue);
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index d0ffd7b..19eff57 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -54,13 +54,15 @@
         if (sDebug) Log.d(TAG, "onSuccess(): " + response);
 
         if (response == null) {
-            mProxy.report(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
+            mProxy.logEvent(AutofillProxy.REPORT_EVENT_NO_RESPONSE);
+            mProxy.reportResult(null /*inlineSuggestions*/);
             return;
         }
 
         List<Dataset> inlineSuggestions = response.getInlineSuggestions();
         if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
-            mProxy.onInlineSuggestionsDataReady(inlineSuggestions, response.getClientState());
+            mProxy.logEvent(AutofillProxy.REPORT_EVENT_INLINE_RESPONSE);
+            mProxy.reportResult(inlineSuggestions);
             return;
         }
 
diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java
index 63ec2d8..7d552d6 100644
--- a/core/java/android/service/autofill/augmented/FillController.java
+++ b/core/java/android/service/autofill/augmented/FillController.java
@@ -62,12 +62,13 @@
 
         try {
             mProxy.autofill(values);
-            final FillWindow fillWindow = mProxy.getFillWindow();
-            if (fillWindow != null) {
-                fillWindow.destroy();
-            }
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
+
+        final FillWindow fillWindow = mProxy.getFillWindow();
+        if (fillWindow != null) {
+            fillWindow.destroy();
+        }
     }
 }
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index ca49e7d..6927cf6 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -53,7 +53,7 @@
      * Gets the task of the activity associated with this request.
      */
     public int getTaskId() {
-        return mProxy.taskId;
+        return mProxy.mTaskId;
     }
 
     /**
@@ -61,7 +61,7 @@
      */
     @NonNull
     public ComponentName getActivityComponent() {
-        return mProxy.componentName;
+        return mProxy.mComponentName;
     }
 
     /**
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 5d00370..077df6c 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -21,6 +21,7 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.graphics.Rect;
@@ -41,6 +42,7 @@
 import dalvik.system.CloseGuard;
 
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 
 /**
  * Handle to a window used to display the augmented autofill UI.
@@ -70,23 +72,22 @@
     private final CloseGuard mCloseGuard = CloseGuard.get();
 
     private final @NonNull Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
-    private final @NonNull FillWindowPresenter mFillWindowPresenter = new FillWindowPresenter();
 
     @GuardedBy("mLock")
-    private WindowManager mWm;
+    private @NonNull WindowManager mWm;
     @GuardedBy("mLock")
     private View mFillView;
     @GuardedBy("mLock")
     private boolean mShowing;
     @GuardedBy("mLock")
-    private Rect mBounds;
+    private @Nullable Rect mBounds;
 
     @GuardedBy("mLock")
     private boolean mUpdateCalled;
     @GuardedBy("mLock")
     private boolean mDestroyed;
 
-    private AutofillProxy mProxy;
+    private @NonNull AutofillProxy mProxy;
 
     /**
      * Updates the content of the window.
@@ -172,11 +173,11 @@
                 try {
                     mProxy.requestShowFillUi(mBounds.right - mBounds.left,
                             mBounds.bottom - mBounds.top,
-                            /*anchorBounds=*/ null, mFillWindowPresenter);
+                            /*anchorBounds=*/ null, new FillWindowPresenter(this));
                 } catch (RemoteException e) {
                     Log.w(TAG, "Error requesting to show fill window", e);
                 }
-                mProxy.report(AutofillProxy.REPORT_EVENT_UI_SHOWN);
+                mProxy.logEvent(AutofillProxy.REPORT_EVENT_UI_SHOWN);
             }
         }
     }
@@ -244,7 +245,7 @@
             if (mUpdateCalled) {
                 mFillView.setOnClickListener(null);
                 hide();
-                mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
+                mProxy.logEvent(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
             }
             mDestroyed = true;
             mCloseGuard.close();
@@ -254,9 +255,7 @@
     @Override
     protected void finalize() throws Throwable {
         try {
-            if (mCloseGuard != null) {
-                mCloseGuard.warnIfOpen();
-            }
+            mCloseGuard.warnIfOpen();
             destroy();
         } finally {
             super.finalize();
@@ -289,22 +288,36 @@
 
     /** @hide */
     @Override
-    public void close() throws Exception {
+    public void close() {
         destroy();
     }
 
-    private final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
+    private static final class FillWindowPresenter extends IAutofillWindowPresenter.Stub {
+        private final @NonNull WeakReference<FillWindow> mFillWindowReference;
+
+        FillWindowPresenter(@NonNull FillWindow fillWindow) {
+            mFillWindowReference = new WeakReference<>(fillWindow);
+        }
+
         @Override
         public void show(WindowManager.LayoutParams p, Rect transitionEpicenter,
                 boolean fitsSystemWindows, int layoutDirection) {
             if (sDebug) Log.d(TAG, "FillWindowPresenter.show()");
-            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleShow, FillWindow.this, p));
+            final FillWindow fillWindow = mFillWindowReference.get();
+            if (fillWindow != null) {
+                fillWindow.mUiThreadHandler.sendMessage(
+                        obtainMessage(FillWindow::handleShow, fillWindow, p));
+            }
         }
 
         @Override
         public void hide(Rect transitionEpicenter) {
             if (sDebug) Log.d(TAG, "FillWindowPresenter.hide()");
-            mUiThreadHandler.sendMessage(obtainMessage(FillWindow::handleHide, FillWindow.this));
+            final FillWindow fillWindow = mFillWindowReference.get();
+            if (fillWindow != null) {
+                fillWindow.mUiThreadHandler.sendMessage(
+                        obtainMessage(FillWindow::handleHide, fillWindow));
+            }
         }
     }
 }
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 31e77f35..d983721 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -28,7 +28,7 @@
  */
 interface IFillCallback {
     void onCancellable(in ICancellationSignal cancellation);
-    void onSuccess(in @nullable Dataset[] inlineSuggestionsData, in @nullable Bundle clientState);
+    void onSuccess(in @nullable Dataset[] inlineSuggestionsData);
     boolean isCompleted();
     void cancel();
 }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 3f9462c..af91596 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -16,6 +16,9 @@
 
 package android.service.notification;
 
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
@@ -99,6 +102,8 @@
     private static final boolean DEFAULT_ALLOW_REMINDERS = false;
     private static final boolean DEFAULT_ALLOW_EVENTS = false;
     private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true;
+    private static final boolean DEFAULT_ALLOW_CONV = true;
+    private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
     private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
     private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 0;
 
@@ -120,6 +125,8 @@
     private static final String ALLOW_ATT_EVENTS = "events";
     private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
     private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
+    private static final String ALLOW_ATT_CONV = "conv";
+    private static final String ALLOW_ATT_CONV_FROM = "convFrom";
     private static final String DISALLOW_TAG = "disallow";
     private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
     private static final String STATE_TAG = "state";
@@ -170,6 +177,8 @@
     public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
     public int allowCallsFrom = DEFAULT_CALLS_SOURCE;
     public int allowMessagesFrom = DEFAULT_SOURCE;
+    public boolean allowConversations = DEFAULT_ALLOW_CONV;
+    public int allowConversationsFrom = DEFAULT_ALLOW_CONV_FROM;
     public int user = UserHandle.USER_SYSTEM;
     public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
     public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND;
@@ -207,6 +216,8 @@
         allowSystem = source.readInt() == 1;
         suppressedVisualEffects = source.readInt();
         areChannelsBypassingDnd = source.readInt() == 1;
+        allowConversations = source.readBoolean();
+        allowConversationsFrom = source.readInt();
     }
 
     @Override
@@ -239,6 +250,8 @@
         dest.writeInt(allowSystem ? 1 : 0);
         dest.writeInt(suppressedVisualEffects);
         dest.writeInt(areChannelsBypassingDnd ? 1 : 0);
+        dest.writeBoolean(allowConversations);
+        dest.writeInt(allowConversationsFrom);
     }
 
     @Override
@@ -253,8 +266,11 @@
                 .append(",allowCalls=").append(allowCalls)
                 .append(",allowRepeatCallers=").append(allowRepeatCallers)
                 .append(",allowMessages=").append(allowMessages)
+                .append(",allowConversations=").append(allowConversations)
                 .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
                 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
+                .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString
+                        (allowConversationsFrom))
                 .append(",suppressedVisualEffects=").append(suppressedVisualEffects)
                 .append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
                 .append(",\nautomaticRules=").append(rulesToString())
@@ -431,7 +447,9 @@
                 && Objects.equals(other.automaticRules, automaticRules)
                 && Objects.equals(other.manualRule, manualRule)
                 && other.suppressedVisualEffects == suppressedVisualEffects
-                && other.areChannelsBypassingDnd == areChannelsBypassingDnd;
+                && other.areChannelsBypassingDnd == areChannelsBypassingDnd
+                && other.allowConversations == allowConversations
+                && other.allowConversationsFrom == allowConversationsFrom;
     }
 
     @Override
@@ -440,7 +458,8 @@
                 allowRepeatCallers, allowMessages,
                 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
                 user, automaticRules, manualRule,
-                suppressedVisualEffects, areChannelsBypassingDnd);
+                suppressedVisualEffects, areChannelsBypassingDnd, allowConversations,
+                allowConversationsFrom);
     }
 
     private static String toDayList(int[] days) {
@@ -518,10 +537,13 @@
                             DEFAULT_ALLOW_MESSAGES);
                     rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
                             DEFAULT_ALLOW_REMINDERS);
+                    rt.allowConversations = safeBoolean(parser, ALLOW_ATT_CONV, DEFAULT_ALLOW_CONV);
                     rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
                     final int from = safeInt(parser, ALLOW_ATT_FROM, -1);
                     final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1);
                     final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1);
+                    rt.allowConversationsFrom = safeInt(parser, ALLOW_ATT_CONV_FROM,
+                            DEFAULT_ALLOW_CONV_FROM);
                     if (isValidSource(callsFrom) && isValidSource(messagesFrom)) {
                         rt.allowCallsFrom = callsFrom;
                         rt.allowMessagesFrom = messagesFrom;
@@ -602,6 +624,8 @@
         out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
         out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia));
         out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem));
+        out.attribute(null, ALLOW_ATT_CONV, Boolean.toString(allowConversations));
+        out.attribute(null, ALLOW_ATT_CONV_FROM, Integer.toString(allowConversationsFrom));
         out.endTag(null, ALLOW_TAG);
 
         out.startTag(null, DISALLOW_TAG);
@@ -944,6 +968,7 @@
         int suppressedVisualEffects = 0;
         int callSenders = defaultPolicy.priorityCallSenders;
         int messageSenders = defaultPolicy.priorityMessageSenders;
+        int conversationSenders = defaultPolicy.priorityConversationSenders;
 
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS,
                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) {
@@ -962,6 +987,14 @@
                     messageSenders);
         }
 
+        if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS,
+                isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CONVERSATIONS, defaultPolicy))) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+            conversationSenders = getNotificationPolicySenders(
+                    zenPolicy.getPriorityConversationSenders(),
+                    conversationSenders);
+        }
+
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
             priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
@@ -1047,7 +1080,7 @@
         }
 
         return new NotificationManager.Policy(priorityCategories, callSenders,
-                messageSenders, suppressedVisualEffects, defaultPolicy.state);
+                messageSenders, suppressedVisualEffects, defaultPolicy.state, conversationSenders);
     }
 
     private boolean isPriorityCategoryEnabled(int categoryType, Policy policy) {
@@ -1088,11 +1121,14 @@
         }
     }
 
-
     public Policy toNotificationPolicy() {
         int priorityCategories = 0;
         int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
         int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
+        int priorityConversationSenders = Policy.CONVERSATION_SENDERS_IMPORTANT;
+        if (allowConversations) {
+            priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+        }
         if (allowCalls) {
             priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
         }
@@ -1119,10 +1155,12 @@
         }
         priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
         priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
+        priorityConversationSenders = allowConversationsFrom;
 
         return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
                 suppressedVisualEffects, areChannelsBypassingDnd
-                ? Policy.STATE_CHANNELS_BYPASSING_DND : 0);
+                ? Policy.STATE_CHANNELS_BYPASSING_DND : 0,
+                priorityConversationSenders);
     }
 
     /**
@@ -1157,6 +1195,27 @@
         }
     }
 
+    private static int normalizePrioritySenders(int prioritySenders, int def) {
+        if (!(prioritySenders == Policy.PRIORITY_SENDERS_CONTACTS
+                || prioritySenders == Policy.PRIORITY_SENDERS_STARRED
+                || prioritySenders == Policy.PRIORITY_SENDERS_ANY)) {
+            return def;
+        }
+        return prioritySenders;
+    }
+
+    private static int normalizeConversationSenders(boolean allowed, int senders, int def) {
+        if (!allowed) {
+            return CONVERSATION_SENDERS_NONE;
+        }
+        if (!(senders == CONVERSATION_SENDERS_ANYONE
+                || senders == CONVERSATION_SENDERS_IMPORTANT
+                || senders == CONVERSATION_SENDERS_NONE)) {
+            return def;
+        }
+        return senders;
+    }
+
     public void applyNotificationPolicy(Policy policy) {
         if (policy == null) return;
         allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
@@ -1168,12 +1227,17 @@
         allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
         allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
                 != 0;
-        allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
-        allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
+        allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom);
+        allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders,
                 allowMessagesFrom);
         if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
             suppressedVisualEffects = policy.suppressedVisualEffects;
         }
+        allowConversations = (policy.priorityCategories
+                & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
+        allowConversationsFrom = normalizeConversationSenders(allowConversations,
+                policy.priorityConversationSenders,
+                allowConversationsFrom);
         if (policy.state != Policy.STATE_UNSET) {
             areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
         }
@@ -1919,10 +1983,13 @@
                 & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
         boolean allowRepeatCallers = (policy.priorityCategories
                 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
+        boolean allowConversations = (policy.priorityConversationSenders
+                & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
         boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
         boolean allowSystem =  (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
         return !allowReminders && !allowCalls && !allowMessages && !allowEvents
-                && !allowRepeatCallers && !areChannelsBypassingDnd && !allowSystem;
+                && !allowRepeatCallers && !areChannelsBypassingDnd && !allowSystem
+                && !allowConversations;
     }
 
     /**
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 6e2faa9..87295e1 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -41,6 +41,7 @@
     private ArrayList<Integer> mVisualEffects;
     private @PeopleType int mPriorityMessages = PEOPLE_TYPE_UNSET;
     private @PeopleType int mPriorityCalls = PEOPLE_TYPE_UNSET;
+    private @ConversationSenders int mConversationSenders = CONVERSATION_SENDERS_UNSET;
 
     /** @hide */
     @IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = {
@@ -52,6 +53,7 @@
             PRIORITY_CATEGORY_ALARMS,
             PRIORITY_CATEGORY_MEDIA,
             PRIORITY_CATEGORY_SYSTEM,
+            PRIORITY_CATEGORY_CONVERSATIONS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PriorityCategory {}
@@ -72,6 +74,8 @@
     public static final int PRIORITY_CATEGORY_MEDIA = 6;
     /** @hide */
     public static final int PRIORITY_CATEGORY_SYSTEM = 7;
+    /** @hide */
+    public static final int PRIORITY_CATEGORY_CONVERSATIONS = 8;
 
     /** @hide */
     @IntDef(prefix = { "VISUAL_EFFECT_" }, value = {
@@ -138,6 +142,37 @@
      */
     public static final int PEOPLE_TYPE_NONE = 4;
 
+
+    /** @hide */
+    @IntDef(prefix = { "CONVERSATION_SENDERS_" }, value = {
+            CONVERSATION_SENDERS_UNSET,
+            CONVERSATION_SENDERS_ANYONE,
+            CONVERSATION_SENDERS_IMPORTANT,
+            CONVERSATION_SENDERS_NONE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ConversationSenders {}
+
+    /**
+     * Used to indicate no preference for the type of conversations that can bypass dnd.
+     */
+    public static final int CONVERSATION_SENDERS_UNSET = 0;
+
+    /**
+     * Used to indicate all conversations can bypass dnd.
+     */
+    public static final int CONVERSATION_SENDERS_ANYONE = 1;
+
+    /**
+     * Used to indicate important conversations can bypass dnd.
+     */
+    public static final int CONVERSATION_SENDERS_IMPORTANT = 2;
+
+    /**
+     * Used to indicate no conversations can bypass dnd.
+     */
+    public static final int CONVERSATION_SENDERS_NONE = 3;
+
     /** @hide */
     @IntDef(prefix = { "STATE_" }, value = {
             STATE_UNSET,
@@ -165,11 +200,20 @@
 
     /** @hide */
     public ZenPolicy() {
-        mPriorityCategories = new ArrayList<>(Collections.nCopies(8, 0));
+        mPriorityCategories = new ArrayList<>(Collections.nCopies(9, 0));
         mVisualEffects = new ArrayList<>(Collections.nCopies(7, 0));
     }
 
     /**
+     * Conversation type that can bypass DND.
+     * @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE},
+     * {@link #CONVERSATION_SENDERS_IMPORTANT}, {@link #CONVERSATION_SENDERS_NONE}.
+     */
+    public @PeopleType int getPriorityConversationSenders() {
+        return mConversationSenders;
+    }
+
+    /**
      * Message senders that can bypass DND.
      * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE},
      * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE}
@@ -188,6 +232,16 @@
     }
 
     /**
+     * Whether this policy wants to allow conversation notifications
+     * (see {@link NotificationChannel#getConversationId()}) to play sounds and visually appear
+     * or to intercept them when DND is active.
+     * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
+     */
+    public @State int getPriorityCategoryConversations() {
+        return mPriorityCategories.get(PRIORITY_CATEGORY_CONVERSATIONS);
+    }
+
+    /**
      * Whether this policy wants to allow notifications with category
      * {@link Notification#CATEGORY_REMINDER} to play sounds and visually appear
      * or to intercept them when DND is active.
@@ -392,6 +446,7 @@
             }
             mZenPolicy.mPriorityMessages = PEOPLE_TYPE_ANYONE;
             mZenPolicy.mPriorityCalls = PEOPLE_TYPE_ANYONE;
+            mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_ANYONE;
             return this;
         }
 
@@ -408,6 +463,7 @@
             }
             mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE;
             mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE;
+            mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_NONE;
             return this;
         }
 
@@ -443,6 +499,8 @@
                 mZenPolicy.mPriorityMessages = STATE_UNSET;
             } else if (category == PRIORITY_CATEGORY_CALLS) {
                 mZenPolicy.mPriorityCalls = STATE_UNSET;
+            } else if (category == PRIORITY_CATEGORY_CONVERSATIONS) {
+                mZenPolicy.mConversationSenders = STATE_UNSET;
             }
 
             return this;
@@ -479,6 +537,31 @@
         }
 
         /**
+         * Whether to allow conversation notifications
+         * (see {@link NotificationChannel#setConversationId(String, String)})
+         * that match audienceType to play sounds and visually appear or to intercept
+         * them when DND is active.
+         * @param audienceType callers that are allowed to bypass DND
+         */
+        public @NonNull  Builder allowConversations(@ConversationSenders int audienceType) {
+            if (audienceType == STATE_UNSET) {
+                return unsetPriorityCategory(PRIORITY_CATEGORY_CONVERSATIONS);
+            }
+
+            if (audienceType == CONVERSATION_SENDERS_NONE) {
+                mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_DISALLOW);
+            } else if (audienceType == CONVERSATION_SENDERS_ANYONE
+                    || audienceType == CONVERSATION_SENDERS_IMPORTANT) {
+                mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_ALLOW);
+            } else {
+                return this;
+            }
+
+            mZenPolicy.mConversationSenders = audienceType;
+            return this;
+        }
+
+        /**
          * Whether to allow notifications with category {@link Notification#CATEGORY_MESSAGE}
          * that match audienceType to play sounds and visually appear or to intercept
          * them when DND is active.
@@ -537,7 +620,6 @@
             return this;
         }
 
-
         /**
          * Whether to allow notifications with category {@link Notification#CATEGORY_ALARM}
          * to play sounds and visually appear or to intercept them when DND is active.
@@ -712,6 +794,7 @@
         dest.writeList(mVisualEffects);
         dest.writeInt(mPriorityCalls);
         dest.writeInt(mPriorityMessages);
+        dest.writeInt(mConversationSenders);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ZenPolicy> CREATOR =
@@ -723,6 +806,7 @@
             policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader());
             policy.mPriorityCalls = source.readInt();
             policy.mPriorityMessages = source.readInt();
+            policy.mConversationSenders = source.readInt();
             return policy;
         }
 
@@ -738,8 +822,10 @@
                 .append('{')
                 .append("priorityCategories=[").append(priorityCategoriesToString())
                 .append("], visualEffects=[").append(visualEffectsToString())
-                .append("], priorityCalls=").append(peopleTypeToString(mPriorityCalls))
-                .append(", priorityMessages=").append(peopleTypeToString(mPriorityMessages))
+                .append("], priorityCallsSenders=").append(peopleTypeToString(mPriorityCalls))
+                .append(", priorityMessagesSenders=").append(peopleTypeToString(mPriorityMessages))
+                .append(", priorityConversationSenders=").append(
+                        conversationTypeToString(mConversationSenders))
                 .append('}')
                 .toString();
     }
@@ -811,6 +897,8 @@
                 return "media";
             case PRIORITY_CATEGORY_SYSTEM:
                 return "system";
+            case PRIORITY_CATEGORY_CONVERSATIONS:
+                return "convs";
         }
         return null;
     }
@@ -843,6 +931,23 @@
         return "invalidPeopleType{" + peopleType + "}";
     }
 
+    /**
+     * @hide
+     */
+    public static String conversationTypeToString(@ConversationSenders int conversationType) {
+        switch (conversationType) {
+            case CONVERSATION_SENDERS_ANYONE:
+                return "anyone";
+            case CONVERSATION_SENDERS_IMPORTANT:
+                return "important";
+            case CONVERSATION_SENDERS_NONE:
+                return "none";
+            case CONVERSATION_SENDERS_UNSET:
+                return "unset";
+        }
+        return "invalidConversationType{" + conversationType + "}";
+    }
+
     @Override
     public boolean equals(Object o) {
         if (!(o instanceof ZenPolicy)) return false;
@@ -852,12 +957,14 @@
         return Objects.equals(other.mPriorityCategories, mPriorityCategories)
                 && Objects.equals(other.mVisualEffects, mVisualEffects)
                 && other.mPriorityCalls == mPriorityCalls
-                && other.mPriorityMessages == mPriorityMessages;
+                && other.mPriorityMessages == mPriorityMessages
+                && other.mConversationSenders == mConversationSenders;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages);
+        return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages,
+                mConversationSenders);
     }
 
     private @ZenPolicy.State int getZenPolicyPriorityCategoryState(@PriorityCategory int
@@ -879,6 +986,8 @@
                 return getPriorityCategoryMedia();
             case PRIORITY_CATEGORY_SYSTEM:
                 return getPriorityCategorySystem();
+            case PRIORITY_CATEGORY_CONVERSATIONS:
+                return getPriorityCategoryConversations();
         }
         return -1;
     }
@@ -953,6 +1062,9 @@
                 } else if (category == PRIORITY_CATEGORY_CALLS
                         && mPriorityCalls < policyToApply.mPriorityCalls) {
                     mPriorityCalls = policyToApply.mPriorityCalls;
+                } else if (category == PRIORITY_CATEGORY_CONVERSATIONS
+                        && mConversationSenders < policyToApply.mConversationSenders) {
+                    mConversationSenders = policyToApply.mConversationSenders;
                 }
             }
         }
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 8dca69f..848868a 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -27,7 +27,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
@@ -42,7 +41,6 @@
 import android.view.textclassifier.ConversationActions;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassificationConstants;
 import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassificationSessionId;
@@ -394,19 +392,32 @@
      */
     @Deprecated
     public final TextClassifier getLocalTextClassifier() {
-        // Deprecated: In the future, we may not guarantee that this runs in the service's process.
-        return getDefaultTextClassifierImplementation(this);
+        return TextClassifier.NO_OP;
     }
 
     /**
      * Returns the platform's default TextClassifier implementation.
+     *
+     * @throws RuntimeException if the TextClassifier from
+     *                          PackageManager#getDefaultTextClassifierPackageName() calls
+     *                          this method.
      */
     @NonNull
     public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
+        final String defaultTextClassifierPackageName =
+                context.getPackageManager().getDefaultTextClassifierPackageName();
+        if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
+            return TextClassifier.NO_OP;
+        }
+        if (defaultTextClassifierPackageName.equals(context.getPackageName())) {
+            throw new RuntimeException(
+                    "The default text classifier itself should not call the"
+                            + "getDefaultTextClassifierImplementation() method.");
+        }
         final TextClassificationManager tcm =
                 context.getSystemService(TextClassificationManager.class);
         if (tcm != null) {
-            return tcm.getTextClassifier(TextClassifier.LOCAL);
+            return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
         }
         return TextClassifier.NO_OP;
     }
@@ -434,46 +445,20 @@
     }
 
     /**
-     * Returns the component name of the system default textclassifier service if it can be found
-     * on the system. Otherwise, returns null.
+     * Returns the component name of the textclassifier service from the given package.
+     * Otherwise, returns null.
      *
-     * @param context the text classification context
+     * @param context
+     * @param packageName  the package to look for.
+     * @param resolveFlags the flags that are used by PackageManager to resolve the component name.
      * @hide
      */
     @Nullable
-    public static ComponentName getServiceComponentName(@NonNull Context context) {
-        final TextClassificationConstants settings = TextClassificationManager.getSettings(context);
-        // get override TextClassifierService package name
-        String packageName = settings.getTextClassifierServicePackageOverride();
-
-        ComponentName serviceComponent = null;
-        final boolean isOverrideService = !TextUtils.isEmpty(packageName);
-        if (isOverrideService) {
-            serviceComponent = getServiceComponentNameByPackage(context, packageName,
-                    isOverrideService);
-        }
-        if (serviceComponent != null) {
-            return serviceComponent;
-        }
-        // If no TextClassifierService override or invalid override package name, read the first
-        // package defined in the config
-        final String[] packages = context.getPackageManager().getSystemTextClassifierPackages();
-        if (packages.length == 0 || TextUtils.isEmpty(packages[0])) {
-            Slog.d(LOG_TAG, "No configured system TextClassifierService");
-            return null;
-        }
-        packageName = packages[0];
-        serviceComponent = getServiceComponentNameByPackage(context, packageName,
-                isOverrideService);
-        return serviceComponent;
-    }
-
-    private static ComponentName getServiceComponentNameByPackage(Context context,
-            String packageName, boolean isOverrideService) {
+    public static ComponentName getServiceComponentName(
+            Context context, String packageName, int resolveFlags) {
         final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName);
 
-        final int flags = isOverrideService ? 0 : PackageManager.MATCH_SYSTEM_ONLY;
-        final ResolveInfo ri = context.getPackageManager().resolveService(intent, flags);
+        final ResolveInfo ri = context.getPackageManager().resolveService(intent, resolveFlags);
 
         if ((ri == null) || (ri.serviceInfo == null)) {
             Slog.w(LOG_TAG, String.format("Package or service not found in package %s for user %d",
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index 970acd0..ab2c4fc 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
 import android.icu.util.TimeZone;
 
 import java.util.ArrayList;
@@ -32,7 +31,6 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class CountryTimeZones {
 
     /**
@@ -40,7 +38,6 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final class TimeZoneMapping {
 
         @NonNull
@@ -97,7 +94,6 @@
      *
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final class OffsetResult {
 
         private final TimeZone mTimeZone;
diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java
index 8a5864e..a4c3fbd 100644
--- a/core/java/android/timezone/TelephonyLookup.java
+++ b/core/java/android/timezone/TelephonyLookup.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -29,7 +28,6 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class TelephonyLookup {
 
     private static final Object sLock = new Object();
diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java
index 487b3f2..823cd25 100644
--- a/core/java/android/timezone/TelephonyNetwork.java
+++ b/core/java/android/timezone/TelephonyNetwork.java
@@ -17,7 +17,6 @@
 package android.timezone;
 
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 
 import java.util.Objects;
 
@@ -26,7 +25,6 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class TelephonyNetwork {
 
     @NonNull
diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java
index 2ddd3d9..4bfeff8 100644
--- a/core/java/android/timezone/TelephonyNetworkFinder.java
+++ b/core/java/android/timezone/TelephonyNetworkFinder.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 
 import java.util.Objects;
 
@@ -27,7 +26,6 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class TelephonyNetworkFinder {
 
     @NonNull
diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java
index c76bb1d..03f5013 100644
--- a/core/java/android/timezone/TimeZoneFinder.java
+++ b/core/java/android/timezone/TimeZoneFinder.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -29,7 +28,6 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class TimeZoneFinder {
 
     private static final Object sLock = new Object();
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
index efe50a0..f993012 100644
--- a/core/java/android/timezone/TzDataSetVersion.java
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -17,7 +17,6 @@
 package android.timezone;
 
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -45,7 +44,6 @@
  * @hide
  */
 @VisibleForTesting
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class TzDataSetVersion {
 
     /**
@@ -88,7 +86,6 @@
      * A checked exception used in connection with time zone data sets.
      * @hide
      */
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final class TzDataSetException extends Exception {
 
         /** Creates an instance with a message. */
diff --git a/core/java/android/timezone/ZoneInfoDb.java b/core/java/android/timezone/ZoneInfoDb.java
index 4612a56..9354a69 100644
--- a/core/java/android/timezone/ZoneInfoDb.java
+++ b/core/java/android/timezone/ZoneInfoDb.java
@@ -17,7 +17,6 @@
 package android.timezone;
 
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -29,7 +28,6 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class ZoneInfoDb {
 
     private static final Object sLock = new Object();
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d79fc9a..f61217d 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1514,6 +1514,7 @@
         public HdrCapabilities(int[] supportedHdrTypes, float maxLuminance,
                 float maxAverageLuminance, float minLuminance) {
             mSupportedHdrTypes = supportedHdrTypes;
+            Arrays.sort(mSupportedHdrTypes);
             mMaxLuminance = maxLuminance;
             mMaxAverageLuminance = maxAverageLuminance;
             mMinLuminance = minLuminance;
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index b9f08ad..b4c8795 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.graphics.Bitmap;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -829,31 +828,6 @@
     }
 
     /**
-     * Takes the screenshot of the specified display and returns it by bitmap format.
-     *
-     * @param connectionId The id of a connection for interacting with the system.
-     * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for
-     *                  default display.
-     * @return The screenshot bitmap on success, null otherwise.
-     */
-    public Bitmap takeScreenshot(int connectionId, int displayId) {
-        Bitmap screenShot = null;
-        try {
-            IAccessibilityServiceConnection connection = getConnection(connectionId);
-            if (connection != null) {
-                screenShot = connection.takeScreenshot(displayId);
-            } else {
-                if (DEBUG) {
-                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
-                }
-            }
-        } catch (RemoteException re) {
-            Log.w(LOG_TAG, "Error while calling remote takeScreenshot", re);
-        }
-        return screenShot;
-    }
-
-    /**
      * Clears the result state.
      */
     private void clearResultLocked() {
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 80027b1e..6246b50 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -323,6 +323,7 @@
         private int mUserId = UserHandle.USER_NULL;
         @NonNull
         private Bundle mExtras;
+        private boolean mUseDefaultTextClassifier;
 
         private Request(
                 @NonNull List<Message> conversation,
@@ -347,6 +348,8 @@
             String callingPackageName = in.readString();
             int userId = in.readInt();
             Bundle extras = in.readBundle();
+            boolean useDefaultTextClassifier = in.readBoolean();
+
             Request request = new Request(
                     conversation,
                     typeConfig,
@@ -355,6 +358,7 @@
                     extras);
             request.setCallingPackageName(callingPackageName);
             request.setUserId(userId);
+            request.setUseDefaultTextClassifier(useDefaultTextClassifier);
             return request;
         }
 
@@ -367,6 +371,7 @@
             parcel.writeString(mCallingPackageName);
             parcel.writeInt(mUserId);
             parcel.writeBundle(mExtras);
+            parcel.writeBoolean(mUseDefaultTextClassifier);
         }
 
         @Override
@@ -455,6 +460,26 @@
         }
 
         /**
+         * Sets whether to use the default text classifier to handle this request.
+         * This will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        }
+
+        /**
+         * Returns whether to use the default text classifier to handle this request. This
+         * will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        public boolean getUseDefaultTextClassifier() {
+            return mUseDefaultTextClassifier;
+        }
+
+        /**
          * Returns the extended data related to this request.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 09cb7a0..e0f29a9 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -140,6 +140,7 @@
     private int mEnd;
     private int mSmartStart;
     private int mSmartEnd;
+    private boolean mUseDefaultTextClassifier;
 
     SelectionEvent(
             int start, int end,
@@ -175,6 +176,7 @@
         mSmartStart = in.readInt();
         mSmartEnd = in.readInt();
         mUserId = in.readInt();
+        mUseDefaultTextClassifier = in.readBoolean();
     }
 
     @Override
@@ -204,6 +206,7 @@
         dest.writeInt(mSmartStart);
         dest.writeInt(mSmartEnd);
         dest.writeInt(mUserId);
+        dest.writeBoolean(mUseDefaultTextClassifier);
     }
 
     @Override
@@ -428,6 +431,26 @@
     }
 
     /**
+     * Sets whether to use the default text classifier to handle this request.
+     * This will be ignored if it is not the system text classifier to handle this request.
+     *
+     * @hide
+     */
+    void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+        mUseDefaultTextClassifier = useDefaultTextClassifier;
+    }
+
+    /**
+     * Returns whether to use the default text classifier to handle this request. This
+     * will be ignored if it is not the system text classifier to handle this request.
+     *
+     * @hide
+     */
+    public boolean getUseDefaultTextClassifier() {
+        return mUseDefaultTextClassifier;
+    }
+
+    /**
      * Returns the type of widget that was involved in triggering this event.
      */
     @WidgetType
@@ -642,7 +665,8 @@
         return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
                 mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId,
                 mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent,
-                mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
+                mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd,
+                mUseDefaultTextClassifier);
     }
 
     @Override
@@ -673,7 +697,8 @@
                 && mStart == other.mStart
                 && mEnd == other.mEnd
                 && mSmartStart == other.mSmartStart
-                && mSmartEnd == other.mSmartEnd;
+                && mSmartEnd == other.mSmartEnd
+                && mUseDefaultTextClassifier == other.mUseDefaultTextClassifier;
     }
 
     @Override
@@ -683,12 +708,13 @@
                         + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, "
                         + "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, "
                         + "durationSincePreviousEvent=%d, eventIndex=%d,"
-                        + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}",
+                        + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d, "
+                        + "mUseDefaultTextClassifier=%b}",
                 mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType,
                 mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod,
                 mUserId, mResultId, mEventTime, mDurationSinceSessionStart,
                 mDurationSincePreviousEvent, mEventIndex,
-                mSessionId, mStart, mEnd, mSmartStart, mSmartEnd);
+                mSessionId, mStart, mEnd, mSmartStart, mSmartEnd, mUseDefaultTextClassifier);
     }
 
     public static final @android.annotation.NonNull Creator<SelectionEvent> CREATOR = new Creator<SelectionEvent>() {
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 138d25d..fe5e8d6 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -55,17 +55,20 @@
     // service will throw a remote exception.
     @UserIdInt
     private final int mUserId;
+    private final boolean mUseDefault;
     private TextClassificationSessionId mSessionId;
 
-    public SystemTextClassifier(Context context, TextClassificationConstants settings)
-                throws ServiceManager.ServiceNotFoundException {
+    public SystemTextClassifier(
+            Context context,
+            TextClassificationConstants settings,
+            boolean useDefault) throws ServiceManager.ServiceNotFoundException {
         mManagerService = ITextClassifierService.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
         mSettings = Objects.requireNonNull(settings);
-        mFallback = context.getSystemService(TextClassificationManager.class)
-                .getTextClassifier(TextClassifier.LOCAL);
+        mFallback = TextClassifier.NO_OP;
         mPackageName = Objects.requireNonNull(context.getOpPackageName());
         mUserId = context.getUserId();
+        mUseDefault = useDefault;
     }
 
     /**
@@ -79,6 +82,7 @@
         try {
             request.setCallingPackageName(mPackageName);
             request.setUserId(mUserId);
+            request.setUseDefaultTextClassifier(mUseDefault);
             final BlockingCallback<TextSelection> callback =
                     new BlockingCallback<>("textselection");
             mManagerService.onSuggestSelection(mSessionId, request, callback);
@@ -103,6 +107,7 @@
         try {
             request.setCallingPackageName(mPackageName);
             request.setUserId(mUserId);
+            request.setUseDefaultTextClassifier(mUseDefault);
             final BlockingCallback<TextClassification> callback =
                     new BlockingCallback<>("textclassification");
             mManagerService.onClassifyText(mSessionId, request, callback);
@@ -124,7 +129,9 @@
     public TextLinks generateLinks(@NonNull TextLinks.Request request) {
         Objects.requireNonNull(request);
         Utils.checkMainThread();
-
+        if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) {
+            return mFallback.generateLinks(request);
+        }
         if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
             return Utils.generateLegacyLinks(request);
         }
@@ -132,6 +139,7 @@
         try {
             request.setCallingPackageName(mPackageName);
             request.setUserId(mUserId);
+            request.setUseDefaultTextClassifier(mUseDefault);
             final BlockingCallback<TextLinks> callback =
                     new BlockingCallback<>("textlinks");
             mManagerService.onGenerateLinks(mSessionId, request, callback);
@@ -152,6 +160,7 @@
 
         try {
             event.setUserId(mUserId);
+            event.setUseDefaultTextClassifier(mUseDefault);
             mManagerService.onSelectionEvent(mSessionId, event);
         } catch (RemoteException e) {
             Log.e(LOG_TAG, "Error reporting selection event.", e);
@@ -169,6 +178,7 @@
                             .build()
                     : event.getEventContext();
             tcContext.setUserId(mUserId);
+            tcContext.setUseDefaultTextClassifier(mUseDefault);
             event.setEventContext(tcContext);
             mManagerService.onTextClassifierEvent(mSessionId, event);
         } catch (RemoteException e) {
@@ -184,6 +194,7 @@
         try {
             request.setCallingPackageName(mPackageName);
             request.setUserId(mUserId);
+            request.setUseDefaultTextClassifier(mUseDefault);
             final BlockingCallback<TextLanguage> callback =
                     new BlockingCallback<>("textlanguage");
             mManagerService.onDetectLanguage(mSessionId, request, callback);
@@ -205,6 +216,7 @@
         try {
             request.setCallingPackageName(mPackageName);
             request.setUserId(mUserId);
+            request.setUseDefaultTextClassifier(mUseDefault);
             final BlockingCallback<ConversationActions> callback =
                     new BlockingCallback<>("conversation-actions");
             mManagerService.onSuggestConversationActions(mSessionId, request, callback);
@@ -225,7 +237,7 @@
     @WorkerThread
     public int getMaxGenerateLinksTextLength() {
         // TODO: retrieve this from the bound service.
-        return mFallback.getMaxGenerateLinksTextLength();
+        return mSettings.getGenerateLinksMaxTextLength();
     }
 
     @Override
@@ -247,6 +259,7 @@
         printWriter.printPair("mPackageName", mPackageName);
         printWriter.printPair("mSessionId", mSessionId);
         printWriter.printPair("mUserId", mUserId);
+        printWriter.printPair("mUseDefault",  mUseDefault);
         printWriter.decreaseIndent();
         printWriter.println();
     }
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 3628d2d4..00f762b 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -555,6 +555,7 @@
         @Nullable private String mCallingPackageName;
         @UserIdInt
         private int mUserId = UserHandle.USER_NULL;
+        private boolean mUseDefaultTextClassifier;
 
         private Request(
                 CharSequence text,
@@ -654,6 +655,26 @@
         }
 
         /**
+         * Sets whether to use the default text classifier to handle this request.
+         * This will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        }
+
+        /**
+         * Returns whether to use the default text classifier to handle this request. This
+         * will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        public boolean getUseDefaultTextClassifier() {
+            return mUseDefaultTextClassifier;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -755,6 +776,7 @@
             dest.writeString(mCallingPackageName);
             dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
+            dest.writeBoolean(mUseDefaultTextClassifier);
         }
 
         private static Request readFromParcel(Parcel in) {
@@ -768,11 +790,13 @@
             final String callingPackageName = in.readString();
             final int userId = in.readInt();
             final Bundle extras = in.readBundle();
+            final boolean useDefaultTextClassifier = in.readBoolean();
 
             final Request request = new Request(text, startIndex, endIndex,
                     defaultLocales, referenceTime, extras);
             request.setCallingPackageName(callingPackageName);
             request.setUserId(userId);
+            request.setUseDefaultTextClassifier(useDefaultTextClassifier);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
index 930765b..d58d175 100644
--- a/core/java/android/view/textclassifier/TextClassificationContext.java
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -38,6 +38,7 @@
     @Nullable private final String mWidgetVersion;
     @UserIdInt
     private int mUserId = UserHandle.USER_NULL;
+    private boolean mUseDefaultTextClassifier;
 
     private TextClassificationContext(
             String packageName,
@@ -76,6 +77,26 @@
     }
 
     /**
+     * Sets whether to use the default text classifier to handle this request.
+     * This will be ignored if it is not the system text classifier to handle this request.
+     *
+     * @hide
+     */
+    void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+        mUseDefaultTextClassifier = useDefaultTextClassifier;
+    }
+
+    /**
+     * Returns whether to use the default text classifier to handle this request. This
+     * will be ignored if it is not the system text classifier to handle this request.
+     *
+     * @hide
+     */
+    public boolean getUseDefaultTextClassifier() {
+        return mUseDefaultTextClassifier;
+    }
+
+    /**
      * Returns the widget type for this classification context.
      */
     @NonNull
@@ -156,6 +177,7 @@
         parcel.writeString(mWidgetType);
         parcel.writeString(mWidgetVersion);
         parcel.writeInt(mUserId);
+        parcel.writeBoolean(mUseDefaultTextClassifier);
     }
 
     private TextClassificationContext(Parcel in) {
@@ -163,6 +185,7 @@
         mWidgetType = in.readString();
         mWidgetVersion = in.readString();
         mUserId = in.readInt();
+        mUseDefaultTextClassifier = in.readBoolean();
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR =
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index bb96d55..a6c83a1 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -25,7 +25,7 @@
 import android.os.ServiceManager;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
-import android.service.textclassifier.TextClassifierService;
+import android.util.SparseArray;
 import android.view.textclassifier.TextClassifier.TextClassifierType;
 
 import com.android.internal.annotations.GuardedBy;
@@ -62,8 +62,7 @@
     @Nullable
     private TextClassifier mLocalTextClassifier;
     @GuardedBy("mLock")
-    @Nullable
-    private TextClassifier mSystemTextClassifier;
+    private SparseArray<TextClassifier> mSystemTextClassifiers = new SparseArray<>();
     @GuardedBy("mLock")
     private TextClassificationSessionFactory mSessionFactory;
     @GuardedBy("mLock")
@@ -91,8 +90,8 @@
         synchronized (mLock) {
             if (mCustomTextClassifier != null) {
                 return mCustomTextClassifier;
-            } else if (isSystemTextClassifierEnabled()) {
-                return getSystemTextClassifier();
+            } else if (getSettings().isSystemTextClassifierEnabled()) {
+                return getSystemTextClassifier(SystemTextClassifier.SYSTEM);
             } else {
                 return getLocalTextClassifier();
             }
@@ -116,6 +115,7 @@
      *
      * @see TextClassifier#LOCAL
      * @see TextClassifier#SYSTEM
+     * @see TextClassifier#DEFAULT_SERVICE
      * @hide
      */
     @UnsupportedAppUsage
@@ -124,7 +124,7 @@
             case TextClassifier.LOCAL:
                 return getLocalTextClassifier();
             default:
-                return getSystemTextClassifier();
+                return getSystemTextClassifier(type);
         }
     }
 
@@ -204,21 +204,28 @@
         }
     }
 
-    private TextClassifier getSystemTextClassifier() {
+    /** @hide */
+    private TextClassifier getSystemTextClassifier(@TextClassifierType int type) {
         synchronized (mLock) {
-            if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
+            if (mSystemTextClassifiers.get(type) == null
+                    && getSettings().isSystemTextClassifierEnabled()) {
                 try {
-                    mSystemTextClassifier = new SystemTextClassifier(mContext, getSettings());
-                    Log.d(LOG_TAG, "Initialized SystemTextClassifier");
+                    mSystemTextClassifiers.put(
+                            type,
+                            new SystemTextClassifier(
+                                    mContext,
+                                    getSettings(),
+                                    /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE));
+                    Log.d(LOG_TAG, "Initialized SystemTextClassifier, type = " + type);
                 } catch (ServiceManager.ServiceNotFoundException e) {
                     Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
                 }
             }
+            if (mSystemTextClassifiers.get(type) != null) {
+                return mSystemTextClassifiers.get(type);
+            }
+            return TextClassifier.NO_OP;
         }
-        if (mSystemTextClassifier != null) {
-            return mSystemTextClassifier;
-        }
-        return TextClassifier.NO_OP;
     }
 
     /**
@@ -240,11 +247,6 @@
         }
     }
 
-    private boolean isSystemTextClassifierEnabled() {
-        return getSettings().isSystemTextClassifierEnabled()
-                && TextClassifierService.getServiceComponentName(mContext) != null;
-    }
-
     /** @hide */
     @VisibleForTesting
     public void invalidateForTesting() {
@@ -261,7 +263,7 @@
     private void invalidateTextClassifiers() {
         synchronized (mLock) {
             mLocalTextClassifier = null;
-            mSystemTextClassifier = null;
+            mSystemTextClassifiers.clear();
         }
     }
 
@@ -274,7 +276,8 @@
     /** @hide **/
     public void dump(IndentingPrintWriter pw) {
         getLocalTextClassifier().dump(pw);
-        getSystemTextClassifier().dump(pw);
+        getSystemTextClassifier(TextClassifier.DEFAULT_SERVICE).dump(pw);
+        getSystemTextClassifier(TextClassifier.SYSTEM).dump(pw);
         getSettings().dump(pw);
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 9b33693..2cc226d 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -66,12 +66,14 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {LOCAL, SYSTEM})
+    @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SERVICE})
     @interface TextClassifierType {}  // TODO: Expose as system APIs.
     /** Specifies a TextClassifier that runs locally in the app's process. @hide */
     int LOCAL = 0;
     /** Specifies a TextClassifier that runs in the system process and serves all apps. @hide */
     int SYSTEM = 1;
+    /** Specifies the default TextClassifier that runs in the system process. @hide */
+    int DEFAULT_SERVICE = 2;
 
     /** The TextClassifier failed to run. */
     String TYPE_UNKNOWN = "";
@@ -667,8 +669,10 @@
             Preconditions.checkArgument(endIndex > startIndex);
         }
 
-        static void checkTextLength(CharSequence text, int maxLength) {
-            Preconditions.checkArgumentInRange(text.length(), 0, maxLength, "text.length()");
+        /** Returns if the length of the text is within the range. */
+        static boolean checkTextLength(CharSequence text, int maxLength) {
+            int textLength = text.length();
+            return textLength >= 0 && textLength <= maxLength;
         }
 
         /**
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 61bd7c7..d7149ee 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -286,8 +286,10 @@
     @WorkerThread
     public TextLinks generateLinks(@NonNull TextLinks.Request request) {
         Objects.requireNonNull(request);
-        Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength());
         Utils.checkMainThread();
+        if (!Utils.checkTextLength(request.getText(), getMaxGenerateLinksTextLength())) {
+            return mFallback.generateLinks(request);
+        }
 
         if (!mSettings.isSmartLinkifyEnabled() && request.isLegacyFallback()) {
             return Utils.generateLegacyLinks(request);
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index cc9109e..58024dc 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -230,6 +230,7 @@
         @Nullable private String mCallingPackageName;
         @UserIdInt
         private int mUserId = UserHandle.USER_NULL;
+        private boolean mUseDefaultTextClassifier;
 
         private Request(CharSequence text, Bundle bundle) {
             mText = text;
@@ -283,6 +284,26 @@
         }
 
         /**
+         * Sets whether to use the default text classifier to handle this request.
+         * This will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        }
+
+        /**
+         * Returns whether to use the default text classifier to handle this request. This
+         * will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        public boolean getUseDefaultTextClassifier() {
+            return mUseDefaultTextClassifier;
+        }
+
+        /**
          * Returns a bundle containing non-structured extra information about this request.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -303,6 +324,7 @@
             dest.writeString(mCallingPackageName);
             dest.writeInt(mUserId);
             dest.writeBundle(mExtra);
+            dest.writeBoolean(mUseDefaultTextClassifier);
         }
 
         private static Request readFromParcel(Parcel in) {
@@ -310,10 +332,12 @@
             final String callingPackageName = in.readString();
             final int userId = in.readInt();
             final Bundle extra = in.readBundle();
+            final boolean useDefaultTextClassifier = in.readBoolean();
 
             final Request request = new Request(text, extra);
             request.setCallingPackageName(callingPackageName);
             request.setUserId(userId);
+            request.setUseDefaultTextClassifier(useDefaultTextClassifier);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index bda12b0..7430cb3 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -345,6 +345,7 @@
         @Nullable private final ZonedDateTime mReferenceTime;
         @UserIdInt
         private int mUserId = UserHandle.USER_NULL;
+        private boolean mUseDefaultTextClassifier;
 
         private Request(
                 CharSequence text,
@@ -447,6 +448,26 @@
         }
 
         /**
+         * Sets whether to use the default text classifier to handle this request.
+         * This will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        }
+
+        /**
+         * Returns whether to use the default text classifier to handle this request. This
+         * will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        public boolean getUseDefaultTextClassifier() {
+            return mUseDefaultTextClassifier;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -568,6 +589,7 @@
             dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
             dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
+            dest.writeBoolean(mUseDefaultTextClassifier);
         }
 
         private static Request readFromParcel(Parcel in) {
@@ -580,11 +602,13 @@
             final String referenceTimeString = in.readString();
             final ZonedDateTime referenceTime = referenceTimeString == null
                     ? null : ZonedDateTime.parse(referenceTimeString);
+            final boolean useDefaultTextClassifier = in.readBoolean();
 
             final Request request = new Request(text, defaultLocales, entityConfig,
                     /* legacyFallback= */ true, referenceTime, extras);
             request.setCallingPackageName(callingPackageName);
             request.setUserId(userId);
+            request.setUseDefaultTextClassifier(useDefaultTextClassifier);
             return request;
         }
 
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 4a36cbf..575a072 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -216,6 +216,7 @@
         @Nullable private String mCallingPackageName;
         @UserIdInt
         private int mUserId = UserHandle.USER_NULL;
+        private boolean mUseDefaultTextClassifier;
 
         private Request(
                 CharSequence text,
@@ -316,6 +317,26 @@
         }
 
         /**
+         * Sets whether to use the default text classifier to handle this request.
+         * This will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        void setUseDefaultTextClassifier(boolean useDefaultTextClassifier) {
+            mUseDefaultTextClassifier = useDefaultTextClassifier;
+        }
+
+        /**
+         * Returns whether to use the default text classifier to handle this request. This
+         * will be ignored if it is not the system text classifier to handle this request.
+         *
+         * @hide
+         */
+        public boolean getUseDefaultTextClassifier() {
+            return mUseDefaultTextClassifier;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Do not modify this bundle.
@@ -420,6 +441,7 @@
             dest.writeString(mCallingPackageName);
             dest.writeInt(mUserId);
             dest.writeBundle(mExtras);
+            dest.writeBoolean(mUseDefaultTextClassifier);
         }
 
         private static Request readFromParcel(Parcel in) {
@@ -430,11 +452,13 @@
             final String callingPackageName = in.readString();
             final int userId = in.readInt();
             final Bundle extras = in.readBundle();
+            final boolean systemTextClassifierType = in.readBoolean();
 
             final Request request = new Request(text, startIndex, endIndex, defaultLocales,
                     /* darkLaunchAllowed= */ false, extras);
             request.setCallingPackageName(callingPackageName);
             request.setUserId(userId);
+            request.setUseDefaultTextClassifier(systemTextClassifierType);
             return request;
         }
 
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 6a6a60d..82eb55a 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -267,8 +267,7 @@
         targets.addAll(getAccessibilityServiceTargets(context));
         targets.addAll(getWhiteListingServiceTargets(context));
 
-        final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
-                Context.ACCESSIBILITY_SERVICE);
+        final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
         final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType);
         targets.removeIf(target -> !requiredTargets.contains(target.getId()));
 
@@ -277,8 +276,7 @@
 
     private static List<AccessibilityButtonTarget> getAccessibilityServiceTargets(
             @NonNull Context context) {
-        final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
-                Context.ACCESSIBILITY_SERVICE);
+        final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
         final List<AccessibilityServiceInfo> installedServices =
                 ams.getInstalledAccessibilityServiceList();
         if (installedServices == null) {
@@ -354,10 +352,11 @@
     }
 
     private static class ViewHolder {
+        View mItemView;
         ImageView mIconView;
         TextView mLabelView;
         FrameLayout mItemContainer;
-        ImageView mViewItem;
+        ImageView mActionViewItem;
         Switch mSwitchItem;
     }
 
@@ -407,12 +406,13 @@
                         R.layout.accessibility_button_chooser_item, parent, /* attachToRoot= */
                         false);
                 holder = new ViewHolder();
+                holder.mItemView = convertView;
                 holder.mIconView = convertView.findViewById(R.id.accessibility_button_target_icon);
                 holder.mLabelView = convertView.findViewById(
                         R.id.accessibility_button_target_label);
                 holder.mItemContainer = convertView.findViewById(
                         R.id.accessibility_button_target_item_container);
-                holder.mViewItem = convertView.findViewById(
+                holder.mActionViewItem = convertView.findViewById(
                         R.id.accessibility_button_target_view_item);
                 holder.mSwitchItem = convertView.findViewById(
                         R.id.accessibility_button_target_switch_item);
@@ -465,11 +465,12 @@
             holder.mIconView.setAlpha(enabledState
                     ? ENABLED_ALPHA : DISABLED_ALPHA);
             holder.mLabelView.setEnabled(enabledState);
-            holder.mViewItem.setEnabled(enabledState);
-            holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
-            holder.mViewItem.setVisibility(View.VISIBLE);
+            holder.mActionViewItem.setEnabled(enabledState);
+            holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+            holder.mActionViewItem.setVisibility(View.VISIBLE);
             holder.mSwitchItem.setVisibility(View.GONE);
             holder.mItemContainer.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE);
+            holder.mItemView.setEnabled(enabledState);
         }
 
         private void updateInvisibleActionItemVisibility(@NonNull Context context,
@@ -477,12 +478,13 @@
             holder.mIconView.setColorFilter(null);
             holder.mIconView.setAlpha(ENABLED_ALPHA);
             holder.mLabelView.setEnabled(true);
-            holder.mViewItem.setEnabled(true);
-            holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
-            holder.mViewItem.setVisibility(View.VISIBLE);
+            holder.mActionViewItem.setEnabled(true);
+            holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+            holder.mActionViewItem.setVisibility(View.VISIBLE);
             holder.mSwitchItem.setVisibility(View.GONE);
             holder.mItemContainer.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
                     ? View.VISIBLE : View.GONE);
+            holder.mItemView.setEnabled(true);
         }
 
         private void updateIntuitiveActionItemVisibility(@NonNull Context context,
@@ -495,12 +497,13 @@
             holder.mIconView.setColorFilter(null);
             holder.mIconView.setAlpha(ENABLED_ALPHA);
             holder.mLabelView.setEnabled(true);
-            holder.mViewItem.setEnabled(true);
-            holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
-            holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+            holder.mActionViewItem.setEnabled(true);
+            holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+            holder.mActionViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
             holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
             holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled);
             holder.mItemContainer.setVisibility(View.VISIBLE);
+            holder.mItemView.setEnabled(true);
         }
 
         private void updateBounceActionItemVisibility(@NonNull Context context,
@@ -508,12 +511,13 @@
             holder.mIconView.setColorFilter(null);
             holder.mIconView.setAlpha(ENABLED_ALPHA);
             holder.mLabelView.setEnabled(true);
-            holder.mViewItem.setEnabled(true);
-            holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
-            holder.mViewItem.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
+            holder.mActionViewItem.setEnabled(true);
+            holder.mActionViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+            holder.mActionViewItem.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
                     ? View.VISIBLE : View.GONE);
             holder.mSwitchItem.setVisibility(View.GONE);
             holder.mItemContainer.setVisibility(View.VISIBLE);
+            holder.mItemView.setEnabled(true);
         }
     }
 
@@ -559,8 +563,7 @@
 
     private static boolean isAccessibilityServiceEnabled(@NonNull Context context,
             AccessibilityButtonTarget target) {
-        final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
-                Context.ACCESSIBILITY_SERVICE);
+        final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
         final List<AccessibilityServiceInfo> enabledServices =
                 ams.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
 
@@ -598,8 +601,7 @@
 
     private void onLegacyTargetSelected(AccessibilityButtonTarget target) {
         if (mShortcutType == ACCESSIBILITY_BUTTON) {
-            final AccessibilityManager ams = (AccessibilityManager) getSystemService(
-                    Context.ACCESSIBILITY_SERVICE);
+            final AccessibilityManager ams = getSystemService(AccessibilityManager.class);
             ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
         } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
             switchServiceState(target);
@@ -607,9 +609,12 @@
     }
 
     private void onInvisibleTargetSelected(AccessibilityButtonTarget target) {
-        final AccessibilityManager ams = (AccessibilityManager) getSystemService(
-                Context.ACCESSIBILITY_SERVICE);
-        ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
+        final AccessibilityManager ams = getSystemService(AccessibilityManager.class);
+        if (mShortcutType == ACCESSIBILITY_BUTTON) {
+            ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
+        } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+            ams.performAccessibilityShortcut(target.getId());
+        }
     }
 
     private void onIntuitiveTargetSelected(AccessibilityButtonTarget target) {
diff --git a/core/java/com/android/internal/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java
new file mode 100644
index 0000000..fbdbbfb
--- /dev/null
+++ b/core/java/com/android/internal/app/BlockedAppActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+/**
+ * A dialog shown to the user when they try to launch an app that is not allowed in lock task
+ * mode. The intent to start this activity must be created with the static factory method provided
+ * below.
+ */
+public class BlockedAppActivity extends AlertActivity {
+
+    private static final String TAG = "BlockedAppActivity";
+    private static final String PACKAGE_NAME = "com.android.internal.app";
+    private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+        int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, /* defaultValue= */ -1);
+        if (userId < 0) {
+            Slog.wtf(TAG, "Invalid user: " + userId);
+            finish();
+            return;
+        }
+
+        String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE);
+        if (TextUtils.isEmpty(packageName)) {
+            Slog.wtf(TAG, "Invalid package: " + packageName);
+            finish();
+            return;
+        }
+
+        CharSequence appLabel = getAppLabel(userId, packageName);
+
+        mAlertParams.mTitle = getString(R.string.app_blocked_title);
+        mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
+        mAlertParams.mPositiveButtonText = getString(android.R.string.ok);
+        setupAlert();
+    }
+
+    private CharSequence getAppLabel(int userId, String packageName) {
+        PackageManager pm = getPackageManager();
+        try {
+            ApplicationInfo aInfo =
+                    pm.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId);
+            return aInfo.loadLabel(pm);
+        } catch (PackageManager.NameNotFoundException ne) {
+            Slog.e(TAG, "Package " + packageName + " not found", ne);
+        }
+        return packageName;
+    }
+
+
+    /** Creates an intent that launches {@link BlockedAppActivity}. */
+    public static Intent createIntent(int userId, String packageName) {
+        return new Intent()
+                .setClassName("android", BlockedAppActivity.class.getName())
+                .putExtra(Intent.EXTRA_USER_ID, userId)
+                .putExtra(EXTRA_BLOCKED_PACKAGE, packageName);
+    }
+}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index cec68df..9758673 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -35,8 +35,13 @@
         "android_animation_PropertyValuesHolder.cpp",
         "android_os_SystemClock.cpp",
         "android_os_SystemProperties.cpp",
+        "android_os_Trace.cpp",
+        "android_text_AndroidCharacter.cpp",
         "android_util_EventLog.cpp",
         "android_util_Log.cpp",
+        "android_util_StringBlock.cpp",
+        "android_util_XmlBlock.cpp",
+        "android_view_RenderNodeAnimator.cpp",
         "com_android_internal_util_VirtualRefBasePtr.cpp",
         "com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
     ],
@@ -114,14 +119,12 @@
                 "android_view_KeyEvent.cpp",
                 "android_view_MotionEvent.cpp",
                 "android_view_PointerIcon.cpp",
-                "android_view_RenderNodeAnimator.cpp",
                 "android_view_Surface.cpp",
                 "android_view_SurfaceControl.cpp",
                 "android_graphics_BLASTBufferQueue.cpp",
                 "android_view_SurfaceSession.cpp",
                 "android_view_TextureView.cpp",
                 "android_view_VelocityTracker.cpp",
-                "android_text_AndroidCharacter.cpp",
                 "android_text_Hyphenator.cpp",
                 "android_os_Debug.cpp",
                 "android_os_GraphicsEnvironment.cpp",
@@ -138,7 +141,6 @@
                 "android_os_SELinux.cpp",
                 "android_os_SharedMemory.cpp",
                 "android_os_storage_StorageManager.cpp",
-                "android_os_Trace.cpp",
                 "android_os_UEventObserver.cpp",
                 "android_os_VintfObject.cpp",
                 "android_os_VintfRuntimeInfo.cpp",
@@ -150,8 +152,6 @@
                 "android_util_Binder.cpp",
                 "android_util_MemoryIntArray.cpp",
                 "android_util_Process.cpp",
-                "android_util_StringBlock.cpp",
-                "android_util_XmlBlock.cpp",
                 "android_util_jar_StrictJarFile.cpp",
                 "android_media_AudioDeviceAddress.cpp",
                 "android_media_AudioEffectDescriptor.cpp",
@@ -311,11 +311,8 @@
             srcs: [
                 "android_content_res_ApkAssets.cpp",
                 "android_os_MessageQueue.cpp",
-                "android_os_Trace.cpp",
                 "android_util_AssetManager.cpp",
                 "android_util_FileObserver.cpp",
-                "android_util_StringBlock.cpp",
-                "android_util_XmlBlock.cpp",
             ],
         },
     },
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 481be24..657336e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -634,8 +634,6 @@
     char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
-    char dex2oatXmsFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
-    char dex2oatXmxFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
     char dex2oatCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
     char dex2oatImageCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
     char dex2oatThreadsBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
@@ -885,88 +883,45 @@
     bool skip_compilation = ((strcmp(voldDecryptBuf, "trigger_restart_min_framework") == 0) ||
                              (strcmp(voldDecryptBuf, "1") == 0));
 
-    // Extra options for boot.art/boot.oat image generation.
-    parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf,
-                               "-Xms", "-Ximage-compiler-option");
-    parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf,
-                               "-Xmx", "-Ximage-compiler-option");
-    if (skip_compilation) {
-        addOption("-Ximage-compiler-option");
-        addOption("--compiler-filter=assume-verified");
-    } else {
-        parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf,
-                            "--compiler-filter=", "-Ximage-compiler-option");
-    }
-
-    // If there is a boot profile, it takes precedence over the image and preloaded classes.
-    if (hasFile("/system/etc/boot-image.prof")) {
-        addOption("-Ximage-compiler-option");
-        addOption("--profile-file=/system/etc/boot-image.prof");
-        addOption("-Ximage-compiler-option");
-        addOption("--compiler-filter=speed-profile");
-    } else {
-        ALOGE("Missing boot-image.prof file, /system/etc/boot-image.prof not found: %s\n",
-              strerror(errno));
-        return -1;
-    }
-
-
-    // If there is a dirty-image-objects file, push it.
-    if (hasFile("/system/etc/dirty-image-objects")) {
-        addOption("-Ximage-compiler-option");
-        addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
-    }
-
-    property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
-    parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option");
-
-    // Extra options for DexClassLoader.
-    parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xms", dex2oatXmsFlagsBuf,
-                               "-Xms", "-Xcompiler-option");
-    parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xmx", dex2oatXmxFlagsBuf,
-                               "-Xmx", "-Xcompiler-option");
+    // Extra options for JIT.
     if (skip_compilation) {
         addOption("-Xcompiler-option");
         addOption("--compiler-filter=assume-verified");
-
-        // We skip compilation when a minimal runtime is brought up for decryption. In that case
-        // /data is temporarily backed by a tmpfs, which is usually small.
-        // If the system image contains prebuilts, they will be relocated into the tmpfs. In this
-        // specific situation it is acceptable to *not* relocate and run out of the prebuilts
-        // directly instead.
-        addOption("--runtime-arg");
-        addOption("-Xnorelocate");
     } else {
         parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,
                             "--compiler-filter=", "-Xcompiler-option");
     }
     parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option");
-    parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
-                        "-Ximage-compiler-option");
     parseCompilerOption("dalvik.vm.dex2oat-cpu-set", dex2oatCpuSetBuf, "--cpu-set=",
                         "-Xcompiler-option");
-    parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=",
-                        "-Ximage-compiler-option");
-
-    // The runtime will compile a boot image, when necessary, not using installd. Thus, we need to
-    // pass the instruction-set-features/variant as an image-compiler-option.
-    // Note: it is OK to reuse the buffer, as the values are exactly the same between
-    //       * compiler-option, used for runtime compilation (DexClassLoader)
-    //       * image-compiler-option, used for boot-image compilation on device
 
     // Copy the variant.
     sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", ABI_STRING);
     parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
-                        "--instruction-set-variant=", "-Ximage-compiler-option");
-    parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
                         "--instruction-set-variant=", "-Xcompiler-option");
     // Copy the features.
     sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", ABI_STRING);
     parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
-                        "--instruction-set-features=", "-Ximage-compiler-option");
-    parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
                         "--instruction-set-features=", "-Xcompiler-option");
 
+    /*
+     * When running with debug.generate-debug-info, add --generate-debug-info to
+     * the compiler options so that both JITted code and the boot image extension,
+     * if it is compiled on device, will include native debugging information.
+     */
+    property_get("debug.generate-debug-info", propBuf, "");
+    bool generate_debug_info = (strcmp(propBuf, "true") == 0);
+    if (generate_debug_info) {
+        addOption("-Xcompiler-option");
+        addOption("--generate-debug-info");
+    }
+
+    // The mini-debug-info makes it possible to backtrace through compiled code.
+    bool generate_mini_debug_info = property_get_bool("dalvik.vm.minidebuginfo", 0);
+    if (generate_mini_debug_info) {
+        addOption("-Xcompiler-option");
+        addOption("--generate-mini-debug-info");
+    }
 
     property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
     parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
@@ -975,6 +930,53 @@
     property_get("dalvik.vm.extra-opts", extraOptsBuf, "");
     parseExtraOpts(extraOptsBuf, NULL);
 
+    // Extra options for boot image extension generation.
+    if (skip_compilation) {
+        addOption("-Xnoimage-dex2oat");
+    } else {
+        parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf,
+                                   "-Xms", "-Ximage-compiler-option");
+        parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf,
+                                   "-Xmx", "-Ximage-compiler-option");
+
+        parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf,
+                            "--compiler-filter=", "-Ximage-compiler-option");
+
+        // If there is a dirty-image-objects file, push it.
+        if (hasFile("/system/etc/dirty-image-objects")) {
+            addOption("-Ximage-compiler-option");
+            addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
+        }
+
+        parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
+                            "-Ximage-compiler-option");
+        parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=",
+                            "-Ximage-compiler-option");
+
+        // The runtime may compile a boot image extension, when necessary, not using installd.
+        // Thus, we need to pass the instruction-set-features/variant as an image-compiler-option.
+        // Note: it is OK to reuse the buffer, as the values are exactly the same between
+        //       * compiler-option, used for runtime compilation (DexClassLoader)
+        //       * image-compiler-option, used for boot-image compilation on device
+        parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
+                            "--instruction-set-variant=", "-Ximage-compiler-option");
+        parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
+                            "--instruction-set-features=", "-Ximage-compiler-option");
+
+        if (generate_debug_info) {
+            addOption("-Ximage-compiler-option");
+            addOption("--generate-debug-info");
+        }
+
+        if (generate_mini_debug_info) {
+            addOption("-Ximage-compiler-option");
+            addOption("--generate-mini-debug-info");
+        }
+
+        property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
+        parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option");
+    }
+
     /* Set the properties for locale */
     {
         strcpy(localeOption, "-Duser.locale=");
@@ -1032,25 +1034,6 @@
     parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf,
                        "-Xzygote-max-boot-retry=");
 
-    /*
-     * When running with debug.generate-debug-info, add --generate-debug-info to
-     * the compiler options so that the boot image, if it is compiled on device,
-     * will include native debugging information.
-     */
-    property_get("debug.generate-debug-info", propBuf, "");
-    if (strcmp(propBuf, "true") == 0) {
-        addOption("-Xcompiler-option");
-        addOption("--generate-debug-info");
-        addOption("-Ximage-compiler-option");
-        addOption("--generate-debug-info");
-    }
-
-    // The mini-debug-info makes it possible to backtrace through JIT code.
-    if (property_get_bool("dalvik.vm.minidebuginfo", 0)) {
-        addOption("-Xcompiler-option");
-        addOption("--generate-mini-debug-info");
-    }
-
     // If set, the property below can be used to enable core platform API violation reporting.
     property_get("persist.debug.dalvik.vm.core_platform_api_policy", propBuf, "");
     if (propBuf[0] != '\0') {
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 6c0680f..571a3387 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -75,10 +75,12 @@
 extern int register_android_os_SystemClock(JNIEnv* env);
 extern int register_android_os_SystemProperties(JNIEnv* env);
 extern int register_android_os_Trace(JNIEnv* env);
+extern int register_android_text_AndroidCharacter(JNIEnv* env);
 extern int register_android_util_EventLog(JNIEnv* env);
 extern int register_android_util_Log(JNIEnv* env);
 extern int register_android_util_PathParser(JNIEnv* env);
 extern int register_android_view_RenderNode(JNIEnv* env);
+extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
 extern int register_android_view_DisplayListCanvas(JNIEnv* env);
 extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
 extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
@@ -90,58 +92,66 @@
 
 // Map of all possible class names to register to their corresponding JNI registration function pointer
 // The actual list of registered classes will be determined at runtime via the 'native_classes' System property
-static const std::unordered_map<std::string, RegJNIRec>  gRegJNIMap = {
-    {"android.animation.PropertyValuesHolder", REG_JNI(register_android_animation_PropertyValuesHolder)},
+static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
+        {"android.animation.PropertyValuesHolder",
+         REG_JNI(register_android_animation_PropertyValuesHolder)},
 #ifdef __linux__
-    {"android.content.AssetManager", REG_JNI(register_android_content_AssetManager)},
-    {"android.content.StringBlock", REG_JNI(register_android_content_StringBlock)},
-    {"android.content.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
-    {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
+        {"android.content.res.ApkAssets", REG_JNI(register_android_content_res_ApkAssets)},
+        {"android.content.res.AssetManager", REG_JNI(register_android_content_AssetManager)},
 #endif
-    {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)},
-    {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
-    {"android.graphics.ByteBufferStreamAdaptor", REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
-    {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
-    {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
-    {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
-    {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
-    {"android.graphics.CreateJavaOutputStreamAdaptor", REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)},
-    {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)},
-    {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)},
-    {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)},
-    {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)},
-    {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)},
-    {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)},
-    {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)},
-    {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)},
-    {"android.graphics.Path", REG_JNI(register_android_graphics_Path)},
-    {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)},
-    {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
-    {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
-    {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
-    {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
-    {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
-    {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
-    {"android.graphics.drawable.AnimatedVectorDrawable", REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)},
-    {"android.graphics.drawable.VectorDrawable", REG_JNI(register_android_graphics_drawable_VectorDrawable)},
-    {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)},
-    {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)},
-    {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
-    {"android.graphics.text.MeasuredText", REG_JNI(register_android_graphics_text_MeasuredText)},
+        {"android.content.res.StringBlock", REG_JNI(register_android_content_StringBlock)},
+        {"android.content.res.XmlBlock", REG_JNI(register_android_content_XmlBlock)},
+        {"android.graphics.Bitmap", REG_JNI(register_android_graphics_Bitmap)},
+        {"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
+        {"android.graphics.ByteBufferStreamAdaptor",
+         REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
+        {"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
+        {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
+        {"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
+        {"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
+        {"android.graphics.CreateJavaOutputStreamAdaptor",
+         REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor)},
+        {"android.graphics.DrawFilter", REG_JNI(register_android_graphics_DrawFilter)},
+        {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)},
+        {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)},
+        {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)},
+        {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)},
+        {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)},
+        {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)},
+        {"android.graphics.Paint", REG_JNI(register_android_graphics_Paint)},
+        {"android.graphics.Path", REG_JNI(register_android_graphics_Path)},
+        {"android.graphics.PathEffect", REG_JNI(register_android_graphics_PathEffect)},
+        {"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
+        {"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
+        {"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
+        {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
+        {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
+        {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
+        {"android.graphics.drawable.AnimatedVectorDrawable",
+         REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)},
+        {"android.graphics.drawable.VectorDrawable",
+         REG_JNI(register_android_graphics_drawable_VectorDrawable)},
+        {"android.graphics.fonts.Font", REG_JNI(register_android_graphics_fonts_Font)},
+        {"android.graphics.fonts.FontFamily", REG_JNI(register_android_graphics_fonts_FontFamily)},
+        {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
+        {"android.graphics.text.MeasuredText",
+         REG_JNI(register_android_graphics_text_MeasuredText)},
 #ifdef __linux__
-    {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
-    {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
+        {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
+        {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
 #endif
-    {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
-    {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
-#ifdef __linux__
-    {"android.os.Trace", REG_JNI(register_android_os_Trace)},
-#endif
-    {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
-    {"android.util.Log", REG_JNI(register_android_util_Log)},
-    {"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
-    {"com.android.internal.util.VirtualRefBasePtr", REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
-    {"com.android.internal.view.animation.NativeInterpolatorFactoryHelper", REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
+        {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
+        {"android.os.SystemProperties", REG_JNI(register_android_os_SystemProperties)},
+        {"android.os.Trace", REG_JNI(register_android_os_Trace)},
+        {"android.text.AndroidCharacter", REG_JNI(register_android_text_AndroidCharacter)},
+        {"android.util.EventLog", REG_JNI(register_android_util_EventLog)},
+        {"android.util.Log", REG_JNI(register_android_util_Log)},
+        {"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
+        {"android.view.RenderNodeAnimator", REG_JNI(register_android_view_RenderNodeAnimator)},
+        {"com.android.internal.util.VirtualRefBasePtr",
+         REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
+        {"com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
+         REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
 };
 // Vector to store the names of classes that need delegates of their native methods
 static vector<string> classesToDelegate;
@@ -159,7 +169,6 @@
 
 int AndroidRuntime::registerNativeMethods(JNIEnv* env,
         const char* className, const JNINativeMethod* gMethods, int numMethods) {
-
     string classNameString = string(className);
     if (find(classesToDelegate.begin(), classesToDelegate.end(), classNameString)
             != classesToDelegate.end()) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 753d64a..7a30256 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2603,7 +2603,7 @@
 
     <!-- Allows telephony to suggest the time / time zone.
          <p>Not for use by third-party applications.
-         @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @hide
+         @hide
      -->
     <permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"
         android:protectionLevel="signature|telephony" />
@@ -5075,6 +5075,12 @@
                 android:process=":ui">
         </activity>
 
+        <activity android:name="com.android.internal.app.BlockedAppActivity"
+                android:theme="@style/Theme.Dialog.Confirmation"
+                android:excludeFromRecents="true"
+                android:process=":ui">
+        </activity>
+
         <activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
                   android:theme="@style/Theme.Dialog.Confirmation"
                   android:excludeFromRecents="true">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3d7b1e1..b2d08a9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3560,25 +3560,16 @@
     -->
     <string name="config_defaultAutofillService" translatable="false"></string>
 
-    <!-- The package name for the default system textclassifier service.
+    <!-- The package name for the OEM custom system textclassifier service.
          This service must be trusted, as it can be activated without explicit consent of the user.
          Example: "com.android.textclassifier"
-         If no textclassifier service with the specified name exists on the device (or if this is
-         set to empty string), a default textclassifier will be loaded in the calling app's process.
+         If this is empty, the default textclassifier service (i.e. config_servicesExtensionPackage)
+         will be used.
+
          See android.view.textclassifier.TextClassificationManager.
     -->
-    <!-- TODO(b/144896755) remove the config -->
     <string name="config_defaultTextClassifierPackage" translatable="false"></string>
 
-    <!-- A list of supported system textClassifier service package names. Only one of the packages
-         will be activated. The first package in the list is the default system textClassifier
-         service. OS only tries to bind and grant permissions to the first trusted service and the
-         others can be selected via device config. These services must be trusted, as they can be
-         activated without explicit consent of the user. Example: "com.android.textclassifier"
-    -->
-    <string-array name="config_defaultTextClassifierPackages" translatable="false">
-        <item>android.ext.services</item>
-    </string-array>
 
     <!-- The package name for the system companion device manager service.
          This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 11cc365..a54566c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4936,6 +4936,13 @@
     <!-- Title for button to turn on work profile. [CHAR LIMIT=NONE] -->
     <string name="work_mode_turn_on">Turn on</string>
 
+    <!-- Title of the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=50] -->
+    <string name="app_blocked_title">App is not available</string>
+    <!-- Default message shown in the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=NONE] -->
+    <string name="app_blocked_message">
+        <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now.
+    </string>
+
     <!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] -->
     <string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string>
     <!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 36296a8..c59d25f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3048,6 +3048,9 @@
   <java-symbol type="string" name="app_suspended_unsuspend_message" />
   <java-symbol type="string" name="app_suspended_default_message" />
 
+  <java-symbol type="string" name="app_blocked_title" />
+  <java-symbol type="string" name="app_blocked_message" />
+
   <!-- Used internally for assistant to launch activity transitions -->
   <java-symbol type="id" name="cross_task_transition" />
 
@@ -3390,7 +3393,6 @@
   <java-symbol type="string" name="notification_channel_do_not_disturb" />
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
-  <java-symbol type="array" name="config_defaultTextClassifierPackages" />
   <java-symbol type="string" name="config_defaultWellbeingPackage" />
   <java-symbol type="string" name="config_telephonyPackages" />
   <java-symbol type="string" name="config_defaultContentCaptureService" />
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index 6cf6a82..9110661 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,9 +18,10 @@
 -->
 
 <!-- Default configuration for zen mode.  See android.service.notification.ZenModeConfig. -->
-<zen version="8">
+<zen version="9">
     <allow alarms="true" media="true" system="false" calls="true" callsFrom="2" messages="false"
-           reminders="false" events="false" repeatCallers="true" />
+            reminders="false" events="false" repeatCallers="true" conversations="true"
+            conversationsFrom="2"/>
     <automatic ruleId="EVENTS_DEFAULT_RULE" enabled="false" snoozing="false" name="Event" zen="1"
                component="android/com.android.server.notification.EventConditionProvider"
                conditionId="condition://android/event?userId=-10000&amp;calendar=&amp;reply=1"/>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 2d1c61c..0d7ac08 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -39,6 +39,9 @@
     <!-- Albania: 5 digits, known short codes listed -->
     <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
 
+    <!-- Argentia: 5 digits, known short codes listed -->
+    <shortcode country="ar" pattern="\\d{5}" free="11711|28291" />
+
     <!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
     <shortcode country="am" pattern="\\d{3,4}" premium="11[2456]1|3024" free="10[123]" />
 
@@ -80,7 +83,7 @@
     <shortcode country="cn" premium="1066.*" free="1065.*" />
 
     <!-- Colombia: 1-6 digits (not confirmed) -->
-    <shortcode country="co" pattern="\\d{1,6}" free="890350|908160" />
+    <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960" />
 
     <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
     <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 8faf790..1ca4649 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -25,7 +25,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.LocaleList;
-import android.service.textclassifier.TextClassifierService;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -64,9 +63,7 @@
 
     @Test
     public void testGetSystemTextClassifier() {
-        assertTrue(
-                TextClassifierService.getServiceComponentName(mContext) == null
-                || mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
+        assertTrue(mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 2304ba6..372a478 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -27,6 +27,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.LocaleList;
+import android.service.textclassifier.TextClassifierService;
 import android.text.Spannable;
 import android.text.SpannableString;
 
@@ -58,11 +59,12 @@
 public class TextClassifierTest {
     private static final String LOCAL = "local";
     private static final String SESSION = "session";
+    private static final String DEFAULT = "default";
 
     // TODO: Add SYSTEM, which tests TextClassifier.SYSTEM.
     @Parameterized.Parameters(name = "{0}")
     public static Iterable<Object> textClassifierTypes() {
-        return Arrays.asList(LOCAL, SESSION);
+        return Arrays.asList(LOCAL, SESSION, DEFAULT);
     }
 
     @Parameterized.Parameter
@@ -84,13 +86,15 @@
 
         if (mTextClassifierType.equals(LOCAL)) {
             mClassifier = mTcm.getTextClassifier(TextClassifier.LOCAL);
-        } else {
+        } else if (mTextClassifierType.equals(SESSION)) {
             mClassifier = mTcm.createTextClassificationSession(
                     new TextClassificationContext.Builder(
                             "android",
                             TextClassifier.WIDGET_TYPE_NOTIFICATION)
                             .build(),
                     mTcm.getTextClassifier(TextClassifier.LOCAL));
+        } else {
+            mClassifier = TextClassifierService.getDefaultTextClassifierImplementation(mContext);
         }
     }
 
@@ -369,16 +373,14 @@
                 mClassifier.generateLinks(request).apply(url, 0, null));
     }
 
-
-    @Test(expected = IllegalArgumentException.class)
+    @Test
     public void testGenerateLinks_tooLong() {
-        if (isTextClassifierDisabled()) {
-            throw new IllegalArgumentException("pass if disabled");
-        }
+        if (isTextClassifierDisabled()) return;
         char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
         Arrays.fill(manySpaces, ' ');
         TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
-        mClassifier.generateLinks(request);
+        TextLinks links = mClassifier.generateLinks(request);
+        assertTrue(links.getLinks().isEmpty());
     }
 
     @Test
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index dcc6b95..1290633 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -38,6 +38,10 @@
         ICredentialStoreFactory storeFactory =
                 ICredentialStoreFactory.Stub.asInterface(
                     ServiceManager.getService("android.security.identity"));
+        if (storeFactory == null) {
+            // This can happen if credstore is not running or not installed.
+            return null;
+        }
 
         ICredentialStore credStore = null;
         try {
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index f3613d3..42841d1 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -229,13 +230,19 @@
      * <p>This information is only available if the caller has the
      * {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING}
      * permission.
-     * <br>The result is -1 without the permission.
      * @return the user id
+     * @throws SecurityException Thrown if the caller is missing the MODIFY_AUDIO_ROUTING permission
      *
      * @hide
      */
     @SystemApi
-    public int getClientUid() { return mClientUid; }
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public int getClientUid() {
+        if (mClientUid == -1) {
+            throw new SecurityException("MODIFY_AUDIO_ROUTING permission is missing");
+        }
+        return mClientUid;
+    }
 
     /**
      * Returns information about the audio input device used for this recording.
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index a25aff6..194669c 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.media.IMediaRoute2ProviderClient;
+import android.media.RouteDiscoveryPreference;
 import android.os.Bundle;
 
 /**
@@ -28,6 +29,7 @@
     void requestCreateSession(String packageName, String routeId, long requestId,
             in @nullable Bundle sessionHints);
     void releaseSession(String sessionId);
+    void updateDiscoveryPreference(in RouteDiscoveryPreference discoveryPreference);
 
     void selectRoute(String sessionId, String routeId);
     void deselectRoute(String sessionId, String routeId);
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index e39b7bc..d0b5d48 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -370,7 +370,6 @@
      *
      * @param preference the new discovery preference
      */
-    // TODO: This method needs tests.
     public void onDiscoveryPreferenceChanged(@NonNull RouteDiscoveryPreference preference) {}
 
     /**
@@ -456,6 +455,16 @@
         }
 
         @Override
+        public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
+            mHandler.sendMessage(obtainMessage(
+                    MediaRoute2ProviderService::onDiscoveryPreferenceChanged,
+                    MediaRoute2ProviderService.this, discoveryPreference));
+        }
+
+        @Override
         public void selectRoute(@NonNull String sessionId, String routeId) {
             if (!checkCallerisSystem()) {
                 return;
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index f751a22..515cabe 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -182,11 +182,16 @@
                 Client2 client = new Client2();
                 try {
                     mMediaRouterService.registerClient2(client, mPackageName);
-                    updateDiscoveryRequestLocked();
-                    mMediaRouterService.setDiscoveryRequest2(client, mDiscoveryPreference);
                     mClient = client;
                 } catch (RemoteException ex) {
-                    Log.e(TAG, "Unable to register media router.", ex);
+                    Log.e(TAG, "registerRouteCallback: Unable to register client.", ex);
+                }
+            }
+            if (mClient != null && updateDiscoveryPreferenceIfNeededLocked()) {
+                try {
+                    mMediaRouterService.setDiscoveryRequest2(mClient, mDiscoveryPreference);
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "registerRouteCallback: Unable to set discovery request.");
                 }
             }
         }
@@ -209,22 +214,37 @@
         }
 
         synchronized (sRouterLock) {
-            if (mRouteCallbackRecords.size() == 0 && mClient != null) {
-                try {
-                    mMediaRouterService.unregisterClient2(mClient);
-                } catch (RemoteException ex) {
-                    Log.e(TAG, "Unable to unregister media router.", ex);
+            if (mClient != null) {
+                if (updateDiscoveryPreferenceIfNeededLocked()) {
+                    try {
+                        mMediaRouterService.setDiscoveryRequest2(mClient, mDiscoveryPreference);
+                    } catch (RemoteException ex) {
+                        Log.e(TAG, "unregisterRouteCallback: Unable to set discovery request.");
+                    }
                 }
-                //TODO: Clean up mRoutes. (onHandler?)
+                if (mRouteCallbackRecords.size() == 0) {
+                    try {
+                        mMediaRouterService.unregisterClient2(mClient);
+                    } catch (RemoteException ex) {
+                        Log.e(TAG, "Unable to unregister media router.", ex);
+                    }
+                }
+                mShouldUpdateRoutes = true;
                 mClient = null;
             }
         }
     }
 
-    private void updateDiscoveryRequestLocked() {
-        mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
+    private boolean updateDiscoveryPreferenceIfNeededLocked() {
+        RouteDiscoveryPreference newDiscoveryPreference = new RouteDiscoveryPreference.Builder(
                 mRouteCallbackRecords.stream().map(record -> record.mPreference).collect(
                         Collectors.toList())).build();
+        if (Objects.equals(mDiscoveryPreference, newDiscoveryPreference)) {
+            return false;
+        }
+        mDiscoveryPreference = newDiscoveryPreference;
+        mShouldUpdateRoutes = true;
+        return true;
     }
 
     /**
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index 7ec1123..ebcb9ed 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -31,7 +31,7 @@
 import java.util.Set;
 
 /**
- * A media route discovery preference  describing the kinds of routes that media router
+ * A media route discovery preference describing the kinds of routes that media router
  * would like to discover and whether to perform active scanning.
  *
  * @see MediaRouter2#registerRouteCallback
@@ -58,6 +58,7 @@
     private final Bundle mExtras;
 
     /**
+     * An empty discovery preference.
      * @hide
      */
     public static final RouteDiscoveryPreference EMPTY =
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index e29b323..f1a08f2 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -155,7 +155,6 @@
 
     @Override
     public void onUpdateVolume(String routeId, int delta) {
-        android.util.Log.d(TAG, "onUpdateVolume routeId= " + routeId + "delta=" + delta);
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null) {
             return;
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 82cd5f7..07a926f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1812,16 +1812,16 @@
     <string name="demote">Mark this notification as not a conversation</string>
 
     <!-- [CHAR LIMIT=100] Mark this conversation as a favorite -->
-    <string name="notification_conversation_favorite">Favorite</string>
+    <string name="notification_conversation_favorite">Mark as important</string>
 
     <!-- [CHAR LIMIT=100] Unmark this conversation as a favorite -->
-    <string name="notification_conversation_unfavorite">Unfavorite</string>
+    <string name="notification_conversation_unfavorite">Mark as unimportant</string>
 
     <!-- [CHAR LIMIT=100] Mute this conversation -->
-    <string name="notification_conversation_mute">Mute</string>
+    <string name="notification_conversation_mute">Silence</string>
 
     <!-- [CHAR LIMIT=100] Umute this conversation -->
-    <string name="notification_conversation_unmute">Unmute</string>
+    <string name="notification_conversation_unmute">Alerting</string>
 
     <!-- [CHAR LIMIT=100] Show notification as bubble -->
     <string name="notification_conversation_bubble">Show as bubble</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 6045524..bb0681c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -280,7 +280,7 @@
 
         Button favorite = findViewById(R.id.fave);
         favorite.setOnClickListener(mOnFavoriteClick);
-        if (mNotificationChannel.canBypassDnd()) {
+        if (mNotificationChannel.isImportantConversation()) {
             favorite.setText(R.string.notification_conversation_unfavorite);
             favorite.setCompoundDrawablesRelative(
                     mContext.getDrawable(R.drawable.ic_star), null, null, null);
@@ -621,8 +621,8 @@
                         }
                         break;
                     case ACTION_FAVORITE:
-                        // TODO: extend beyond DND
-                        mChannelToUpdate.setBypassDnd(!mChannelToUpdate.canBypassDnd());
+                        mChannelToUpdate.setImportantConversation(
+                                !mChannelToUpdate.isImportantConversation());
                         break;
                     case ACTION_MUTE:
                         if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 20a089f..f080d67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -655,13 +655,13 @@
                 ArgumentCaptor.forClass(NotificationChannel.class);
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 anyString(), anyInt(), captor.capture());
-        assertTrue(captor.getValue().canBypassDnd());
+        assertTrue(captor.getValue().isImportantConversation());
     }
 
     @Test
     public void testFavorite_unfavorite() throws Exception {
-        mNotificationChannel.setBypassDnd(true);
-        mConversationChannel.setBypassDnd(true);
+        mNotificationChannel.setImportantConversation(true);
+        mConversationChannel.setImportantConversation(true);
 
         mNotificationInfo.bindNotification(
                 mShortcutManager,
@@ -688,7 +688,7 @@
                 ArgumentCaptor.forClass(NotificationChannel.class);
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 anyString(), anyInt(), captor.capture());
-        assertFalse(captor.getValue().canBypassDnd());
+        assertFalse(captor.getValue().isImportantConversation());
     }
 
     @Test
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1a4fc32..1cb9313 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -818,27 +818,26 @@
         }
     }
 
-    void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
-            @Nullable Bundle clientState) {
+    void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId) {
         synchronized (mLock) {
             if (mAugmentedAutofillEventHistory == null
                     || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
                 return;
             }
             mAugmentedAutofillEventHistory.addEvent(
-                    new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null,
+                    new Event(Event.TYPE_DATASET_SELECTED, suggestionId, null, null, null,
                             null, null, null, null, null, null));
         }
     }
 
-    void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState) {
+    void logAugmentedAutofillShown(int sessionId) {
         synchronized (mLock) {
             if (mAugmentedAutofillEventHistory == null
                     || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
                 return;
             }
             mAugmentedAutofillEventHistory.addEvent(
-                    new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
+                    new Event(Event.TYPE_DATASETS_SHOWN, null, null, null, null, null,
                             null, null, null, null, null));
 
         }
@@ -1227,16 +1226,15 @@
                         }
 
                         @Override
-                        public void logAugmentedAutofillShown(int sessionId, Bundle clientState) {
-                            AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId,
-                                    clientState);
+                        public void logAugmentedAutofillShown(int sessionId) {
+                            AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId);
                         }
 
                         @Override
-                        public void logAugmentedAutofillSelected(int sessionId, String suggestionId,
-                                Bundle clientState) {
+                        public void logAugmentedAutofillSelected(int sessionId,
+                                String suggestionId) {
                             AutofillManagerServiceImpl.this.logAugmentedAutofillSelected(sessionId,
-                                    suggestionId, clientState);
+                                    suggestionId);
                         }
 
                         @Override
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 5e6f6fea..880c401 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -55,6 +55,7 @@
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.view.IInlineSuggestionsResponseCallback;
+import com.android.server.autofill.ui.InlineSuggestionFactory;
 
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.TimeUnit;
@@ -144,7 +145,8 @@
             int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
             @Nullable AutofillValue focusedValue,
             @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
-            @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback) {
+            @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
+            @NonNull Runnable onErrorCallback) {
         long requestTime = SystemClock.elapsedRealtime();
         AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>();
 
@@ -161,12 +163,12 @@
                             focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
                             new IFillCallback.Stub() {
                                 @Override
-                                public void onSuccess(@Nullable Dataset[] inlineSuggestionsData,
-                                        @Nullable Bundle clientState) {
+                                public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
                                     mCallbacks.resetLastResponse();
                                     maybeRequestShowInlineSuggestions(sessionId,
                                             inlineSuggestionsData, focusedId,
-                                            inlineSuggestionsCallback, client, clientState);
+                                            inlineSuggestionsCallback, client,
+                                            onErrorCallback);
                                     requestAutofill.complete(null);
                                 }
 
@@ -231,29 +233,31 @@
     private void maybeRequestShowInlineSuggestions(int sessionId,
             @Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId,
             @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
-            @NonNull IAutoFillManagerClient client, @Nullable Bundle clientState) {
+            @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback) {
         if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null) {
             return;
         }
         mCallbacks.setLastResponse(sessionId);
+
         try {
             inlineSuggestionsCallback.onInlineSuggestionsResponse(
                     InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
                             inlineSuggestionsData, focusedId, mContext,
                             dataset -> {
                                 mCallbacks.logAugmentedAutofillSelected(sessionId,
-                                        dataset.getId(), clientState);
+                                        dataset.getId());
                                 try {
                                     client.autofill(sessionId, dataset.getFieldIds(),
                                             dataset.getFieldValues());
                                 } catch (RemoteException e) {
                                     Slog.w(TAG, "Encounter exception autofilling the values");
                                 }
-                            }));
+                            }, onErrorCallback));
         } catch (RemoteException e) {
             Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
         }
-        mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
+
+        mCallbacks.logAugmentedAutofillShown(sessionId);
     }
 
     @Override
@@ -275,9 +279,8 @@
 
         void setLastResponse(int sessionId);
 
-        void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState);
+        void logAugmentedAutofillShown(int sessionId);
 
-        void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
-                @Nullable Bundle clientState);
+        void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId);
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 415ecd8..7e5123c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -103,6 +103,7 @@
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
 import com.android.internal.view.IInlineSuggestionsResponseCallback;
 import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.autofill.ui.InlineSuggestionFactory;
 import com.android.server.autofill.ui.PendingUi;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 
@@ -2681,7 +2682,6 @@
             }
         }
 
-
         getUiForShowing().showFillUi(filledId, response, filterText,
                 mService.getServicePackageName(), mComponentName,
                 serviceLabel, serviceIcon, this, id, mCompatMode);
@@ -2733,7 +2733,11 @@
 
         InlineSuggestionsResponse inlineSuggestionsResponse =
                 InlineSuggestionFactory.createInlineSuggestionsResponse(response.getRequestId(),
-                        datasets.toArray(new Dataset[]{}), mCurrentViewId, mContext, this);
+                        datasets.toArray(new Dataset[]{}), mCurrentViewId, mContext, this, () -> {
+                    synchronized (mLock) {
+                        requestHideFillUi(mCurrentViewId);
+                    }
+                });
         try  {
             inlineContentCallback.onInlineSuggestionsResponse(inlineSuggestionsResponse);
         } catch (RemoteException e) {
@@ -3024,7 +3028,11 @@
                 mInlineSuggestionsRequestCallback != null
                         ? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
         remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
-                currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback);
+                currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback, () -> {
+                    synchronized (mLock) {
+                        cancelAugmentedAutofillLocked();
+                    }
+                });
 
         if (mAugmentedAutofillDestroyer == null) {
             mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
similarity index 80%
rename from services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
rename to services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index cb6c8f5..38a5b5b 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.autofill;
+package com.android.server.autofill.ui;
 
 import static com.android.server.autofill.Helper.sDebug;
 
@@ -32,18 +32,13 @@
 import android.view.inputmethod.InlineSuggestionInfo;
 import android.view.inputmethod.InlineSuggestionsResponse;
 
+import com.android.internal.util.function.QuadFunction;
 import com.android.internal.view.inline.IInlineContentCallback;
 import com.android.internal.view.inline.IInlineContentProvider;
 import com.android.server.UiThread;
-import com.android.server.autofill.ui.AutoFillUI;
-import com.android.server.autofill.ui.InlineSuggestionUi;
 
 import java.util.ArrayList;
 
-
-/**
- * @hide
- */
 public final class InlineSuggestionFactory {
     private static final String TAG = "InlineSuggestionFactory";
 
@@ -65,28 +60,12 @@
             @NonNull Dataset[] datasets,
             @NonNull AutofillId autofillId,
             @NonNull Context context,
-            @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
-        if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
-
-        final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
-        final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context);
-        for (Dataset dataset : datasets) {
-            final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
-            if (fieldIndex < 0) {
-                Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
-                return null;
-            }
-            final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(
-                    fieldIndex);
-            if (inlinePresentation == null) {
-                Slog.w(TAG, "InlinePresentation not found in dataset");
-                return null;
-            }
-            InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(dataset,
-                    inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback);
-            inlineSuggestions.add(inlineSuggestion);
-        }
-        return new InlineSuggestionsResponse(inlineSuggestions);
+            @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
+            @NonNull Runnable onErrorCallback) {
+        return createInlineSuggestionsResponseInternal(datasets, autofillId,
+                context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
+                        filedIndex) -> createAugmentedInlineSuggestion(dataset,
+                    inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback));
     }
 
     /**
@@ -97,11 +76,26 @@
             @NonNull Dataset[] datasets,
             @NonNull AutofillId autofillId,
             @NonNull Context context,
-            @NonNull AutoFillUI.AutoFillUiCallback client) {
-        if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
+            @NonNull AutoFillUI.AutoFillUiCallback client,
+            @NonNull Runnable onErrorCallback) {
+        return createInlineSuggestionsResponseInternal(datasets, autofillId,
+                context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
+                        filedIndex) -> createInlineSuggestion(requestId, dataset, filedIndex,
+                        inlinePresentation, inlineSuggestionUi, client));
+    }
+
+    private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
+            @NonNull Dataset[] datasets,
+            @NonNull AutofillId autofillId,
+            @NonNull Context context,
+            @NonNull Runnable onErrorCallback,
+            @NonNull QuadFunction<Dataset, InlinePresentation, InlineSuggestionUi,
+                    Integer, InlineSuggestion> suggestionFactory) {
+        if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
 
         final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
-        final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context);
+        final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
+                onErrorCallback);
         for (Dataset dataset : datasets) {
             final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
             if (fieldIndex < 0) {
@@ -114,9 +108,8 @@
                 Slog.w(TAG, "InlinePresentation not found in dataset");
                 return null;
             }
-            InlineSuggestion inlineSuggestion = createInlineSuggestion(requestId, dataset,
-                    fieldIndex,
-                    inlinePresentation, inlineSuggestionUi, client);
+            InlineSuggestion inlineSuggestion = suggestionFactory.apply(dataset,
+                    inlinePresentation, inlineSuggestionUi, fieldIndex);
             inlineSuggestions.add(inlineSuggestion);
         }
         return new InlineSuggestionsResponse(inlineSuggestions);
@@ -131,9 +124,8 @@
                 inlinePresentation.getInlinePresentationSpec(),
                 InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""},
                 InlineSuggestionInfo.TYPE_SUGGESTION);
-        final View.OnClickListener onClickListener = v -> {
+        final View.OnClickListener onClickListener = v ->
             inlineSuggestionUiCallback.autofill(dataset);
-        };
         final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
                 createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
                         onClickListener));
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
new file mode 100644
index 0000000..8d476d7
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill.ui;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.widget.FrameLayout;
+
+import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerInternal;
+
+/**
+ * This class is the root view for an inline suggestion. It is responsible for
+ * detecting the click on the item and to also transfer input focus to the IME
+ * window if we detect the user is scrolling.
+ */
+ // TODO(b/146453086) Move to ExtServices and add @SystemApi to transfer touch focus
+@SuppressLint("ViewConstructor")
+class InlineSuggestionRoot extends FrameLayout {
+    private static final String LOG_TAG = InlineSuggestionRoot.class.getSimpleName();
+
+    private final @NonNull Runnable mOnErrorCallback;
+    private final int mTouchSlop;
+
+    private float mDownX;
+    private float mDownY;
+
+    InlineSuggestionRoot(@NonNull Context context, @NonNull Runnable onErrorCallback) {
+        super(context);
+        mOnErrorCallback = onErrorCallback;
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+    }
+
+    @Override
+    @SuppressLint("ClickableViewAccessibility")
+    public boolean onTouchEvent(@NonNull MotionEvent event) {
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN: {
+                mDownX = event.getX();
+                mDownY = event.getY();
+            } break;
+
+            case MotionEvent.ACTION_MOVE: {
+                final float distance = MathUtils.dist(mDownX, mDownY,
+                        event.getX(), event.getY());
+                if (distance > mTouchSlop) {
+                    transferTouchFocusToImeWindow();
+                }
+            } break;
+        }
+        return super.onTouchEvent(event);
+    }
+
+    private void transferTouchFocusToImeWindow() {
+        final WindowManagerInternal windowManagerInternal = LocalServices.getService(
+                WindowManagerInternal.class);
+        if (!windowManagerInternal.transferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
+                getContext().getDisplayId())) {
+            Log.e(LOG_TAG, "Cannot transfer touch focus from suggestion to IME");
+            mOnErrorCallback.run();
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
index 2adefea..bf148a6 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
@@ -67,10 +67,12 @@
     // (int)}. This name is a single string of the form "package:type/entry".
     private static final Pattern RESOURCE_NAME_PATTERN = Pattern.compile("([^:]+):([^/]+)/(\\S+)");
 
-    private final Context mContext;
+    private final @NonNull Context mContext;
+    private final @NonNull Runnable mOnErrorCallback;
 
-    public InlineSuggestionUi(Context context) {
+    InlineSuggestionUi(@NonNull Context context, @NonNull Runnable onErrorCallback) {
         this.mContext = context;
+        mOnErrorCallback = onErrorCallback;
     }
 
     /**
@@ -94,15 +96,17 @@
         }
         final View suggestionView = renderSlice(inlinePresentation.getSlice(),
                 contextThemeWrapper);
-        if (onClickListener != null) {
-            suggestionView.setOnClickListener(onClickListener);
-        }
+
+        final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(
+                mContext, mOnErrorCallback);
+        suggestionRoot.addView(suggestionView);
+        suggestionRoot.setOnClickListener(onClickListener);
 
         WindowManager.LayoutParams lp =
                 new WindowManager.LayoutParams(width, height,
                         WindowManager.LayoutParams.TYPE_APPLICATION, 0,
                         PixelFormat.TRANSPARENT);
-        wvr.addView(suggestionView, lp);
+        wvr.addView(suggestionRoot, lp);
         return sc;
     }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ce5e241..ec3dbe9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -48,8 +48,11 @@
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 
+import static java.util.Map.Entry;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -62,6 +65,8 @@
 import android.database.ContentObserver;
 import android.net.CaptivePortal;
 import android.net.ConnectionInfo;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import android.net.ConnectivityManager;
 import android.net.ICaptivePortal;
 import android.net.IConnectivityDiagnosticsCallback;
@@ -130,6 +135,7 @@
 import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -170,6 +176,7 @@
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.LocationPermissionChecker;
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
@@ -492,9 +499,9 @@
      /**
       * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
       * been tested.
-      * obj = String representing URL that Internet probe was redirect to, if it was redirected.
-      * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
-      * arg2 = NetID.
+      * obj = {@link NetworkTestedResults} representing information sent from NetworkMonitor.
+      * data = PersistableBundle of extras passed from NetworkMonitor. If {@link
+      * NetworkMonitorCallbacks#notifyNetworkTested} is called, this will be null.
       */
     private static final int EVENT_NETWORK_TESTED = 41;
 
@@ -596,6 +603,9 @@
     private Set<String> mWolSupportedInterfaces;
 
     private TelephonyManager mTelephonyManager;
+    private final AppOpsManager mAppOpsManager;
+
+    private final LocationPermissionChecker mLocationPermissionChecker;
 
     private KeepaliveTracker mKeepaliveTracker;
     private NetworkNotificationManager mNotifier;
@@ -992,6 +1002,8 @@
         mNetd = netd;
         mKeyStore = KeyStore.getInstance();
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mLocationPermissionChecker = new LocationPermissionChecker(mContext);
 
         // To ensure uid rules are synchronized with Network Policy, register for
         // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
@@ -2101,6 +2113,12 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
+    private boolean checkNetworkStackPermission(int pid, int uid) {
+        return checkAnyPermissionOf(pid, uid,
+                android.Manifest.permission.NETWORK_STACK,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+    }
+
     private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
         return checkAnyPermissionOf(pid, uid,
                 android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
@@ -2747,88 +2765,21 @@
                     break;
                 }
                 case EVENT_NETWORK_TESTED: {
-                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+                    final NetworkTestedResults results = (NetworkTestedResults) msg.obj;
+
+                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(results.mNetId);
                     if (nai == null) break;
 
-                    final boolean wasPartial = nai.partialConnectivity;
-                    nai.partialConnectivity = ((msg.arg1 & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
-                    final boolean partialConnectivityChanged =
-                            (wasPartial != nai.partialConnectivity);
+                    handleNetworkTested(nai, results.mTestResult,
+                            (results.mRedirectUrl == null) ? "" : results.mRedirectUrl);
 
-                    final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0);
-                    final boolean wasValidated = nai.lastValidated;
-                    final boolean wasDefault = isDefaultNetwork(nai);
-                    // Only show a connected notification if the network is pending validation
-                    // after the captive portal app was open, and it has now validated.
-                    if (nai.captivePortalValidationPending && valid) {
-                        // User is now logged in, network validated.
-                        nai.captivePortalValidationPending = false;
-                        showNetworkNotification(nai, NotificationType.LOGGED_IN);
-                    }
-
-                    final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
-
-                    if (DBG) {
-                        final String logMsg = !TextUtils.isEmpty(redirectUrl)
-                                 ? " with redirect to " + redirectUrl
-                                 : "";
-                        log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
-                    }
-                    if (valid != nai.lastValidated) {
-                        if (wasDefault) {
-                            mDeps.getMetricsLogger()
-                                    .defaultNetworkMetrics().logDefaultNetworkValidity(
-                                            SystemClock.elapsedRealtime(), valid);
-                        }
-                        final int oldScore = nai.getCurrentScore();
-                        nai.lastValidated = valid;
-                        nai.everValidated |= valid;
-                        updateCapabilities(oldScore, nai, nai.networkCapabilities);
-                        // If score has changed, rebroadcast to NetworkProviders. b/17726566
-                        if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
-                        if (valid) {
-                            handleFreshlyValidatedNetwork(nai);
-                            // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
-                            // LOST_INTERNET notifications if network becomes valid.
-                            mNotifier.clearNotification(nai.network.netId,
-                                    NotificationType.NO_INTERNET);
-                            mNotifier.clearNotification(nai.network.netId,
-                                    NotificationType.LOST_INTERNET);
-                            mNotifier.clearNotification(nai.network.netId,
-                                    NotificationType.PARTIAL_CONNECTIVITY);
-                            mNotifier.clearNotification(nai.network.netId,
-                                    NotificationType.PRIVATE_DNS_BROKEN);
-                            // If network becomes valid, the hasShownBroken should be reset for
-                            // that network so that the notification will be fired when the private
-                            // DNS is broken again.
-                            nai.networkAgentConfig.hasShownBroken = false;
-                        }
-                    } else if (partialConnectivityChanged) {
-                        updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
-                    }
-                    updateInetCondition(nai);
-                    // Let the NetworkAgent know the state of its network
-                    Bundle redirectUrlBundle = new Bundle();
-                    redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
-                    // TODO: Evaluate to update partial connectivity to status to NetworkAgent.
-                    nai.asyncChannel.sendMessage(
-                            NetworkAgent.CMD_REPORT_NETWORK_STATUS,
-                            (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
-                            0, redirectUrlBundle);
-
-                    // If NetworkMonitor detects partial connectivity before
-                    // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
-                    // immediately. Re-notify partial connectivity silently if no internet
-                    // notification already there.
-                    if (!wasPartial && nai.partialConnectivity) {
-                        // Remove delayed message if there is a pending message.
-                        mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
-                        handlePromptUnvalidated(nai.network);
-                    }
-
-                    if (wasValidated && !nai.lastValidated) {
-                        handleNetworkUnvalidated(nai);
-                    }
+                    // Invoke ConnectivityReport generation for this Network test event.
+                    final Message m =
+                            mConnectivityDiagnosticsHandler.obtainMessage(
+                                    ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED,
+                                    new ConnectivityReportEvent(results.mTimestampMillis, nai));
+                    m.setData(msg.getData());
+                    mConnectivityDiagnosticsHandler.sendMessage(m);
                     break;
                 }
                 case EVENT_PROVISIONING_NOTIFICATION: {
@@ -2879,6 +2830,87 @@
             return true;
         }
 
+        private void handleNetworkTested(
+                @NonNull NetworkAgentInfo nai, int testResult, @NonNull String redirectUrl) {
+            final boolean wasPartial = nai.partialConnectivity;
+            nai.partialConnectivity = ((testResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
+            final boolean partialConnectivityChanged =
+                    (wasPartial != nai.partialConnectivity);
+
+            final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
+            final boolean wasValidated = nai.lastValidated;
+            final boolean wasDefault = isDefaultNetwork(nai);
+            // Only show a connected notification if the network is pending validation
+            // after the captive portal app was open, and it has now validated.
+            if (nai.captivePortalValidationPending && valid) {
+                // User is now logged in, network validated.
+                nai.captivePortalValidationPending = false;
+                showNetworkNotification(nai, NotificationType.LOGGED_IN);
+            }
+
+            if (DBG) {
+                final String logMsg = !TextUtils.isEmpty(redirectUrl)
+                        ? " with redirect to " + redirectUrl
+                        : "";
+                log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+            }
+            if (valid != nai.lastValidated) {
+                if (wasDefault) {
+                    mDeps.getMetricsLogger()
+                            .defaultNetworkMetrics().logDefaultNetworkValidity(
+                            SystemClock.elapsedRealtime(), valid);
+                }
+                final int oldScore = nai.getCurrentScore();
+                nai.lastValidated = valid;
+                nai.everValidated |= valid;
+                updateCapabilities(oldScore, nai, nai.networkCapabilities);
+                // If score has changed, rebroadcast to NetworkProviders. b/17726566
+                if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
+                if (valid) {
+                    handleFreshlyValidatedNetwork(nai);
+                    // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
+                    // LOST_INTERNET notifications if network becomes valid.
+                    mNotifier.clearNotification(nai.network.netId,
+                            NotificationType.NO_INTERNET);
+                    mNotifier.clearNotification(nai.network.netId,
+                            NotificationType.LOST_INTERNET);
+                    mNotifier.clearNotification(nai.network.netId,
+                            NotificationType.PARTIAL_CONNECTIVITY);
+                    mNotifier.clearNotification(nai.network.netId,
+                            NotificationType.PRIVATE_DNS_BROKEN);
+                    // If network becomes valid, the hasShownBroken should be reset for
+                    // that network so that the notification will be fired when the private
+                    // DNS is broken again.
+                    nai.networkAgentConfig.hasShownBroken = false;
+                }
+            } else if (partialConnectivityChanged) {
+                updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
+            }
+            updateInetCondition(nai);
+            // Let the NetworkAgent know the state of its network
+            Bundle redirectUrlBundle = new Bundle();
+            redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
+            // TODO: Evaluate to update partial connectivity to status to NetworkAgent.
+            nai.asyncChannel.sendMessage(
+                    NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+                    (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+                    0, redirectUrlBundle);
+
+            // If NetworkMonitor detects partial connectivity before
+            // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
+            // immediately. Re-notify partial connectivity silently if no internet
+            // notification already there.
+            if (!wasPartial && nai.partialConnectivity) {
+                // Remove delayed message if there is a pending message.
+                mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
+                handlePromptUnvalidated(nai.network);
+            }
+
+            if (wasValidated && !nai.lastValidated) {
+                handleNetworkUnvalidated(nai);
+            }
+        }
+
         private int getCaptivePortalMode() {
             return Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.CAPTIVE_PORTAL_MODE,
@@ -2927,8 +2959,23 @@
 
         @Override
         public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
-            mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED,
-                    testResult, mNetId, redirectUrl));
+            notifyNetworkTestedWithExtras(testResult, redirectUrl, SystemClock.elapsedRealtime(),
+                    PersistableBundle.EMPTY);
+        }
+
+        @Override
+        public void notifyNetworkTestedWithExtras(
+                int testResult,
+                @Nullable String redirectUrl,
+                long timestampMillis,
+                @NonNull PersistableBundle extras) {
+            final Message msg =
+                    mTrackerHandler.obtainMessage(
+                            EVENT_NETWORK_TESTED,
+                            new NetworkTestedResults(
+                                    mNetId, testResult, timestampMillis, redirectUrl));
+            msg.setData(new Bundle(extras));
+            mTrackerHandler.sendMessage(msg);
         }
 
         @Override
@@ -2970,6 +3017,21 @@
         }
 
         @Override
+        public void notifyDataStallSuspected(
+                long timestampMillis, int detectionMethod, PersistableBundle extras) {
+            final Message msg =
+                    mConnectivityDiagnosticsHandler.obtainMessage(
+                            ConnectivityDiagnosticsHandler.EVENT_DATA_STALL_SUSPECTED,
+                            detectionMethod, mNetId, timestampMillis);
+            msg.setData(new Bundle(extras));
+
+            // NetworkStateTrackerHandler currently doesn't take any actions based on data
+            // stalls so send the message directly to ConnectivityDiagnosticsHandler and avoid
+            // the cost of going through two handlers.
+            mConnectivityDiagnosticsHandler.sendMessage(msg);
+        }
+
+        @Override
         public int getInterfaceVersion() {
             return this.VERSION;
         }
@@ -4143,6 +4205,19 @@
         final int connectivityInfo = encodeBool(hasConnectivity);
         mHandler.sendMessage(
                 mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network));
+
+        final NetworkAgentInfo nai;
+        if (network == null) {
+            nai = getDefaultNetwork();
+        } else {
+            nai = getNetworkAgentInfoForNetwork(network);
+        }
+        if (nai != null) {
+            mConnectivityDiagnosticsHandler.sendMessage(
+                    mConnectivityDiagnosticsHandler.obtainMessage(
+                            ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
+                            connectivityInfo, 0, nai));
+        }
     }
 
     private void handleReportNetworkConnectivity(
@@ -7373,7 +7448,11 @@
 
     @GuardedBy("mVpns")
     private Vpn getVpnIfOwner() {
-        final int uid = Binder.getCallingUid();
+        return getVpnIfOwner(Binder.getCallingUid());
+    }
+
+    @GuardedBy("mVpns")
+    private Vpn getVpnIfOwner(int uid) {
         final int user = UserHandle.getUserId(uid);
 
         final Vpn vpn = mVpns.get(user);
@@ -7469,6 +7548,8 @@
      */
     @VisibleForTesting
     class ConnectivityDiagnosticsHandler extends Handler {
+        private final String mTag = ConnectivityDiagnosticsHandler.class.getSimpleName();
+
         /**
          * Used to handle ConnectivityDiagnosticsCallback registration events from {@link
          * android.net.ConnectivityDiagnosticsManager}.
@@ -7485,6 +7566,37 @@
          */
         private static final int EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 2;
 
+        /**
+         * Event for {@link NetworkStateTrackerHandler} to trigger ConnectivityReport callbacks
+         * after processing {@link #EVENT_NETWORK_TESTED} events.
+         * obj = {@link ConnectivityReportEvent} representing ConnectivityReport info reported from
+         * NetworkMonitor.
+         * data = PersistableBundle of extras passed from NetworkMonitor.
+         *
+         * <p>See {@link ConnectivityService#EVENT_NETWORK_TESTED}.
+         */
+        private static final int EVENT_NETWORK_TESTED = ConnectivityService.EVENT_NETWORK_TESTED;
+
+        /**
+         * Event for NetworkMonitor to inform ConnectivityService that a potential data stall has
+         * been detected on the network.
+         * obj = Long the timestamp (in millis) for when the suspected data stall was detected.
+         * arg1 = {@link DataStallReport#DetectionMethod} indicating the detection method.
+         * arg2 = NetID.
+         * data = PersistableBundle of extras passed from NetworkMonitor.
+         */
+        private static final int EVENT_DATA_STALL_SUSPECTED = 4;
+
+        /**
+         * Event for ConnectivityDiagnosticsHandler to handle network connectivity being reported to
+         * the platform. This event will invoke {@link
+         * IConnectivityDiagnosticsCallback#onNetworkConnectivityReported} for permissioned
+         * callbacks.
+         * obj = Network that was reported on
+         * arg1 = boolint for the quality reported
+         */
+        private static final int EVENT_NETWORK_CONNECTIVITY_REPORTED = 5;
+
         private ConnectivityDiagnosticsHandler(Looper looper) {
             super(looper);
         }
@@ -7502,6 +7614,37 @@
                             (IConnectivityDiagnosticsCallback) msg.obj, msg.arg1);
                     break;
                 }
+                case EVENT_NETWORK_TESTED: {
+                    final ConnectivityReportEvent reportEvent =
+                            (ConnectivityReportEvent) msg.obj;
+
+                    // This is safe because {@link
+                    // NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} receives a
+                    // PersistableBundle and converts it to the Bundle in the incoming Message. If
+                    // {@link NetworkMonitorCallbacks#notifyNetworkTested} is called, msg.data will
+                    // not be set. This is also safe, as msg.getData() will return an empty Bundle.
+                    final PersistableBundle extras = new PersistableBundle(msg.getData());
+                    handleNetworkTestedWithExtras(reportEvent, extras);
+                    break;
+                }
+                case EVENT_DATA_STALL_SUSPECTED: {
+                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
+                    if (nai == null) break;
+
+                    // This is safe because NetworkMonitorCallbacks#notifyDataStallSuspected
+                    // receives a PersistableBundle and converts it to the Bundle in the incoming
+                    // Message.
+                    final PersistableBundle extras = new PersistableBundle(msg.getData());
+                    handleDataStallSuspected(nai, (long) msg.obj, msg.arg1, extras);
+                    break;
+                }
+                case EVENT_NETWORK_CONNECTIVITY_REPORTED: {
+                    handleNetworkConnectivityReported((NetworkAgentInfo) msg.obj, toBool(msg.arg1));
+                    break;
+                }
+                default: {
+                    Log.e(mTag, "Unrecognized event in ConnectivityDiagnostics: " + msg.what);
+                }
             }
         }
     }
@@ -7511,12 +7654,16 @@
     class ConnectivityDiagnosticsCallbackInfo implements Binder.DeathRecipient {
         @NonNull private final IConnectivityDiagnosticsCallback mCb;
         @NonNull private final NetworkRequestInfo mRequestInfo;
+        @NonNull private final String mCallingPackageName;
 
         @VisibleForTesting
         ConnectivityDiagnosticsCallbackInfo(
-                @NonNull IConnectivityDiagnosticsCallback cb, @NonNull NetworkRequestInfo nri) {
+                @NonNull IConnectivityDiagnosticsCallback cb,
+                @NonNull NetworkRequestInfo nri,
+                @NonNull String callingPackageName) {
             mCb = cb;
             mRequestInfo = nri;
+            mCallingPackageName = callingPackageName;
         }
 
         @Override
@@ -7526,6 +7673,39 @@
         }
     }
 
+    /**
+     * Class used for sending information from {@link
+     * NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} to the handler for processing it.
+     */
+    private static class NetworkTestedResults {
+        private final int mNetId;
+        private final int mTestResult;
+        private final long mTimestampMillis;
+        @Nullable private final String mRedirectUrl;
+
+        private NetworkTestedResults(
+                int netId, int testResult, long timestampMillis, @Nullable String redirectUrl) {
+            mNetId = netId;
+            mTestResult = testResult;
+            mTimestampMillis = timestampMillis;
+            mRedirectUrl = redirectUrl;
+        }
+    }
+
+    /**
+     * Class used for sending information from {@link NetworkStateTrackerHandler} to {@link
+     * ConnectivityDiagnosticsHandler}.
+     */
+    private static class ConnectivityReportEvent {
+        private final long mTimestampMillis;
+        @NonNull private final NetworkAgentInfo mNai;
+
+        private ConnectivityReportEvent(long timestampMillis, @NonNull NetworkAgentInfo nai) {
+            mTimestampMillis = timestampMillis;
+            mNai = nai;
+        }
+    }
+
     private void handleRegisterConnectivityDiagnosticsCallback(
             @NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
         ensureRunningOnConnectivityServiceThread();
@@ -7573,13 +7753,109 @@
         cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
     }
 
+    private void handleNetworkTestedWithExtras(
+            @NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
+        final NetworkAgentInfo nai = reportEvent.mNai;
+        final ConnectivityReport report =
+                new ConnectivityReport(
+                        reportEvent.mNai.network,
+                        reportEvent.mTimestampMillis,
+                        nai.linkProperties,
+                        nai.networkCapabilities,
+                        extras);
+        final List<IConnectivityDiagnosticsCallback> results =
+                getMatchingPermissionedCallbacks(nai);
+        for (final IConnectivityDiagnosticsCallback cb : results) {
+            try {
+                cb.onConnectivityReport(report);
+            } catch (RemoteException ex) {
+                loge("Error invoking onConnectivityReport", ex);
+            }
+        }
+    }
+
+    private void handleDataStallSuspected(
+            @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
+            @NonNull PersistableBundle extras) {
+        final DataStallReport report =
+                new DataStallReport(nai.network, timestampMillis, detectionMethod, extras);
+        final List<IConnectivityDiagnosticsCallback> results =
+                getMatchingPermissionedCallbacks(nai);
+        for (final IConnectivityDiagnosticsCallback cb : results) {
+            try {
+                cb.onDataStallSuspected(report);
+            } catch (RemoteException ex) {
+                loge("Error invoking onDataStallSuspected", ex);
+            }
+        }
+    }
+
+    private void handleNetworkConnectivityReported(
+            @NonNull NetworkAgentInfo nai, boolean connectivity) {
+        final List<IConnectivityDiagnosticsCallback> results =
+                getMatchingPermissionedCallbacks(nai);
+        for (final IConnectivityDiagnosticsCallback cb : results) {
+            try {
+                cb.onNetworkConnectivityReported(nai.network, connectivity);
+            } catch (RemoteException ex) {
+                loge("Error invoking onNetworkConnectivityReported", ex);
+            }
+        }
+    }
+
+    private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
+            @NonNull NetworkAgentInfo nai) {
+        final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
+        for (Entry<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo> entry :
+                mConnectivityDiagnosticsCallbacks.entrySet()) {
+            final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
+            final NetworkRequestInfo nri = cbInfo.mRequestInfo;
+            if (nai.satisfies(nri.request)) {
+                if (checkConnectivityDiagnosticsPermissions(
+                        nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
+                    results.add(entry.getKey());
+                }
+            }
+        }
+        return results;
+    }
+
+    @VisibleForTesting
+    boolean checkConnectivityDiagnosticsPermissions(
+            int callbackPid, int callbackUid, NetworkAgentInfo nai, String callbackPackageName) {
+        if (checkNetworkStackPermission(callbackPid, callbackUid)) {
+            return true;
+        }
+
+        if (!mLocationPermissionChecker.checkLocationPermission(
+                callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+            return false;
+        }
+
+        synchronized (mVpns) {
+            if (getVpnIfOwner(callbackUid) != null) {
+                return true;
+            }
+        }
+
+        // Administrator UIDs also contains the Owner UID
+        if (nai.networkCapabilities.getAdministratorUids().contains(callbackUid)) {
+            return true;
+        }
+
+        return false;
+    }
+
     @Override
     public void registerConnectivityDiagnosticsCallback(
-            @NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) {
+            @NonNull IConnectivityDiagnosticsCallback callback,
+            @NonNull NetworkRequest request,
+            @NonNull String callingPackageName) {
         if (request.legacyType != TYPE_NONE) {
             throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
                     + " Please use NetworkCapabilities instead.");
         }
+        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
 
         // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
         // and administrator uids to be safe.
@@ -7597,7 +7873,7 @@
         // callback's binder death.
         final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId);
         final ConnectivityDiagnosticsCallbackInfo cbInfo =
-                new ConnectivityDiagnosticsCallbackInfo(callback, nri);
+                new ConnectivityDiagnosticsCallbackInfo(callback, nri, callingPackageName);
 
         mConnectivityDiagnosticsHandler.sendMessage(
                 mConnectivityDiagnosticsHandler.obtainMessage(
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 4a65a96..fabe92db 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -287,7 +287,7 @@
     // When the restriction is enabled, foreground service started from background will not have
     // while-in-use permissions like location, camera and microphone. (The foreground service can be
     // started, the restriction is on while-in-use permissions.)
-    volatile boolean mFlagBackgroundFgsStartRestrictionEnabled;
+    volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true;
 
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
@@ -304,18 +304,19 @@
     // we have no limit on the number of service, visible, foreground, or other such
     // processes and the number of those processes does not count against the cached
     // process limit.
-    public int CUR_MAX_CACHED_PROCESSES;
+    public int CUR_MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
 
     // The maximum number of empty app processes we will let sit around.
-    public int CUR_MAX_EMPTY_PROCESSES;
+    public int CUR_MAX_EMPTY_PROCESSES = computeEmptyProcessLimit(CUR_MAX_CACHED_PROCESSES);
 
     // The number of empty apps at which we don't consider it necessary to do
     // memory trimming.
-    public int CUR_TRIM_EMPTY_PROCESSES;
+    public int CUR_TRIM_EMPTY_PROCESSES = computeEmptyProcessLimit(MAX_CACHED_PROCESSES) / 2;
 
     // The number of cached at which we don't consider it necessary to do
     // memory trimming.
-    public int CUR_TRIM_CACHED_PROCESSES;
+    public int CUR_TRIM_CACHED_PROCESSES =
+            (MAX_CACHED_PROCESSES - computeEmptyProcessLimit(MAX_CACHED_PROCESSES)) / 3;
 
     /**
      * Packages that can't be killed even if it's requested to be killed on imperceptible.
@@ -419,6 +420,8 @@
                 context.getResources().getIntArray(
                 com.android.internal.R.array.config_defaultImperceptibleKillingExemptionProcStates))
                 .boxed().collect(Collectors.toList());
+        IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
+        IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.addAll(mDefaultImperceptibleKillExemptProcStates);
     }
 
     public void start(ContentResolver resolver) {
@@ -550,8 +553,6 @@
 
             // For new flags that are intended for server-side experiments, please use the new
             // DeviceConfig package.
-
-            updateMaxCachedProcesses();
         }
     }
 
@@ -580,6 +581,9 @@
     }
 
     private void updateOomAdjUpdatePolicy() {
+
+
+
         OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 KEY_OOMADJ_UPDATE_POLICY,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 784ce4a..a0589c5 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2126,24 +2126,27 @@
             UserHandle user = UserHandle.getUserHandleForUid(uid);
             boolean isRevokedCompat;
             if (permissionInfo.backgroundPermission != null) {
-                boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
+                if (packageManager.checkPermission(permissionInfo.backgroundPermission, packageName)
+                        == PackageManager.PERMISSION_GRANTED) {
+                    boolean isBackgroundRevokedCompat = mode != AppOpsManager.MODE_ALLOWED;
 
-                if (isBackgroundRevokedCompat && supportsRuntimePermissions) {
-                    Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
-                            + " permission state, this is discouraged and you should revoke the"
-                            + " runtime permission instead: uid=" + uid + ", switchCode="
-                            + switchCode + ", mode=" + mode + ", permission="
-                            + permissionInfo.backgroundPermission);
-                }
+                    if (isBackgroundRevokedCompat && supportsRuntimePermissions) {
+                        Slog.w(TAG, "setUidMode() called with a mode inconsistent with runtime"
+                                + " permission state, this is discouraged and you should revoke the"
+                                + " runtime permission instead: uid=" + uid + ", switchCode="
+                                + switchCode + ", mode=" + mode + ", permission="
+                                + permissionInfo.backgroundPermission);
+                    }
 
-                long identity = Binder.clearCallingIdentity();
-                try {
-                    packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
-                            packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
-                            isBackgroundRevokedCompat
-                                    ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
+                    long identity = Binder.clearCallingIdentity();
+                    try {
+                        packageManager.updatePermissionFlags(permissionInfo.backgroundPermission,
+                                packageName, PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+                                isBackgroundRevokedCompat
+                                        ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0, user);
+                    } finally {
+                        Binder.restoreCallingIdentity(identity);
+                    }
                 }
 
                 isRevokedCompat = mode != AppOpsManager.MODE_ALLOWED
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b2548af..82a2f01 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3406,13 +3406,11 @@
         }
 
         public void binderDied() {
-            int oldModeOwnerPid = 0;
+            int oldModeOwnerPid;
             int newModeOwnerPid = 0;
             synchronized (mDeviceBroker.mSetModeLock) {
                 Log.w(TAG, "setMode() client died");
-                if (!mSetModeDeathHandlers.isEmpty()) {
-                    oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
-                }
+                oldModeOwnerPid = getModeOwnerPid();
                 int index = mSetModeDeathHandlers.indexOf(this);
                 if (index < 0) {
                     Log.w(TAG, "unregistered setMode() client died");
@@ -3446,17 +3444,19 @@
 
     /** @see AudioManager#setMode(int) */
     public void setMode(int mode, IBinder cb, String callingPackage) {
-        if (DEBUG_MODE) { Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); }
+        if (DEBUG_MODE) {
+            Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
+        }
         if (!checkAudioSettingsPermission("setMode()")) {
             return;
         }
-
-        if ( (mode == AudioSystem.MODE_IN_CALL) &&
-                (mContext.checkCallingOrSelfPermission(
+        final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
                         android.Manifest.permission.MODIFY_PHONE_STATE)
-                            != PackageManager.PERMISSION_GRANTED)) {
+                        == PackageManager.PERMISSION_GRANTED;
+        final int callingPid = Binder.getCallingPid();
+        if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
             Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
-                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+                    + callingPid + ", uid=" + Binder.getCallingUid());
             return;
         }
 
@@ -3470,16 +3470,25 @@
             return;
         }
 
-        int oldModeOwnerPid = 0;
-        int newModeOwnerPid = 0;
+        int oldModeOwnerPid;
+        int newModeOwnerPid;
         synchronized (mDeviceBroker.mSetModeLock) {
-            if (!mSetModeDeathHandlers.isEmpty()) {
-                oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
-            }
             if (mode == AudioSystem.MODE_CURRENT) {
                 mode = mMode;
             }
-            newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
+            oldModeOwnerPid = getModeOwnerPid();
+            // Do not allow changing mode if a call is active and the requester
+            // does not have permission to modify phone state or is not the mode owner.
+            if (((mMode == AudioSystem.MODE_IN_CALL)
+                    || (mMode == AudioSystem.MODE_IN_COMMUNICATION))
+                    && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) {
+                Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid
+                        + ", uid=" + Binder.getCallingUid()
+                        + ", cannot change mode from " + mMode
+                        + " without permission or being mode owner");
+                return;
+            }
+            newModeOwnerPid = setModeInt(mode, cb, callingPid, callingPackage);
         }
         // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
         // SCO connections not started by the application changing the mode when pid changes
@@ -3569,10 +3578,9 @@
 
         if (status == AudioSystem.AUDIO_STATUS_OK) {
             if (actualMode != AudioSystem.MODE_NORMAL) {
-                if (mSetModeDeathHandlers.isEmpty()) {
+                newModeOwnerPid = getModeOwnerPid();
+                if (newModeOwnerPid == 0) {
                     Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
-                } else {
-                    newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
                 }
             }
             // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 87a5730..704cbff4 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -49,6 +49,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A display adapter for the local displays managed by Surface Flinger.
@@ -118,22 +119,28 @@
                         physicalDisplayId);
                 activeColorMode = Display.COLOR_MODE_INVALID;
             }
-            int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
             SurfaceControl.DesiredDisplayConfigSpecs configSpecs =
                     SurfaceControl.getDesiredDisplayConfigSpecs(displayToken);
+            int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
+            Display.HdrCapabilities hdrCapabilities =
+                    SurfaceControl.getHdrCapabilities(displayToken);
             LocalDisplayDevice device = mDevices.get(physicalDisplayId);
             if (device == null) {
                 // Display was added.
                 final boolean isInternal = mDevices.size() == 0;
                 device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
                         configs, activeConfig, configSpecs, colorModes, activeColorMode,
-                        isInternal);
+                        hdrCapabilities, isInternal);
                 mDevices.put(physicalDisplayId, device);
                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
-            } else if (device.updateDisplayConfigsLocked(configs, activeConfig, configSpecs,
-                        colorModes, activeColorMode)) {
-                // Display properties changed.
-                sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+            } else {
+                boolean changed = device.updateDisplayConfigsLocked(configs, activeConfig,
+                        configSpecs);
+                changed |= device.updateColorModesLocked(colorModes, activeColorMode);
+                changed |= device.updateHdrCapabilitiesLocked(hdrCapabilities);
+                if (changed) {
+                    sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+                }
             }
         } else {
             // The display is no longer available. Ignore the attempt to add it.
@@ -202,16 +209,16 @@
         LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
                 SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
                 int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
-                int[] colorModes, int activeColorMode, boolean isInternal) {
+                int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
+                boolean isInternal) {
             super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
             mPhysicalDisplayId = physicalDisplayId;
             mIsInternal = isInternal;
             mDisplayInfo = info;
 
-            updateDisplayConfigsLocked(configs, activeConfigId, configSpecs, colorModes,
-                    activeColorMode);
+            updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
             updateColorModesLocked(colorModes, activeColorMode);
-
+            updateHdrCapabilitiesLocked(hdrCapabilities);
             mSidekickInternal = LocalServices.getService(SidekickInternal.class);
             if (mIsInternal) {
                 LightsManager lights = LocalServices.getService(LightsManager.class);
@@ -219,7 +226,6 @@
             } else {
                 mBacklight = null;
             }
-            mHdrCapabilities = SurfaceControl.getHdrCapabilities(displayToken);
             mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
             mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
             mHalBrightnessSupport = SurfaceControl.getDisplayBrightnessSupport(displayToken);
@@ -236,11 +242,9 @@
 
         public boolean updateDisplayConfigsLocked(
                 SurfaceControl.DisplayConfig[] configs, int activeConfigId,
-                SurfaceControl.DesiredDisplayConfigSpecs configSpecs, int[] colorModes,
-                int activeColorMode) {
+                SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
             mDisplayConfigs = Arrays.copyOf(configs, configs.length);
             mActiveConfigId = activeConfigId;
-
             // Build an updated list of all existing modes.
             ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>();
             boolean modesAdded = false;
@@ -389,14 +393,15 @@
             mSystemBrightnessToNits = sysToNits;
         }
 
-        private boolean updateColorModesLocked(int[] colorModes,
-                int activeColorMode) {
-            List<Integer> pendingColorModes = new ArrayList<>();
+        private boolean updateColorModesLocked(int[] colorModes, int activeColorMode) {
+            if (colorModes == null) {
+                return false;
+            }
 
-            if (colorModes == null) return false;
+            List<Integer> pendingColorModes = new ArrayList<>();
             // Build an updated list of all existing color modes.
             boolean colorModesAdded = false;
-            for (int colorMode: colorModes) {
+            for (int colorMode : colorModes) {
                 if (!mSupportedColorModes.contains(colorMode)) {
                     colorModesAdded = true;
                 }
@@ -441,6 +446,15 @@
             return true;
         }
 
+        private boolean updateHdrCapabilitiesLocked(Display.HdrCapabilities newHdrCapabilities) {
+            // If the HDR capabilities haven't changed, then we're done here.
+            if (Objects.equals(mHdrCapabilities, newHdrCapabilities)) {
+                return false;
+            }
+            mHdrCapabilities = newHdrCapabilities;
+            return true;
+        }
+
         private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config) {
             for (int i = 0; i < mSupportedModes.size(); i++) {
                 DisplayModeRecord record = mSupportedModes.valueAt(i);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 90358ca..a8f706c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -223,7 +223,7 @@
             int displayId, InputApplicationHandle application);
     private static native void nativeSetFocusedDisplay(long ptr, int displayId);
     private static native boolean nativeTransferTouchFocus(long ptr,
-            InputChannel fromChannel, InputChannel toChannel);
+            IBinder fromChannelToken, IBinder toChannelToken);
     private static native void nativeSetPointerSpeed(long ptr, int speed);
     private static native void nativeSetShowTouches(long ptr, boolean enabled);
     private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -1554,14 +1554,29 @@
      * @return True if the transfer was successful.  False if the window with the
      * specified channel did not actually have touch focus at the time of the request.
      */
-    public boolean transferTouchFocus(InputChannel fromChannel, InputChannel toChannel) {
-        if (fromChannel == null) {
-            throw new IllegalArgumentException("fromChannel must not be null.");
-        }
-        if (toChannel == null) {
-            throw new IllegalArgumentException("toChannel must not be null.");
-        }
-        return nativeTransferTouchFocus(mPtr, fromChannel, toChannel);
+    public boolean transferTouchFocus(@NonNull InputChannel fromChannel,
+            @NonNull InputChannel toChannel) {
+        return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken());
+    }
+
+    /**
+     * Atomically transfers touch focus from one window to another as identified by
+     * their input channels.  It is possible for multiple windows to have
+     * touch focus if they support split touch dispatch
+     * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
+     * method only transfers touch focus of the specified window without affecting
+     * other windows that may also have touch focus at the same time.
+     * @param fromChannelToken The channel token of a window that currently has touch focus.
+     * @param toChannelToken The channel token of the window that should receive touch focus in
+     * place of the first.
+     * @return True if the transfer was successful.  False if the window with the
+     * specified channel did not actually have touch focus at the time of the request.
+     */
+    public boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
+            @NonNull IBinder toChannelToken) {
+        Objects.nonNull(fromChannelToken);
+        Objects.nonNull(toChannelToken);
+        return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken);
     }
 
     @Override // Binder call
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 5123362..7b17b4e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.media.MediaRoute2ProviderInfo;
+import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.os.Bundle;
 
@@ -54,6 +55,7 @@
     public abstract void requestCreateSession(String packageName, String routeId, long requestId,
             @Nullable Bundle sessionHints);
     public abstract void releaseSession(String sessionId);
+    public abstract void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference);
 
     public abstract void selectRoute(String sessionId, String routeId);
     public abstract void deselectRoute(String sessionId, String routeId);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index e23542e..e22ea46 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -25,6 +25,7 @@
 import android.media.IMediaRoute2ProviderClient;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRoute2ProviderService;
+import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.os.Bundle;
 import android.os.Handler;
@@ -93,6 +94,14 @@
     }
 
     @Override
+    public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+        if (mConnectionReady) {
+            mActiveConnection.updateDiscoveryPreference(discoveryPreference);
+            updateBinding();
+        }
+    }
+
+    @Override
     public void selectRoute(String sessionId, String routeId) {
         if (mConnectionReady) {
             mActiveConnection.selectRoute(sessionId, routeId);
@@ -461,6 +470,14 @@
             }
         }
 
+        public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+            try {
+                mProvider.updateDiscoveryPreference(discoveryPreference);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "updateDiscoveryPreference(): Failed to deliver request.");
+            }
+        }
+
         public void selectRoute(String sessionId, String routeId) {
             try {
                 mProvider.selectRoute(sessionId, routeId);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 9594659..358cc29 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -598,6 +598,9 @@
             clientRecord.mUserRecord.mHandler.sendMessage(
                     obtainMessage(UserHandler::updateClientUsage,
                             clientRecord.mUserRecord.mHandler, clientRecord));
+            clientRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::updateDiscoveryPreference,
+                            clientRecord.mUserRecord.mHandler));
         }
     }
 
@@ -856,6 +859,7 @@
         //TODO: make records private for thread-safety
         final ArrayList<Client2Record> mClientRecords = new ArrayList<>();
         final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
+        RouteDiscoveryPreference mCompositeDiscoveryPreference = RouteDiscoveryPreference.EMPTY;
         final UserHandler mHandler;
 
         UserRecord(int userId) {
@@ -885,8 +889,6 @@
         public final int mClientId;
 
         public RouteDiscoveryPreference mDiscoveryPreference;
-        public boolean mIsManagerSelecting;
-        public MediaRoute2Info mSelectingRoute;
         public MediaRoute2Info mSelectedRoute;
 
         Client2Record(UserRecord userRecord, IMediaRouter2Client client,
@@ -1003,6 +1005,7 @@
         public void onAddProvider(MediaRoute2ProviderProxy provider) {
             provider.setCallback(this);
             mMediaProviders.add(provider);
+            provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference);
         }
 
         @Override
@@ -1642,6 +1645,25 @@
             }
         }
 
+        private void updateDiscoveryPreference() {
+            MediaRouter2ServiceImpl service = mServiceRef.get();
+            if (service == null) {
+                return;
+            }
+            List<RouteDiscoveryPreference> discoveryPreferences = new ArrayList<>();
+            synchronized (service.mLock) {
+                for (Client2Record clientRecord : mUserRecord.mClientRecords) {
+                    discoveryPreferences.add(clientRecord.mDiscoveryPreference);
+                }
+                mUserRecord.mCompositeDiscoveryPreference =
+                        new RouteDiscoveryPreference.Builder(discoveryPreferences)
+                        .build();
+            }
+            for (MediaRoute2Provider provider : mMediaProviders) {
+                provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference);
+            }
+        }
+
         private MediaRoute2Provider findProvider(String providerId) {
             for (MediaRoute2Provider provider : mMediaProviders) {
                 if (TextUtils.equals(provider.getUniqueId(), providerId)) {
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 820731d..11b7a8a 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -52,6 +52,8 @@
     private boolean mIsConnected;
     @GuardedBy("mLock")
     private int mPolicies;
+    @GuardedBy("mLock")
+    private boolean mIsClosed;
 
     public MediaSession2Record(Session2Token sessionToken, MediaSessionService service,
             Looper handlerLooper, int policies) {
@@ -119,6 +121,7 @@
     @Override
     public void close() {
         synchronized (mLock) {
+            mIsClosed = true;
             // Call close regardless of the mIsAvailable. This may be called when it's not yet
             // connected.
             mController.close();
@@ -126,6 +129,13 @@
     }
 
     @Override
+    public boolean isClosed() {
+        synchronized (mLock) {
+            return mIsClosed;
+        }
+    }
+
+    @Override
     public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService,
             KeyEvent ke, int sequenceId, ResultReceiver cb) {
         // TODO(jaewan): Implement.
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 05f7e1d..7bcbcd4 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -420,6 +420,13 @@
         }
     }
 
+    @Override
+    public boolean isClosed() {
+        synchronized (mLock) {
+            return mDestroyed;
+        }
+    }
+
     /**
      * Sends media button.
      *
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 6e10880..74e6ded 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -154,4 +154,9 @@
      */
     @Override
     void close();
+
+    /**
+     * Returns whether {@link #close()} is called before.
+     */
+    boolean isClosed();
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index d0efef0..88b884e 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -434,9 +434,14 @@
         if (DEBUG) {
             Log.d(TAG, "Destroying " + session);
         }
+        if (session.isClosed()) {
+            Log.w(TAG, "Destroying already destroyed session. Ignoring.");
+            return;
+        }
+
         FullUserRecord user = getFullUserRecordLocked(session.getUserId());
 
-        if (user != null) {
+        if (user != null && session instanceof MediaSessionRecord) {
             final int uid = session.getUid();
             final int sessionCount = user.mUidToSessionCount.get(uid, 0);
             if (sessionCount <= 0) {
@@ -570,6 +575,13 @@
                 throw new RuntimeException("Session request from invalid user.");
             }
 
+            final int sessionCount = user.mUidToSessionCount.get(callerUid, 0);
+            if (sessionCount >= SESSION_CREATION_LIMIT_PER_UID
+                    && !hasMediaControlPermission(callerPid, callerUid)) {
+                throw new RuntimeException("Created too many sessions. count="
+                        + sessionCount + ")");
+            }
+
             final MediaSessionRecord session;
             try {
                 session = new MediaSessionRecord(callerPid, callerUid, userId,
@@ -579,12 +591,6 @@
                 throw new RuntimeException("Media Session owner died prematurely.", e);
             }
 
-            final int sessionCount = user.mUidToSessionCount.get(callerUid, 0);
-            if (sessionCount >= SESSION_CREATION_LIMIT_PER_UID
-                    && !hasMediaControlPermission(callerPid, callerUid)) {
-                throw new RuntimeException("Created too many sessions. count="
-                        + sessionCount + ")");
-            }
             user.mUidToSessionCount.put(callerUid, sessionCount + 1);
 
             user.mPriorityStack.addSession(session);
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 924a9b7..798a781 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -29,6 +29,7 @@
 import android.media.IAudioService;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
+import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.os.Bundle;
 import android.os.Handler;
@@ -118,6 +119,10 @@
     public void releaseSession(String sessionId) {
         // Do nothing
     }
+    @Override
+    public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+        // Do nothing
+    }
 
     @Override
     public void selectRoute(String sessionId, String routeId) {
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index d91d541..af8baa5 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -43,9 +43,9 @@
             if (DBG) Slog.d(TAG, "missing config");
             return null;
         }
-        boolean userWantsBadges = mConfig.badgingEnabled(record.sbn.getUser());
+        boolean userWantsBadges = mConfig.badgingEnabled(record.getSbn().getUser());
         boolean appCanShowBadge =
-                mConfig.canShowBadge(record.sbn.getPackageName(), record.sbn.getUid());
+                mConfig.canShowBadge(record.getSbn().getPackageName(), record.getSbn().getUid());
         if (!userWantsBadges || !appCanShowBadge) {
             record.setShowBadge(false);
         } else {
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index e59bf16..c9c8042 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -42,7 +42,7 @@
             return null;
         }
         boolean appCanShowBubble =
-                mConfig.areBubblesAllowed(record.sbn.getPackageName(), record.sbn.getUid());
+                mConfig.areBubblesAllowed(record.getSbn().getPackageName(), record.getSbn().getUid());
         if (!mConfig.bubblesEnabled() || !appCanShowBubble) {
             record.setAllowBubble(false);
         } else {
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index 0e14364..83ca699 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -15,10 +15,8 @@
 */
 package com.android.server.notification;
 
-import android.app.Notification;
 import android.app.NotificationChannel;
 import android.content.Context;
-import android.util.FeatureFlagUtils;
 import android.util.Slog;
 
 /**
@@ -47,9 +45,9 @@
             return null;
         }
         NotificationChannel updatedChannel = mConfig.getConversationNotificationChannel(
-                record.sbn.getPackageName(),
-                record.sbn.getUid(), record.getChannel().getId(),
-                record.sbn.getShortcutId(mContext), true, false);
+                record.getSbn().getPackageName(),
+                record.getSbn().getUid(), record.getChannel().getId(),
+                record.getSbn().getShortcutId(mContext), true, false);
         record.updateNotificationChannel(updatedChannel);
 
         return null;
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index a7e40cb..29b5e81 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -122,8 +122,8 @@
             return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
         }
 
-        final int leftPriority = left.sbn.getNotification().priority;
-        final int rightPriority = right.sbn.getNotification().priority;
+        final int leftPriority = left.getSbn().getNotification().priority;
+        final int rightPriority = right.getSbn().getNotification().priority;
         if (leftPriority != rightPriority) {
             // by priority, high to low
             return -1 * Integer.compare(leftPriority, rightPriority);
@@ -169,7 +169,7 @@
     }
 
     protected boolean isImportantMessaging(NotificationRecord record) {
-        return mMessagingUtil.isImportantMessaging(record.sbn, record.getImportance());
+        return mMessagingUtil.isImportantMessaging(record.getSbn(), record.getImportance());
     }
 
     private boolean isOngoing(NotificationRecord record) {
@@ -183,7 +183,7 @@
 
     private boolean isCall(NotificationRecord record) {
         return record.isCategory(Notification.CATEGORY_CALL)
-                && isDefaultPhoneApp(record.sbn.getPackageName());
+                && isDefaultPhoneApp(record.getSbn().getPackageName());
     }
 
     private boolean isDefaultPhoneApp(String pkg) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 68cc014..b52289e 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -684,14 +684,14 @@
         if (summary == null) {
             return;
         }
-        int oldFlags = summary.sbn.getNotification().flags;
+        int oldFlags = summary.getSbn().getNotification().flags;
         if (needsOngoingFlag) {
-            summary.sbn.getNotification().flags |= FLAG_ONGOING_EVENT;
+            summary.getSbn().getNotification().flags |= FLAG_ONGOING_EVENT;
         } else {
-            summary.sbn.getNotification().flags &= ~FLAG_ONGOING_EVENT;
+            summary.getSbn().getNotification().flags &= ~FLAG_ONGOING_EVENT;
         }
 
-        if (summary.sbn.getNotification().flags != oldFlags) {
+        if (summary.getSbn().getNotification().flags != oldFlags) {
             mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground));
         }
     }
@@ -917,7 +917,7 @@
                         r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
                         nv.rank, nv.count);
 
-                StatusBarNotification sbn = r.sbn;
+                StatusBarNotification sbn = r.getSbn();
                 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
                         sbn.getId(), Notification.FLAG_AUTO_CANCEL,
                         FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
@@ -959,7 +959,7 @@
                 nv.recycle();
                 reportUserInteraction(r);
                 mAssistants.notifyAssistantActionClicked(
-                        r.sbn, actionIndex, action, generatedByAssistant);
+                        r.getSbn(), actionIndex, action, generatedByAssistant);
             }
         }
 
@@ -1044,7 +1044,7 @@
                         reportSeen(r);
                     }
                     r.setVisibility(true, nv.rank, nv.count);
-                    mAssistants.notifyAssistantVisibilityChangedLocked(r.sbn, true);
+                    mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), true);
                     boolean isHun = (nv.location
                             == NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
                     // hasBeenVisiblyExpanded must be called after updating the expansion state of
@@ -1063,7 +1063,7 @@
                     NotificationRecord r = mNotificationsByKey.get(nv.key);
                     if (r == null) continue;
                     r.setVisibility(false, nv.rank, nv.count);
-                    mAssistants.notifyAssistantVisibilityChangedLocked(r.sbn, false);
+                    mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), false);
                     nv.recycle();
                 }
             }
@@ -1090,7 +1090,8 @@
                         r.recordExpanded();
                         reportUserInteraction(r);
                     }
-                    mAssistants.notifyAssistantExpansionChangedLocked(r.sbn, userAction, expanded);
+                    mAssistants.notifyAssistantExpansionChangedLocked(
+                            r.getSbn(), userAction, expanded);
                 }
             }
         }
@@ -1106,7 +1107,7 @@
                             .setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
                             .setType(MetricsEvent.TYPE_ACTION));
                     reportUserInteraction(r);
-                    mAssistants.notifyAssistantNotificationDirectReplyLocked(r.sbn);
+                    mAssistants.notifyAssistantNotificationDirectReplyLocked(r.getSbn());
                 }
             }
         }
@@ -1150,7 +1151,7 @@
                     // Treat clicking on a smart reply as a user interaction.
                     reportUserInteraction(r);
                     mAssistants.notifyAssistantSuggestedReplySent(
-                            r.sbn, reply, r.getSuggestionsGeneratedByAssistant());
+                            r.getSbn(), reply, r.getSuggestionsGeneratedByAssistant());
                 }
             }
         }
@@ -1170,7 +1171,7 @@
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
-                    final StatusBarNotification n = r.sbn;
+                    final StatusBarNotification n = r.getSbn();
                     final int callingUid = n.getUid();
                     final String pkg = n.getPackageName();
                     applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isBubble);
@@ -1372,9 +1373,9 @@
                     record = findNotificationByKeyLocked(intent.getStringExtra(EXTRA_KEY));
                 }
                 if (record != null) {
-                    cancelNotification(record.sbn.getUid(), record.sbn.getInitialPid(),
-                            record.sbn.getPackageName(), record.sbn.getTag(),
-                            record.sbn.getId(), 0,
+                    cancelNotification(record.getSbn().getUid(), record.getSbn().getInitialPid(),
+                            record.getSbn().getPackageName(), record.getSbn().getTag(),
+                            record.getSbn().getId(), 0,
                             FLAG_FOREGROUND_SERVICE, true, record.getUserId(),
                             REASON_TIMEOUT, null);
                 }
@@ -1637,7 +1638,7 @@
                 synchronized (mNotificationLock) {
                     NotificationRecord r = mNotificationsByKey.get(bubbleKey);
                     if (r != null) {
-                        final StatusBarNotification n = r.sbn;
+                        final StatusBarNotification n = r.getSbn();
                         final int callingUid = n.getUid();
                         final String pkg = n.getPackageName();
                         applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isAppForeground);
@@ -1780,7 +1781,7 @@
                 if (mNotificationsByKey.containsKey(posted.getKey())) {
                     count--;
                 }
-                if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
+                if (posted.getSbn().isGroup() && posted.getNotification().isGroupSummary()) {
                     count--;
                 }
             }
@@ -1802,8 +1803,8 @@
     @VisibleForTesting
     void addNotification(NotificationRecord r) {
         mNotificationList.add(r);
-        mNotificationsByKey.put(r.sbn.getKey(), r);
-        if (r.sbn.isGroup()) {
+        mNotificationsByKey.put(r.getSbn().getKey(), r);
+        if (r.getSbn().isGroup()) {
             mSummaryByGroupKey.put(r.getGroupKey(), r);
         }
     }
@@ -2059,9 +2060,9 @@
                     if (DBG) {
                         Slog.d(TAG, "Reposting " + r.getKey());
                     }
-                    enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
-                            r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
-                            r.sbn.getNotification(), userId);
+                    enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),
+                            r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),
+                            r.getSbn().getId(),  r.getSbn().getNotification(), userId);
                 } catch (Exception e) {
                     Slog.e(TAG, "Cannot un-snooze notification", e);
                 }
@@ -2201,7 +2202,7 @@
                 String pkg;
                 synchronized (mNotificationLock) {
                     NotificationRecord r = mNotificationsByKey.get(key);
-                    pkg = r != null && r.sbn != null ? r.sbn.getPackageName() : null;
+                    pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null;
                 }
                 boolean isAppForeground = pkg != null
                         && mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
@@ -2209,7 +2210,7 @@
                     NotificationRecord r = mNotificationsByKey.get(key);
                     if (r == null) return;
                     updateAutobundledSummaryFlags(r.getUser().getIdentifier(),
-                            r.sbn.getPackageName(), needsOngoingFlag, isAppForeground);
+                            r.getSbn().getPackageName(), needsOngoingFlag, isAppForeground);
                 }
             }
         });
@@ -2280,7 +2281,8 @@
         final long updatedSuppressedEffects = calculateSuppressedEffects();
         if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
         final List<ComponentName> suppressors = getSuppressors();
-        ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
+        ZenLog.traceEffectsSuppressorChanged(
+                mEffectsSuppressors, suppressors, updatedSuppressedEffects);
         mEffectsSuppressors = suppressors;
         mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
         sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
@@ -2486,6 +2488,18 @@
         scheduleInterruptionFilterChanged(interruptionFilter);
     }
 
+    int correctCategory(int requestedCategoryList, int categoryType,
+            int currentCategoryList) {
+        if ((requestedCategoryList & categoryType) != 0
+                && (currentCategoryList & categoryType) == 0) {
+            requestedCategoryList &= ~categoryType;
+        } else if ((requestedCategoryList & categoryType) == 0
+                && (currentCategoryList & categoryType) != 0){
+            requestedCategoryList |= categoryType;
+        }
+        return requestedCategoryList;
+    }
+
     @VisibleForTesting
     INotificationManager getBinderService() {
         return INotificationManager.Stub.asInterface(mService);
@@ -2498,8 +2512,8 @@
     @GuardedBy("mNotificationLock")
     protected void reportSeen(NotificationRecord r) {
         if (!r.isProxied()) {
-            mAppUsageStats.reportEvent(r.sbn.getPackageName(),
-                    getRealUserId(r.sbn.getUserId()),
+            mAppUsageStats.reportEvent(r.getSbn().getPackageName(),
+                    getRealUserId(r.getSbn().getUserId()),
                     UsageEvents.Event.NOTIFICATION_SEEN);
         }
     }
@@ -2574,18 +2588,18 @@
     @GuardedBy("mNotificationLock")
     protected void maybeRecordInterruptionLocked(NotificationRecord r) {
         if (r.isInterruptive() && !r.hasRecordedInterruption()) {
-            mAppUsageStats.reportInterruptiveNotification(r.sbn.getPackageName(),
+            mAppUsageStats.reportInterruptiveNotification(r.getSbn().getPackageName(),
                     r.getChannel().getId(),
-                    getRealUserId(r.sbn.getUserId()));
+                    getRealUserId(r.getSbn().getUserId()));
             mHistoryManager.addNotification(new HistoricalNotification.Builder()
-                    .setPackage(r.sbn.getPackageName())
-                    .setUid(r.sbn.getUid())
+                    .setPackage(r.getSbn().getPackageName())
+                    .setUid(r.getSbn().getUid())
                     .setChannelId(r.getChannel().getId())
                     .setChannelName(r.getChannel().getName().toString())
                     .setPostedTimeMs(System.currentTimeMillis())
                     .setTitle(getHistoryTitle(r.getNotification()))
                     .setText(getHistoryText(
-                            r.sbn.getPackageContext(getContext()), r.getNotification()))
+                            r.getSbn().getPackageContext(getContext()), r.getNotification()))
                     .setIcon(r.getNotification().getSmallIcon())
                     .build());
             r.setRecordedInterruption(true);
@@ -2632,8 +2646,8 @@
      * @param r notification record
      */
     protected void reportUserInteraction(NotificationRecord r) {
-        mAppUsageStats.reportEvent(r.sbn.getPackageName(),
-                getRealUserId(r.sbn.getUserId()),
+        mAppUsageStats.reportEvent(r.getSbn().getPackageName(),
+                getRealUserId(r.getSbn().getUserId()),
                 UsageEvents.Event.USER_INTERACTION);
     }
 
@@ -3509,7 +3523,7 @@
                     tmp = new StatusBarNotification[mNotificationList.size()];
                     final int N = mNotificationList.size();
                     for (int i=0; i<N; i++) {
-                        tmp[i] = mNotificationList.get(i).sbn;
+                        tmp[i] = mNotificationList.get(i).getSbn();
                     }
                 }
             }
@@ -3541,13 +3555,13 @@
                 final int N = mNotificationList.size();
                 for (int i = 0; i < N; i++) {
                     StatusBarNotification sbn = sanitizeSbn(pkg, userId,
-                            mNotificationList.get(i).sbn);
+                            mNotificationList.get(i).getSbn());
                     if (sbn != null) {
                         map.put(sbn.getKey(), sbn);
                     }
                 }
                 for(NotificationRecord snoozed: mSnoozeHelper.getSnoozed(userId, pkg)) {
-                    StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.sbn);
+                    StatusBarNotification sbn = sanitizeSbn(pkg, userId, snoozed.getSbn());
                     if (sbn != null) {
                         map.put(sbn.getKey(), sbn);
                     }
@@ -3555,7 +3569,7 @@
                 final int M = mEnqueuedNotifications.size();
                 for (int i = 0; i < M; i++) {
                     StatusBarNotification sbn = sanitizeSbn(pkg, userId,
-                            mEnqueuedNotifications.get(i).sbn);
+                            mEnqueuedNotifications.get(i).getSbn());
                     if (sbn != null) {
                         map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
                     }
@@ -3673,15 +3687,15 @@
                         for (int i = 0; i < N; i++) {
                             NotificationRecord r = mNotificationsByKey.get(keys[i]);
                             if (r == null) continue;
-                            final int userId = r.sbn.getUserId();
+                            final int userId = r.getSbn().getUserId();
                             if (userId != info.userid && userId != UserHandle.USER_ALL &&
                                     !mUserProfiles.isCurrentProfile(userId)) {
                                 throw new SecurityException("Disallowed call from listener: "
                                         + info.service);
                             }
                             cancelNotificationFromListenerLocked(info, callingUid, callingPid,
-                                    r.sbn.getPackageName(), r.sbn.getTag(), r.sbn.getId(),
-                                    userId);
+                                    r.getSbn().getPackageName(), r.getSbn().getTag(),
+                                    r.getSbn().getId(), userId);
                         }
                     } else {
                         cancelAllLocked(callingUid, callingPid, info.userid,
@@ -3741,7 +3755,7 @@
                     for (int i = 0; i < n; i++) {
                         NotificationRecord r = mNotificationsByKey.get(keys[i]);
                         if (r == null) continue;
-                        final int userId = r.sbn.getUserId();
+                        final int userId = r.getSbn().getUserId();
                         if (userId != info.userid && userId != UserHandle.USER_ALL
                                 && !mUserProfiles.isCurrentProfile(userId)) {
                             throw new SecurityException("Disallowed call from listener: "
@@ -3891,7 +3905,7 @@
                             ? mNotificationsByKey.get(keys[i])
                             : mNotificationList.get(i);
                     if (r == null) continue;
-                    StatusBarNotification sbn = r.sbn;
+                    StatusBarNotification sbn = r.getSbn();
                     if (!isVisibleToListener(sbn, info)) continue;
                     StatusBarNotification sbnToSend =
                             (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
@@ -3921,7 +3935,7 @@
                 for (int i=0; i < N; i++) {
                     final NotificationRecord r = snoozedRecords.get(i);
                     if (r == null) continue;
-                    StatusBarNotification sbn = r.sbn;
+                    StatusBarNotification sbn = r.getSbn();
                     if (!isVisibleToListener(sbn, info)) continue;
                     StatusBarNotification sbnToSend =
                             (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
@@ -4096,7 +4110,8 @@
             Objects.requireNonNull(packageName, "Package name is null");
             enforceSystemOrSystemUI("removeAutomaticZenRules");
 
-            return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
+            return mZenModeHelper.removeAutomaticZenRules(packageName,
+                    packageName + "|removeAutomaticZenRules");
         }
 
         @Override
@@ -4411,11 +4426,20 @@
                             policy.priorityCallSenders, policy.priorityMessageSenders,
                             policy.suppressedVisualEffects);
                 }
+                if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.R) {
+                    int priorityCategories = correctCategory(policy.priorityCategories,
+                            Policy.PRIORITY_CATEGORY_CONVERSATIONS,
+                            currPolicy.priorityCategories);
+
+                    policy = new Policy(priorityCategories,
+                            policy.priorityCallSenders, policy.priorityMessageSenders,
+                            policy.suppressedVisualEffects, currPolicy.priorityConversationSenders);
+                }
                 int newVisualEffects = calculateSuppressedVisualEffects(
                             policy, currPolicy, applicationInfo.targetSdkVersion);
                 policy = new Policy(policy.priorityCategories,
                         policy.priorityCallSenders, policy.priorityMessageSenders,
-                        newVisualEffects);
+                        newVisualEffects, policy.priorityConversationSenders);
                 ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion, policy);
                 mZenModeHelper.setNotificationPolicy(policy);
             } catch (RemoteException e) {
@@ -4424,6 +4448,8 @@
             }
         }
 
+
+
         @Override
         public List<String> getEnabledNotificationListenerPackages() {
             checkCallerIsSystem();
@@ -4637,8 +4663,8 @@
             Objects.requireNonNull(user);
             verifyPrivilegedListener(token, user, true);
 
-            return mPreferencesHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
-                    false /* includeDeleted */);
+            return mPreferencesHelper.getNotificationChannels(pkg,
+                    getUidForPackageAndUser(pkg, user), false /* includeDeleted */);
         }
 
         @Override
@@ -4830,7 +4856,7 @@
         if (r == null) {
             return;
         }
-        if (r.sbn.getOverrideGroupKey() == null) {
+        if (r.getSbn().getOverrideGroupKey() == null) {
             addAutoGroupAdjustment(r, GroupHelper.AUTOGROUP_KEY);
             EventLogTags.writeNotificationAutogrouped(key);
             mRankingHandler.requestSort();
@@ -4843,7 +4869,7 @@
         if (r == null) {
             return;
         }
-        if (r.sbn.getOverrideGroupKey() != null) {
+        if (r.getSbn().getOverrideGroupKey() != null) {
             addAutoGroupAdjustment(r, null);
             EventLogTags.writeNotificationUnautogrouped(key);
             mRankingHandler.requestSort();
@@ -4853,8 +4879,8 @@
     private void addAutoGroupAdjustment(NotificationRecord r, String overrideGroupKey) {
         Bundle signals = new Bundle();
         signals.putString(Adjustment.KEY_GROUP_KEY, overrideGroupKey);
-        Adjustment adjustment =
-                new Adjustment(r.sbn.getPackageName(), r.getKey(), signals, "", r.sbn.getUserId());
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getSbn().getUserId());
         r.addAdjustment(adjustment);
     }
 
@@ -4890,7 +4916,7 @@
                 // adjustment will post a summary if needed.
                 return;
             }
-            final StatusBarNotification adjustedSbn = notificationRecord.sbn;
+            final StatusBarNotification adjustedSbn = notificationRecord.getSbn();
             userId = adjustedSbn.getUser().getIdentifier();
             ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
             if (summaries == null) {
@@ -4938,7 +4964,8 @@
             }
         }
         if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
-                summaryRecord.sbn.getId(), summaryRecord.sbn.getTag(), summaryRecord, true)) {
+                summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
+                true)) {
             mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
         }
     }
@@ -4999,14 +5026,14 @@
             int N = mNotificationList.size();
             for (int i = 0; i < N; i++) {
                 final NotificationRecord nr = mNotificationList.get(i);
-                if (filter.filtered && !filter.matches(nr.sbn)) continue;
+                if (filter.filtered && !filter.matches(nr.getSbn())) continue;
                 nr.dump(proto, NotificationServiceDumpProto.RECORDS, filter.redact,
                         NotificationRecordProto.POSTED);
             }
             N = mEnqueuedNotifications.size();
             for (int i = 0; i < N; i++) {
                 final NotificationRecord nr = mEnqueuedNotifications.get(i);
-                if (filter.filtered && !filter.matches(nr.sbn)) continue;
+                if (filter.filtered && !filter.matches(nr.getSbn())) continue;
                 nr.dump(proto, NotificationServiceDumpProto.RECORDS, filter.redact,
                         NotificationRecordProto.ENQUEUED);
             }
@@ -5014,7 +5041,7 @@
             N = snoozed.size();
             for (int i = 0; i < N; i++) {
                 final NotificationRecord nr = snoozed.get(i);
-                if (filter.filtered && !filter.matches(nr.sbn)) continue;
+                if (filter.filtered && !filter.matches(nr.getSbn())) continue;
                 nr.dump(proto, NotificationServiceDumpProto.RECORDS, filter.redact,
                         NotificationRecordProto.SNOOZED);
             }
@@ -5075,7 +5102,7 @@
                 pw.println("  Notification List:");
                 for (int i = 0; i < N; i++) {
                     final NotificationRecord nr = mNotificationList.get(i);
-                    if (filter.filtered && !filter.matches(nr.sbn)) continue;
+                    if (filter.filtered && !filter.matches(nr.getSbn())) continue;
                     nr.dump(pw, "    ", getContext(), filter.redact);
                 }
                 pw.println("  ");
@@ -5155,7 +5182,7 @@
                         pw.println("  Enqueued Notification List:");
                         for (int i = 0; i < N; i++) {
                             final NotificationRecord nr = mEnqueuedNotifications.get(i);
-                            if (filter.filtered && !filter.matches(nr.sbn)) continue;
+                            if (filter.filtered && !filter.matches(nr.getSbn())) continue;
                             nr.dump(pw, "    ", getContext(), filter.redact);
                         }
                         pw.println("  ");
@@ -5281,7 +5308,7 @@
             if (r == null) {
                 return;
             }
-            StatusBarNotification sbn = r.sbn;
+            StatusBarNotification sbn = r.getSbn();
             // NoMan adds flags FLAG_NO_CLEAR and FLAG_ONGOING_EVENT when it sees
             // FLAG_FOREGROUND_SERVICE. Hence it's not enough to remove
             // FLAG_FOREGROUND_SERVICE, we have to revert to the flags we received
@@ -5312,7 +5339,7 @@
                 // Look for the notification, searching both the posted and enqueued lists.
                 NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
                 if (r != null) {
-                    if (!Objects.equals(opPkg, r.sbn.getOpPkg())) {
+                    if (!Objects.equals(opPkg, r.getSbn().getOpPkg())) {
                         throw new SecurityException(opPkg + " does not have permission to "
                                 + "cancel a notification they did not post " + tag + " " + id);
                     }
@@ -5360,8 +5387,8 @@
         try {
             fixNotification(notification, pkg, tag, id, userId);
 
-        } catch (NameNotFoundException e) {
-            Slog.e(TAG, "Cannot create a context for sending app", e);
+        } catch (Exception e) {
+            Slog.e(TAG, "Cannot fix notification", e);
             return;
         }
 
@@ -5442,7 +5469,7 @@
         }
 
         if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
-                r.sbn.getOverrideGroupKey() != null)) {
+                r.getSbn().getOverrideGroupKey() != null)) {
             return;
         }
 
@@ -5586,12 +5613,12 @@
             if (shortcutId != null) {
                 // Must track shortcut based bubbles in case the shortcut is removed
                 HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
-                        r.sbn.getPackageName());
+                        r.getSbn().getPackageName());
                 if (packageBubbles == null) {
                     packageBubbles = new HashMap<>();
                 }
                 packageBubbles.put(shortcutId, r.getKey());
-                mActiveShortcutBubbles.put(r.sbn.getPackageName(), packageBubbles);
+                mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles);
                 if (!mLauncherAppsCallbackRegistered) {
                     mLauncherAppsService.registerCallback(mLauncherAppsCallback, mHandler);
                     mLauncherAppsCallbackRegistered = true;
@@ -5602,12 +5629,12 @@
             if (shortcutId != null) {
                 // No longer track shortcut
                 HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
-                        r.sbn.getPackageName());
+                        r.getSbn().getPackageName());
                 if (packageBubbles != null) {
                     packageBubbles.remove(shortcutId);
                 }
                 if (packageBubbles != null && packageBubbles.isEmpty()) {
-                    mActiveShortcutBubbles.remove(r.sbn.getPackageName());
+                    mActiveShortcutBubbles.remove(r.getSbn().getPackageName());
                 }
                 if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
                     mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
@@ -5853,7 +5880,7 @@
      */
     private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
             NotificationRecord r, boolean isAutogroup) {
-        final String pkg = r.sbn.getPackageName();
+        final String pkg = r.getSbn().getPackageName();
         final boolean isSystemNotification =
                 isUidSystemOrPhone(uid) || ("android".equals(pkg));
         final boolean isNotificationFromListener = mListeners.isListenerPackage(pkg);
@@ -5863,7 +5890,7 @@
         if (!isSystemNotification && !isNotificationFromListener) {
             synchronized (mNotificationLock) {
                 final int callingUid = Binder.getCallingUid();
-                if (mNotificationsByKey.get(r.sbn.getKey()) == null
+                if (mNotificationsByKey.get(r.getSbn().getKey()) == null
                         && isCallerInstantApp(callingUid, userId)) {
                     // Ephemeral apps have some special constraints for notifications.
                     // They are not allowed to create new notifications however they are allowed to
@@ -5874,7 +5901,7 @@
                 }
 
                 // rate limit updates that aren't completed progress notifications
-                if (mNotificationsByKey.get(r.sbn.getKey()) != null
+                if (mNotificationsByKey.get(r.getSbn().getKey()) != null
                         && !r.getNotification().hasCompletedProgress()
                         && !isAutogroup) {
 
@@ -5884,7 +5911,7 @@
                         final long now = SystemClock.elapsedRealtime();
                         if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
                             Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
-                                    + ". Shedding " + r.sbn.getKey() + ". package=" + pkg);
+                                    + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
                             mLastOverRateLogTime = now;
                         }
                         return false;
@@ -5933,10 +5960,10 @@
         final int N = mNotificationList.size();
         for (int i = 0; i < N; i++) {
             final NotificationRecord existing = mNotificationList.get(i);
-            if (existing.sbn.getPackageName().equals(pkg)
-                    && existing.sbn.getUserId() == userId) {
-                if (existing.sbn.getId() == excludedId
-                        && TextUtils.equals(existing.sbn.getTag(), excludedTag)) {
+            if (existing.getSbn().getPackageName().equals(pkg)
+                    && existing.getSbn().getUserId() == userId) {
+                if (existing.getSbn().getId() == excludedId
+                        && TextUtils.equals(existing.getSbn().getTag(), excludedTag)) {
                     continue;
                 }
                 count++;
@@ -5945,8 +5972,8 @@
         final int M = mEnqueuedNotifications.size();
         for (int i = 0; i < M; i++) {
             final NotificationRecord existing = mEnqueuedNotifications.get(i);
-            if (existing.sbn.getPackageName().equals(pkg)
-                    && existing.sbn.getUserId() == userId) {
+            if (existing.getSbn().getPackageName().equals(pkg)
+                    && existing.getSbn().getUserId() == userId) {
                 count++;
             }
         }
@@ -5965,8 +5992,8 @@
     }
 
     private boolean isBlocked(NotificationRecord r) {
-        final String pkg = r.sbn.getPackageName();
-        final int callingUid = r.sbn.getUid();
+        final String pkg = r.getSbn().getPackageName();
+        final int callingUid = r.getSbn().getUid();
         return mPreferencesHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup())
                 || mPreferencesHelper.getImportance(pkg, callingUid)
                 == NotificationManager.IMPORTANCE_NONE
@@ -5996,10 +6023,11 @@
 
         @GuardedBy("mNotificationLock")
         void snoozeLocked(NotificationRecord r) {
-            if (r.sbn.isGroup()) {
+            if (r.getSbn().isGroup()) {
                 final List<NotificationRecord> groupNotifications =
                         findCurrentAndSnoozedGroupNotificationsLocked(
-                        r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId());
+                        r.getSbn().getPackageName(),
+                                r.getSbn().getGroupKey(), r.getSbn().getUserId());
                 if (r.getNotification().isGroupSummary()) {
                     // snooze all children
                     for (int i = 0; i < groupNotifications.size(); i++) {
@@ -6010,7 +6038,7 @@
                 } else {
                     // if there is a valid summary for this group, and we are snoozing the only
                     // child, also snooze the summary
-                    if (mSummaryByGroupKey.containsKey(r.sbn.getGroupKey())) {
+                    if (mSummaryByGroupKey.containsKey(r.getSbn().getGroupKey())) {
                         if (groupNotifications.size() == 2) {
                             // snooze summary and the one child
                             for (int i = 0; i < groupNotifications.size(); i++) {
@@ -6041,7 +6069,7 @@
             cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
             updateLightsLocked();
             if (mSnoozeCriterionId != null) {
-                mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
+                mAssistants.notifyAssistantSnoozedLocked(r.getSbn(), mSnoozeCriterionId);
                 mSnoozeHelper.snooze(r, mSnoozeCriterionId);
             } else {
                 mSnoozeHelper.snooze(r, mDuration);
@@ -6172,10 +6200,10 @@
                 final Long snoozeAt =
                         mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
                                 r.getUser().getIdentifier(),
-                                r.sbn.getPackageName(), r.sbn.getKey());
+                                r.getSbn().getPackageName(), r.getSbn().getKey());
                 final long currentTime = System.currentTimeMillis();
                 if (snoozeAt.longValue() > currentTime) {
-                    (new SnoozeNotificationRunnable(r.sbn.getKey(),
+                    (new SnoozeNotificationRunnable(r.getSbn().getKey(),
                             snoozeAt.longValue() - currentTime, null)).snoozeLocked(r);
                     return;
                 }
@@ -6183,9 +6211,9 @@
                 final String contextId =
                         mSnoozeHelper.getSnoozeContextForUnpostedNotification(
                                 r.getUser().getIdentifier(),
-                                r.sbn.getPackageName(), r.sbn.getKey());
+                                r.getSbn().getPackageName(), r.getSbn().getKey());
                 if (contextId != null) {
-                    (new SnoozeNotificationRunnable(r.sbn.getKey(),
+                    (new SnoozeNotificationRunnable(r.getSbn().getKey(),
                             0, contextId)).snoozeLocked(r);
                     return;
                 }
@@ -6193,7 +6221,7 @@
                 mEnqueuedNotifications.add(r);
                 scheduleTimeoutLocked(r);
 
-                final StatusBarNotification n = r.sbn;
+                final StatusBarNotification n = r.getSbn();
                 if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
                 NotificationRecord old = mNotificationsByKey.get(n.getKey());
                 if (old != null) {
@@ -6291,20 +6319,20 @@
                     }
 
                     final boolean isPackageSuspended =
-                            isPackagePausedOrSuspended(r.sbn.getPackageName(), r.getUid());
+                            isPackagePausedOrSuspended(r.getSbn().getPackageName(), r.getUid());
                     r.setHidden(isPackageSuspended);
                     if (isPackageSuspended) {
                         mUsageStats.registerSuspendedByAdmin(r);
                     }
                     NotificationRecord old = mNotificationsByKey.get(key);
-                    final StatusBarNotification n = r.sbn;
+                    final StatusBarNotification n = r.getSbn();
                     final Notification notification = n.getNotification();
 
                     // Make sure the SBN has an instance ID for statsd logging.
-                    if (old == null || old.sbn.getInstanceId() == null) {
+                    if (old == null || old.getSbn().getInstanceId() == null) {
                         n.setInstanceId(mNotificationInstanceIdSequence.newInstanceId());
                     } else {
-                        n.setInstanceId(old.sbn.getInstanceId());
+                        n.setInstanceId(old.getSbn().getInstanceId());
                     }
 
                     int index = indexOfNotificationLocked(n.getKey());
@@ -6344,7 +6372,7 @@
                     }
 
                     if (notification.getSmallIcon() != null) {
-                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
+                        StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
                         mListeners.notifyPostedLocked(r, old);
                         if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))
                                 && !isCritical(r)) {
@@ -6358,7 +6386,7 @@
                         } else if (oldSbn != null) {
                             final NotificationRecord finalRecord = r;
                             mHandler.post(() -> mGroupHelper.onNotificationUpdated(
-                                    finalRecord.sbn, hasAutoGroupSummaryLocked(n)));
+                                    finalRecord.getSbn(), hasAutoGroupSummaryLocked(n)));
                         }
                     } else {
                         Slog.e(TAG, "Not posting notification without small icon: " + notification);
@@ -6405,7 +6433,7 @@
     @VisibleForTesting
     protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) {
         // Ignore summary updates because we don't display most of the information.
-        if (r.sbn.isGroup() && r.sbn.getNotification().isGroupSummary()) {
+        if (r.getSbn().isGroup() && r.getSbn().getNotification().isGroupSummary()) {
             if (DEBUG_INTERRUPTIVENESS) {
                 Slog.v(TAG, "INTERRUPTIVENESS: "
                         +  r.getKey() + " is not interruptive: summary");
@@ -6429,8 +6457,8 @@
             return false;
         }
 
-        Notification oldN = old.sbn.getNotification();
-        Notification newN = r.sbn.getNotification();
+        Notification oldN = old.getSbn().getNotification();
+        Notification newN = r.getSbn().getNotification();
         if (oldN.extras == null || newN.extras == null) {
             if (DEBUG_INTERRUPTIVENESS) {
                 Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -6441,7 +6469,7 @@
 
         // Ignore visual interruptions from foreground services because users
         // consider them one 'session'. Count them for everything else.
-        if ((r.sbn.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0) {
+        if ((r.getSbn().getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0) {
             if (DEBUG_INTERRUPTIVENESS) {
                 Slog.v(TAG, "INTERRUPTIVENESS: "
                         +  r.getKey() + " is not interruptive: foreground service");
@@ -6555,7 +6583,7 @@
     @GuardedBy("mNotificationLock")
     private void handleGroupedNotificationLocked(NotificationRecord r, NotificationRecord old,
             int callingUid, int callingPid) {
-        StatusBarNotification sbn = r.sbn;
+        StatusBarNotification sbn = r.getSbn();
         Notification n = sbn.getNotification();
         if (n.isGroupSummary() && !sbn.isAppGroup())  {
             // notifications without a group shouldn't be a summary, otherwise autobundling can
@@ -6566,8 +6594,8 @@
         String group = sbn.getGroupKey();
         boolean isSummary = n.isGroupSummary();
 
-        Notification oldN = old != null ? old.sbn.getNotification() : null;
-        String oldGroup = old != null ? old.sbn.getGroupKey() : null;
+        Notification oldN = old != null ? old.getSbn().getNotification() : null;
+        String oldGroup = old != null ? old.getSbn().getGroupKey() : null;
         boolean oldIsSummary = old != null && oldN.isGroupSummary();
 
         if (oldIsSummary) {
@@ -6624,7 +6652,7 @@
         boolean beep = false;
         boolean blink = false;
 
-        final Notification notification = record.sbn.getNotification();
+        final Notification notification = record.getSbn().getNotification();
         final String key = record.getKey();
 
         // Should this notification make noise, vibe, or use the LED?
@@ -6642,7 +6670,7 @@
         // If the notification will appear in the status bar, it should send an accessibility
         // event
         if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
-            sendAccessibilityEvent(notification, record.sbn.getPackageName());
+            sendAccessibilityEvent(notification, record.getSbn().getPackageName());
             sentAccessibilityEvent = true;
         }
 
@@ -6664,7 +6692,7 @@
                 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
                 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
                     if (!sentAccessibilityEvent) {
-                        sendAccessibilityEvent(notification, record.sbn.getPackageName());
+                        sendAccessibilityEvent(notification, record.getSbn().getPackageName());
                         sentAccessibilityEvent = true;
                     }
                     if (DBG) Slog.v(TAG, "Interrupting!");
@@ -6719,7 +6747,7 @@
         final int buzzBeepBlink = (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0);
         if (buzzBeepBlink > 0) {
             // Ignore summary updates because we don't display most of the information.
-            if (record.sbn.isGroup() && record.sbn.getNotification().isGroupSummary()) {
+            if (record.getSbn().isGroup() && record.getSbn().getNotification().isGroupSummary()) {
                 if (DEBUG_INTERRUPTIVENESS) {
                     Slog.v(TAG, "INTERRUPTIVENESS: "
                             + record.getKey() + " is not interruptive: summary");
@@ -6774,7 +6802,7 @@
             return false;
         }
         // Suppressed because another notification in its group handles alerting
-        if (record.sbn.isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
+        if (record.getSbn().isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
             return false;
         }
         // not if in call or the screen's on
@@ -6806,14 +6834,14 @@
         }
 
         // Suppressed because another notification in its group handles alerting
-        if (record.sbn.isGroup()) {
+        if (record.getSbn().isGroup()) {
             if (notification.suppressAlertingDueToGrouping()) {
                 return true;
             }
         }
 
         // Suppressed for being too recently noisy
-        final String pkg = record.sbn.getPackageName();
+        final String pkg = record.getSbn().getPackageName();
         if (mUsageStats.isAlertRateLimited(pkg)) {
             Slog.e(TAG, "Muting recently noisy " + record.getKey());
             return true;
@@ -6854,7 +6882,7 @@
                 if (player != null) {
                     if (DBG) Slog.v(TAG, "Playing sound " + soundUri
                             + " with attributes " + record.getAudioAttributes());
-                    player.playAsync(soundUri, record.sbn.getUser(), looping,
+                    player.playAsync(soundUri, record.getSbn().getUser(), looping,
                             record.getAudioAttributes());
                     return true;
                 }
@@ -6897,15 +6925,16 @@
                     // so need to check the notification still valide for vibrate.
                     synchronized (mNotificationLock) {
                         if (mNotificationsByKey.get(record.getKey()) != null) {
-                            mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+                            mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getOpPkg(),
                                     effect, "Notification (delayed)", record.getAudioAttributes());
                         } else {
-                            Slog.e(TAG, "No vibration for canceled notification : " + record.getKey());
+                            Slog.e(TAG, "No vibration for canceled notification : "
+                                    + record.getKey());
                         }
                     }
                 }).start();
             } else {
-                mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
+                mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getPackageName(),
                         effect, "Notification", record.getAudioAttributes());
             }
             return true;
@@ -7388,7 +7417,7 @@
         if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
                 != null) {
             mNotificationList.remove(recordInList);
-            mNotificationsByKey.remove(recordInList.sbn.getKey());
+            mNotificationsByKey.remove(recordInList.getSbn().getKey());
             wasPosted = true;
         }
         while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
@@ -7429,7 +7458,7 @@
                 } catch (PendingIntent.CanceledException ex) {
                     // do nothing - there's no relevant way to recover, and
                     //     no reason to let this propagate
-                    Slog.w(TAG, "canceled PendingIntent for " + r.sbn.getPackageName(), ex);
+                    Slog.w(TAG, "canceled PendingIntent for " + r.getSbn().getPackageName(), ex);
                 }
             }
         }
@@ -7445,7 +7474,7 @@
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        mGroupHelper.onNotificationRemoved(r.sbn);
+                        mGroupHelper.onNotificationRemoved(r.getSbn());
                     }
                 });
             }
@@ -7501,13 +7530,15 @@
         if (groupSummary != null && groupSummary.getKey().equals(canceledKey)) {
             mSummaryByGroupKey.remove(groupKey);
         }
-        final ArrayMap<String, String> summaries = mAutobundledSummaries.get(r.sbn.getUserId());
-        if (summaries != null && r.sbn.getKey().equals(summaries.get(r.sbn.getPackageName()))) {
-            summaries.remove(r.sbn.getPackageName());
+        final ArrayMap<String, String> summaries =
+                mAutobundledSummaries.get(r.getSbn().getUserId());
+        if (summaries != null && r.getSbn().getKey().equals(
+                summaries.get(r.getSbn().getPackageName()))) {
+            summaries.remove(r.getSbn().getPackageName());
         }
 
         // Save it for users of getHistoricalNotifications()
-        mArchive.record(r.sbn);
+        mArchive.record(r.getSbn());
 
         final long now = System.currentTimeMillis();
         final LogMaker logMaker = r.getItemLogMaker()
@@ -7563,7 +7594,7 @@
             for (int i = 0; i < newUris.size(); i++) {
                 final Uri uri = newUris.valueAt(i);
                 if (oldUris == null || !oldUris.contains(uri)) {
-                    if (DBG) Slog.d(TAG, key + ": granting " + uri);
+                    Slog.d(TAG, key + ": granting " + uri);
                     grantUriPermission(permissionOwner, uri, newRecord.getUid(), targetPkg,
                             targetUserId);
                 }
@@ -7600,6 +7631,8 @@
                     targetUserId);
         } catch (RemoteException ignored) {
             // Ignored because we're in same process
+        } catch (SecurityException e) {
+            Slog.e(TAG, "Cannot grant uri access; " + sourceUid + " does not own " + uri);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -7756,7 +7789,7 @@
             if (!flagChecker.apply(r.getFlags())) {
                 continue;
             }
-            if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
+            if (pkg != null && !r.getSbn().getPackageName().equals(pkg)) {
                 continue;
             }
             if (channelId != null && !channelId.equals(r.getChannel().getId())) {
@@ -7852,7 +7885,7 @@
             return;
         }
 
-        String pkg = r.sbn.getPackageName();
+        String pkg = r.getSbn().getPackageName();
 
         if (pkg == null) {
             if (DBG) Slog.e(TAG, "No package for group summary: " + r.getKey());
@@ -7869,12 +7902,12 @@
     private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList,
             NotificationRecord parentNotification, int callingUid, int callingPid,
             String listenerName, boolean sendDelete, boolean wasPosted, FlagChecker flagChecker) {
-        final String pkg = parentNotification.sbn.getPackageName();
+        final String pkg = parentNotification.getSbn().getPackageName();
         final int userId = parentNotification.getUserId();
         final int reason = REASON_GROUP_SUMMARY_CANCELED;
         for (int i = notificationList.size() - 1; i >= 0; i--) {
             final NotificationRecord childR = notificationList.get(i);
-            final StatusBarNotification childSbn = childR.sbn;
+            final StatusBarNotification childSbn = childR.getSbn();
             if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
                     childR.getGroupKey().equals(parentNotification.getGroupKey())
                     && (childR.getFlags() & FLAG_FOREGROUND_SERVICE) == 0
@@ -7952,7 +7985,7 @@
         for (int i = 0; i < len; i++) {
             NotificationRecord r = list.get(i);
             if (notificationMatchesUserId(r, userId) && r.getGroupKey().equals(groupKey)
-                    && r.sbn.getPackageName().equals(pkg)) {
+                    && r.getSbn().getPackageName().equals(pkg)) {
                 records.add(r);
             }
         }
@@ -7993,8 +8026,9 @@
         final int len = list.size();
         for (int i = 0; i < len; i++) {
             NotificationRecord r = list.get(i);
-            if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
-                    TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
+            if (notificationMatchesUserId(r, userId) && r.getSbn().getId() == id &&
+                    TextUtils.equals(r.getSbn().getTag(), tag)
+                    && r.getSbn().getPackageName().equals(pkg)) {
                 return r;
             }
         }
@@ -8008,8 +8042,9 @@
         final int len = list.size();
         for (int i = 0; i < len; i++) {
             NotificationRecord r = list.get(i);
-            if (notificationMatchesUserId(r, userId) && r.sbn.getId() == id &&
-                    TextUtils.equals(r.sbn.getTag(), tag) && r.sbn.getPackageName().equals(pkg)) {
+            if (notificationMatchesUserId(r, userId) && r.getSbn().getId() == id &&
+                    TextUtils.equals(r.getSbn().getTag(), tag)
+                    && r.getSbn().getPackageName().equals(pkg)) {
                 matching.add(r);
             }
         }
@@ -8047,7 +8082,7 @@
             int numNotifications = mNotificationList.size();
             for (int i = 0; i < numNotifications; i++) {
                 NotificationRecord rec = mNotificationList.get(i);
-                if (pkgList.contains(rec.sbn.getPackageName())) {
+                if (pkgList.contains(rec.getSbn().getPackageName())) {
                     rec.setHidden(true);
                     changedNotifications.add(rec);
                 }
@@ -8065,7 +8100,7 @@
             int numNotifications = mNotificationList.size();
             for (int i = 0; i < numNotifications; i++) {
                 NotificationRecord rec = mNotificationList.get(i);
-                if (pkgList.contains(rec.sbn.getPackageName())) {
+                if (pkgList.contains(rec.getSbn().getPackageName())) {
                     rec.setHidden(false);
                     changedNotifications.add(rec);
                 }
@@ -8264,10 +8299,10 @@
 
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
-            if (!isVisibleToListener(record.sbn, info)) {
+            if (!isVisibleToListener(record.getSbn(), info)) {
                 continue;
             }
-            final String key = record.sbn.getKey();
+            final String key = record.getSbn().getKey();
             final NotificationListenerService.Ranking ranking =
                     new NotificationListenerService.Ranking();
             ranking.populate(
@@ -8278,7 +8313,7 @@
                     record.getSuppressedVisualEffects(),
                     record.getImportance(),
                     record.getImportanceExplanation(),
-                    record.sbn.getOverrideGroupKey(),
+                    record.getSbn().getOverrideGroupKey(),
                     record.getChannel(),
                     record.getPeopleOverride(),
                     record.getSnoozeCriteria(),
@@ -8550,7 +8585,7 @@
             for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
                 ArrayList<String> keys = new ArrayList<>(records.size());
                 for (NotificationRecord r : records) {
-                    boolean sbnVisible = isVisibleToListener(r.sbn, info)
+                    boolean sbnVisible = isVisibleToListener(r.getSbn(), info)
                             && info.isSameUser(r.getUserId());
                     if (sbnVisible) {
                         keys.add(r.getKey());
@@ -8638,7 +8673,7 @@
             if (debug) {
                 Slog.v(TAG, "onNotificationEnqueuedLocked() called with: r = [" + r + "]");
             }
-            final StatusBarNotification sbn = r.sbn;
+            final StatusBarNotification sbn = r.getSbn();
             notifyAssistantLocked(
                     sbn,
                     true /* sameUserOnly */,
@@ -8977,59 +9012,64 @@
         @GuardedBy("mNotificationLock")
         private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                 boolean notifyAllListeners) {
-            // Lazily initialized snapshots of the notification.
-            StatusBarNotification sbn = r.sbn;
-            StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
-            TrimCache trimCache = new TrimCache(sbn);
+            try {
+                // Lazily initialized snapshots of the notification.
+                StatusBarNotification sbn = r.getSbn();
+                StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
+                TrimCache trimCache = new TrimCache(sbn);
 
-            for (final ManagedServiceInfo info : getServices()) {
-                boolean sbnVisible = isVisibleToListener(sbn, info);
-                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
-                // This notification hasn't been and still isn't visible -> ignore.
-                if (!oldSbnVisible && !sbnVisible) {
-                    continue;
-                }
-                // If the notification is hidden, don't notifyPosted listeners targeting < P.
-                // Instead, those listeners will receive notifyPosted when the notification is
-                // unhidden.
-                if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
-                    continue;
-                }
+                for (final ManagedServiceInfo info : getServices()) {
+                    boolean sbnVisible = isVisibleToListener(sbn, info);
+                    boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info)
+                            : false;
+                    // This notification hasn't been and still isn't visible -> ignore.
+                    if (!oldSbnVisible && !sbnVisible) {
+                        continue;
+                    }
+                    // If the notification is hidden, don't notifyPosted listeners targeting < P.
+                    // Instead, those listeners will receive notifyPosted when the notification is
+                    // unhidden.
+                    if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
+                        continue;
+                    }
 
-                // If we shouldn't notify all listeners, this means the hidden state of
-                // a notification was changed.  Don't notifyPosted listeners targeting >= P.
-                // Instead, those listeners will receive notifyRankingUpdate.
-                if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
-                    continue;
-                }
+                    // If we shouldn't notify all listeners, this means the hidden state of
+                    // a notification was changed.  Don't notifyPosted listeners targeting >= P.
+                    // Instead, those listeners will receive notifyRankingUpdate.
+                    if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
+                        continue;
+                    }
 
-                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
+                    final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
 
-                // This notification became invisible -> remove the old one.
-                if (oldSbnVisible && !sbnVisible) {
-                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
+                    // This notification became invisible -> remove the old one.
+                    if (oldSbnVisible && !sbnVisible) {
+                        final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
+                        mHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                notifyRemoved(
+                                        info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
+                            }
+                        });
+                        continue;
+                    }
+
+                    // Grant access before listener is notified
+                    final int targetUserId = (info.userid == UserHandle.USER_ALL)
+                            ? UserHandle.USER_SYSTEM : info.userid;
+                    updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
+
+                    final StatusBarNotification sbnToPost = trimCache.ForListener(info);
                     mHandler.post(new Runnable() {
                         @Override
                         public void run() {
-                            notifyRemoved(
-                                    info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
+                            notifyPosted(info, sbnToPost, update);
                         }
                     });
-                    continue;
                 }
-
-                // Grant access before listener is notified
-                final int targetUserId = (info.userid == UserHandle.USER_ALL)
-                        ? UserHandle.USER_SYSTEM : info.userid;
-                updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
-
-                final StatusBarNotification sbnToPost = trimCache.ForListener(info);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        notifyPosted(info, sbnToPost, update);
-                    }
-                });
+            } catch (Exception e) {
+                Slog.e(TAG, "Could not notify listeners for " + r.getKey(), e);
             }
         }
 
@@ -9039,7 +9079,7 @@
         @GuardedBy("mNotificationLock")
         public void notifyRemovedLocked(NotificationRecord r, int reason,
                 NotificationStats notificationStats) {
-            final StatusBarNotification sbn = r.sbn;
+            final StatusBarNotification sbn = r.getSbn();
 
             // make a copy in case changes are made to the underlying Notification object
             // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
@@ -9103,7 +9143,7 @@
                 if (isHiddenRankingUpdate && serviceInfo.targetSdkVersion >=
                         Build.VERSION_CODES.P) {
                     for (NotificationRecord rec : changedHiddenNotifications) {
-                        if (isVisibleToListener(rec.sbn, serviceInfo)) {
+                        if (isVisibleToListener(rec.getSbn(), serviceInfo)) {
                             notifyThisListener = true;
                             break;
                         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 660d574..4785da9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -91,7 +91,7 @@
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
     // the period after which a notification is updated where it can make sound
     private static final int MAX_SOUND_DELAY_MS = 2000;
-    final StatusBarNotification sbn;
+    private final StatusBarNotification sbn;
     IActivityManager mAm;
     UriGrantsManagerInternal mUgmInternal;
     final int mTargetSdkVersion;
@@ -229,7 +229,7 @@
     }
 
     private Uri calculateSound() {
-        final Notification n = sbn.getNotification();
+        final Notification n = getSbn().getNotification();
 
         // No notification sounds on tv
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
@@ -265,7 +265,7 @@
         if (mPreChannelsNotification
                 && (getChannel().getUserLockedFields()
                 & NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
-            final Notification notification = sbn.getNotification();
+            final Notification notification = getSbn().getNotification();
             if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
                 light = new Light(notification.ledARGB, notification.ledOnMS,
                         notification.ledOffMS);
@@ -296,7 +296,7 @@
         if (mPreChannelsNotification
                 && (getChannel().getUserLockedFields()
                 & NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
-            final Notification notification = sbn.getNotification();
+            final Notification notification = getSbn().getNotification();
             final boolean useDefaultVibrate =
                     (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
             if (useDefaultVibrate) {
@@ -309,7 +309,7 @@
     }
 
     private AudioAttributes calculateAttributes() {
-        final Notification n = sbn.getNotification();
+        final Notification n = getSbn().getNotification();
         AudioAttributes attributes = getChannel().getAudioAttributes();
         if (attributes == null) {
             attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
@@ -335,7 +335,7 @@
     }
 
     private int calculateInitialImportance() {
-        final Notification n = sbn.getNotification();
+        final Notification n = getSbn().getNotification();
         int importance = getChannel().getImportance();  // Post-channels notifications use this
         mInitialImportanceExplanationCode = getChannel().hasUserSetImportance()
                 ? MetricsEvent.IMPORTANCE_EXPLANATION_USER
@@ -406,31 +406,31 @@
         mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
         mCreationTimeMs = previous.mCreationTimeMs;
         mVisibleSinceMs = previous.mVisibleSinceMs;
-        if (previous.sbn.getOverrideGroupKey() != null && !sbn.isAppGroup()) {
-            sbn.setOverrideGroupKey(previous.sbn.getOverrideGroupKey());
+        if (previous.getSbn().getOverrideGroupKey() != null && !getSbn().isAppGroup()) {
+            getSbn().setOverrideGroupKey(previous.getSbn().getOverrideGroupKey());
         }
         // Don't copy importance information or mGlobalSortKey, recompute them.
     }
 
-    public Notification getNotification() { return sbn.getNotification(); }
-    public int getFlags() { return sbn.getNotification().flags; }
-    public UserHandle getUser() { return sbn.getUser(); }
-    public String getKey() { return sbn.getKey(); }
+    public Notification getNotification() { return getSbn().getNotification(); }
+    public int getFlags() { return getSbn().getNotification().flags; }
+    public UserHandle getUser() { return getSbn().getUser(); }
+    public String getKey() { return getSbn().getKey(); }
     /** @deprecated Use {@link #getUser()} instead. */
-    public int getUserId() { return sbn.getUserId(); }
-    public int getUid() { return sbn.getUid(); }
+    public int getUserId() { return getSbn().getUserId(); }
+    public int getUid() { return getSbn().getUid(); }
 
     void dump(ProtoOutputStream proto, long fieldId, boolean redact, int state) {
         final long token = proto.start(fieldId);
 
-        proto.write(NotificationRecordProto.KEY, sbn.getKey());
+        proto.write(NotificationRecordProto.KEY, getSbn().getKey());
         proto.write(NotificationRecordProto.STATE, state);
         if (getChannel() != null) {
             proto.write(NotificationRecordProto.CHANNEL_ID, getChannel().getId());
         }
         proto.write(NotificationRecordProto.CAN_SHOW_LIGHT, getLight() != null);
         proto.write(NotificationRecordProto.CAN_VIBRATE, getVibration() != null);
-        proto.write(NotificationRecordProto.FLAGS, sbn.getNotification().flags);
+        proto.write(NotificationRecordProto.FLAGS, getSbn().getNotification().flags);
         proto.write(NotificationRecordProto.GROUP_KEY, getGroupKey());
         proto.write(NotificationRecordProto.IMPORTANCE, getImportance());
         if (getSound() != null) {
@@ -439,8 +439,8 @@
         if (getAudioAttributes() != null) {
             getAudioAttributes().dumpDebug(proto, NotificationRecordProto.AUDIO_ATTRIBUTES);
         }
-        proto.write(NotificationRecordProto.PACKAGE, sbn.getPackageName());
-        proto.write(NotificationRecordProto.DELEGATE_PACKAGE, sbn.getOpPkg());
+        proto.write(NotificationRecordProto.PACKAGE, getSbn().getPackageName());
+        proto.write(NotificationRecordProto.DELEGATE_PACKAGE, getSbn().getOpPkg());
 
         proto.end(token);
     }
@@ -452,15 +452,15 @@
     }
 
     void dump(PrintWriter pw, String prefix, Context baseContext, boolean redact) {
-        final Notification notification = sbn.getNotification();
+        final Notification notification = getSbn().getNotification();
         pw.println(prefix + this);
         prefix = prefix + "  ";
-        pw.println(prefix + "uid=" + sbn.getUid() + " userId=" + sbn.getUserId());
-        pw.println(prefix + "opPkg=" + sbn.getOpPkg());
+        pw.println(prefix + "uid=" + getSbn().getUid() + " userId=" + getSbn().getUserId());
+        pw.println(prefix + "opPkg=" + getSbn().getOpPkg());
         pw.println(prefix + "icon=" + notification.getSmallIcon());
         pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
         pw.println(prefix + "pri=" + notification.priority);
-        pw.println(prefix + "key=" + sbn.getKey());
+        pw.println(prefix + "key=" + getSbn().getKey());
         pw.println(prefix + "seen=" + mStats.hasSeen());
         pw.println(prefix + "groupKey=" + getGroupKey());
         pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
@@ -594,9 +594,9 @@
                 "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s importance=%d key=%s" +
                         ": %s)",
                 System.identityHashCode(this),
-                this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(),
-                this.sbn.getTag(), this.mImportance, this.sbn.getKey(),
-                this.sbn.getNotification());
+                this.getSbn().getPackageName(), this.getSbn().getUser(), this.getSbn().getId(),
+                this.getSbn().getTag(), this.mImportance, this.getSbn().getKey(),
+                this.getSbn().getNotification());
     }
 
     public boolean hasAdjustment(String key) {
@@ -936,7 +936,7 @@
     private long calculateRankingTimeMs(long previousRankingTimeMs) {
         Notification n = getNotification();
         // Take developer provided 'when', unless it's in the future.
-        if (n.when != 0 && n.when <= sbn.getPostTime()) {
+        if (n.when != 0 && n.when <= getSbn().getPostTime()) {
             return n.when;
         }
         // If we've ranked a previous instance with a timestamp, inherit it. This case is
@@ -944,7 +944,7 @@
         if (previousRankingTimeMs > 0) {
             return previousRankingTimeMs;
         }
-        return sbn.getPostTime();
+        return getSbn().getPostTime();
     }
 
     public void setGlobalSortKey(String globalSortKey) {
@@ -977,11 +977,11 @@
     }
 
     public String getGroupKey() {
-        return sbn.getGroupKey();
+        return getSbn().getGroupKey();
     }
 
     public void setOverrideGroupKey(String overrideGroupKey) {
-        sbn.setOverrideGroupKey(overrideGroupKey);
+        getSbn().setOverrideGroupKey(overrideGroupKey);
     }
 
     public NotificationChannel getChannel() {
@@ -1202,7 +1202,7 @@
      * Returns whether this notification was posted by a secondary app
      */
     public boolean isProxied() {
-        return !Objects.equals(sbn.getPackageName(), sbn.getOpPkg());
+        return !Objects.equals(getSbn().getPackageName(), getSbn().getOpPkg());
     }
 
     /**
@@ -1245,7 +1245,7 @@
         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
 
         // We can't grant Uri permissions from system
-        final int sourceUid = sbn.getUid();
+        final int sourceUid = getSbn().getUid();
         if (sourceUid == android.os.Process.SYSTEM_UID) return;
 
         final long ident = Binder.clearCallingIdentity();
@@ -1274,7 +1274,7 @@
     }
 
     public LogMaker getLogMaker(long now) {
-        LogMaker lm = sbn.getLogMaker()
+        LogMaker lm = getSbn().getLogMaker()
                 .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE, mImportance)
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
                 .addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
@@ -1351,6 +1351,10 @@
         return true;
     }
 
+    StatusBarNotification getSbn() {
+        return sbn;
+    }
+
     @VisibleForTesting
     static final class Light {
         public final int color;
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 9bbc3924..8d8511f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -88,12 +88,12 @@
                 return true;
             }
 
-            return !(Objects.equals(r.sbn.getChannelIdLogTag(), old.sbn.getChannelIdLogTag())
-                    && Objects.equals(r.sbn.getGroupLogTag(), old.sbn.getGroupLogTag())
-                    && (r.sbn.getNotification().isGroupSummary()
-                        == old.sbn.getNotification().isGroupSummary())
-                    && Objects.equals(r.sbn.getNotification().category,
-                        old.sbn.getNotification().category)
+            return !(Objects.equals(r.getSbn().getChannelIdLogTag(), old.getSbn().getChannelIdLogTag())
+                    && Objects.equals(r.getSbn().getGroupLogTag(), old.getSbn().getGroupLogTag())
+                    && (r.getSbn().getNotification().isGroupSummary()
+                        == old.getSbn().getNotification().isGroupSummary())
+                    && Objects.equals(r.getSbn().getNotification().category,
+                        old.getSbn().getNotification().category)
                     && (r.getImportance() == old.getImportance()));
         }
 
@@ -106,7 +106,7 @@
          * @return hash code for the notification style class, or 0 if none exists.
          */
         public int getStyle() {
-            return getStyle(r.sbn.getNotification().extras);
+            return getStyle(r.getSbn().getNotification().extras);
         }
 
         private int getStyle(@Nullable Bundle extras) {
@@ -120,7 +120,7 @@
         }
 
         int getNumPeople() {
-            return getNumPeople(r.sbn.getNotification().extras);
+            return getNumPeople(r.getSbn().getNotification().extras);
         }
 
         private int getNumPeople(@Nullable Bundle extras) {
@@ -140,7 +140,7 @@
         }
 
         int getInstanceId() {
-            return (r.sbn.getInstanceId() == null ? 0 : r.sbn.getInstanceId().getId());
+            return (r.getSbn().getInstanceId() == null ? 0 : r.getSbn().getInstanceId().getId());
         }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index d613799..4974c30 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -34,15 +34,15 @@
         FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
                 /* int32 event_id = 1 */ p.getUiEvent().getId(),
                 /* int32 uid = 2 */ r.getUid(),
-                /* string package_name = 3 */ r.sbn.getPackageName(),
+                /* string package_name = 3 */ r.getSbn().getPackageName(),
                 /* int32 instance_id = 4 */ p.getInstanceId(),
-                /* int32 notification_id = 5 */ r.sbn.getId(),
-                /* string notification_tag = 6 */ r.sbn.getTag(),
-                /* string channel_id = 7 */ r.sbn.getChannelIdLogTag(),
-                /* string group_id = 8 */ r.sbn.getGroupLogTag(),
+                /* int32 notification_id = 5 */ r.getSbn().getId(),
+                /* string notification_tag = 6 */ r.getSbn().getTag(),
+                /* string channel_id = 7 */ r.getSbn().getChannelIdLogTag(),
+                /* string group_id = 8 */ r.getSbn().getGroupLogTag(),
                 /* int32 group_instance_id = 9 */ 0, // TODO generate and fill instance ids
-                /* bool is_group_summary = 10 */ r.sbn.getNotification().isGroupSummary(),
-                /* string category = 11 */ r.sbn.getNotification().category,
+                /* bool is_group_summary = 10 */ r.getSbn().getNotification().isGroupSummary(),
+                /* string category = 11 */ r.getSbn().getNotification().category,
                 /* int32 style = 12 */ p.getStyle(),
                 /* int32 num_people = 13 */ p.getNumPeople(),
                 /* int32 position = 14 */ position,
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index d1fe0d9..b42fe92 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -276,7 +276,7 @@
 
     // Locked by this.
     private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) {
-        return getAggregatedStatsLocked(record.sbn.getPackageName());
+        return getAggregatedStatsLocked(record.getSbn().getPackageName());
     }
 
     // Locked by this.
@@ -1142,7 +1142,7 @@
                     long nowMs = System.currentTimeMillis();
                     switch (msg.what) {
                         case MSG_POST:
-                            writeEvent(r.sbn.getPostTime(), EVENT_TYPE_POST, r);
+                            writeEvent(r.getSbn().getPostTime(), EVENT_TYPE_POST, r);
                             break;
                         case MSG_CLICK:
                             writeEvent(nowMs, EVENT_TYPE_CLICK, r);
@@ -1287,7 +1287,7 @@
 
         private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r) {
             ContentValues cv = new ContentValues();
-            cv.put(COL_EVENT_USER_ID, r.sbn.getUser().getIdentifier());
+            cv.put(COL_EVENT_USER_ID, r.getSbn().getUser().getIdentifier());
             cv.put(COL_EVENT_TIME, eventTimeMs);
             cv.put(COL_EVENT_TYPE, eventType);
             putNotificationIdentifiers(r, cv);
@@ -1324,16 +1324,16 @@
         }
 
         private static void putNotificationIdentifiers(NotificationRecord r, ContentValues outCv) {
-            outCv.put(COL_KEY, r.sbn.getKey());
-            outCv.put(COL_PKG, r.sbn.getPackageName());
+            outCv.put(COL_KEY, r.getSbn().getKey());
+            outCv.put(COL_PKG, r.getSbn().getPackageName());
         }
 
         private static void putNotificationDetails(NotificationRecord r, ContentValues outCv) {
-            outCv.put(COL_NOTIFICATION_ID, r.sbn.getId());
-            if (r.sbn.getTag() != null) {
-                outCv.put(COL_TAG, r.sbn.getTag());
+            outCv.put(COL_NOTIFICATION_ID, r.getSbn().getId());
+            if (r.getSbn().getTag() != null) {
+                outCv.put(COL_TAG, r.getSbn().getTag());
             }
-            outCv.put(COL_WHEN_MS, r.sbn.getPostTime());
+            outCv.put(COL_WHEN_MS, r.getSbn().getPostTime());
             outCv.put(COL_FLAGS, r.getNotification().flags);
             final int before = r.stats.requestedImportance;
             final int after = r.getImportance();
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b0c1863..fe39322 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1380,7 +1380,8 @@
                 policy.priorityCategories, policy.priorityCallSenders,
                 policy.priorityMessageSenders, policy.suppressedVisualEffects,
                 (areChannelsBypassingDnd ? NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND
-                        : 0)));
+                        : 0),
+                policy.priorityConversationSenders));
     }
 
     public boolean areChannelsBypassingDnd() {
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 9e32d0e..661297a 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -166,7 +166,7 @@
             ArrayMap<String, NotificationRecord> packages =
                     mSnoozedNotifications.get(userId).get(pkg);
             for (int i = 0; i < packages.size(); i++) {
-                String currentGroupKey = packages.valueAt(i).sbn.getGroup();
+                String currentGroupKey = packages.valueAt(i).getSbn().getGroup();
                 if (currentGroupKey.equals(groupKey)) {
                     records.add(packages.valueAt(i));
                 }
@@ -223,7 +223,7 @@
      * Snoozes a notification and schedules an alarm to repost at that time.
      */
     protected void snooze(NotificationRecord record, long duration) {
-        String pkg = record.sbn.getPackageName();
+        String pkg = record.getSbn().getPackageName();
         String key = record.getKey();
         int userId = record.getUser().getIdentifier();
 
@@ -242,7 +242,7 @@
         int userId = record.getUser().getIdentifier();
         if (contextId != null) {
             synchronized (mPersistedSnoozedNotificationsWithContext) {
-                storeRecord(record.sbn.getPackageName(), record.getKey(),
+                storeRecord(record.getSbn().getPackageName(), record.getKey(),
                         userId, mPersistedSnoozedNotificationsWithContext, contextId);
             }
         }
@@ -254,9 +254,9 @@
         if (DEBUG) {
             Slog.d(TAG, "Snoozing " + record.getKey());
         }
-        storeRecord(record.sbn.getPackageName(), record.getKey(),
+        storeRecord(record.getSbn().getPackageName(), record.getKey(),
                 userId, mSnoozedNotifications, record);
-        mPackages.put(record.getKey(), record.sbn.getPackageName());
+        mPackages.put(record.getKey(), record.getSbn().getPackageName());
         mUsers.put(record.getKey(), userId);
     }
 
@@ -308,7 +308,7 @@
             if (recordsForPkg != null) {
                 final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
                 for (Map.Entry<String, NotificationRecord> record : records) {
-                    final StatusBarNotification sbn = record.getValue().sbn;
+                    final StatusBarNotification sbn = record.getValue().getSbn();
                     if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
                         record.getValue().isCanceled = true;
                         return true;
@@ -369,7 +369,7 @@
         if (records == null) {
             return;
         }
-        ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.sbn.getPackageName());
+        ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.getSbn().getPackageName());
         if (pkgRecords == null) {
             return;
         }
@@ -420,7 +420,7 @@
                     int N = recordsByKey.size();
                     for (int i = 0; i < N; i++) {
                         final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
-                        if (potentialGroupSummary.sbn.isGroup()
+                        if (potentialGroupSummary.getSbn().isGroup()
                                 && potentialGroupSummary.getNotification().isGroupSummary()
                                 && groupKey.equals(potentialGroupSummary.getGroupKey())) {
                             groupSummaryKey = potentialGroupSummary.getKey();
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 6045f6c..4d19855 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -106,8 +107,8 @@
     }
 
     private static Bundle extras(NotificationRecord record) {
-        return record != null && record.sbn != null && record.sbn.getNotification() != null
-                ? record.sbn.getNotification().extras : null;
+        return record != null && record.getSbn() != null && record.getSbn().getNotification() != null
+                ? record.getSbn().getNotification().extras : null;
     }
 
     protected void recordCall(NotificationRecord record) {
@@ -125,8 +126,8 @@
         }
         // Make an exception to policy for the notification saying that policy has changed
         if (NotificationManager.Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)
-                && "android".equals(record.sbn.getPackageName())
-                && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.sbn.getId()) {
+                && "android".equals(record.getSbn().getPackageName())
+                && SystemMessageProto.SystemMessage.NOTE_ZEN_UPGRADE == record.getSbn().getId()) {
             ZenLog.traceNotIntercepted(record, "systemDndChangedNotification");
             return false;
         }
@@ -156,25 +157,6 @@
                     }
                     return false;
                 }
-                if (isCall(record)) {
-                    if (policy.allowRepeatCallers()
-                            && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
-                        ZenLog.traceNotIntercepted(record, "repeatCaller");
-                        return false;
-                    }
-                    if (!policy.allowCalls()) {
-                        ZenLog.traceIntercepted(record, "!allowCalls");
-                        return true;
-                    }
-                    return shouldInterceptAudience(policy.allowCallsFrom(), record);
-                }
-                if (isMessage(record)) {
-                    if (!policy.allowMessages()) {
-                        ZenLog.traceIntercepted(record, "!allowMessages");
-                        return true;
-                    }
-                    return shouldInterceptAudience(policy.allowMessagesFrom(), record);
-                }
                 if (isEvent(record)) {
                     if (!policy.allowEvents()) {
                         ZenLog.traceIntercepted(record, "!allowEvents");
@@ -203,6 +185,41 @@
                     }
                     return false;
                 }
+                if (isConversation(record)) {
+                    if (policy.allowConversations()) {
+                        if (policy.priorityConversationSenders == CONVERSATION_SENDERS_ANYONE) {
+                            ZenLog.traceNotIntercepted(record, "conversationAnyone");
+                            return false;
+                        } else if (policy.priorityConversationSenders
+                                == NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT
+                                && record.getChannel().isImportantConversation()) {
+                            ZenLog.traceNotIntercepted(record, "conversationMatches");
+                            return false;
+                        }
+                    }
+                    // if conversations aren't allowed record might still be allowed thanks
+                    // to call or message metadata, so don't return yet
+                }
+                if (isCall(record)) {
+                    if (policy.allowRepeatCallers()
+                            && REPEAT_CALLERS.isRepeat(mContext, extras(record))) {
+                        ZenLog.traceNotIntercepted(record, "repeatCaller");
+                        return false;
+                    }
+                    if (!policy.allowCalls()) {
+                        ZenLog.traceIntercepted(record, "!allowCalls");
+                        return true;
+                    }
+                    return shouldInterceptAudience(policy.allowCallsFrom(), record);
+                }
+                if (isMessage(record)) {
+                    if (!policy.allowMessages()) {
+                        ZenLog.traceIntercepted(record, "!allowMessages");
+                        return true;
+                    }
+                    return shouldInterceptAudience(policy.allowMessagesFrom(), record);
+                }
+
                 ZenLog.traceIntercepted(record, "!priority");
                 return true;
             default:
@@ -245,7 +262,7 @@
     }
 
     public boolean isCall(NotificationRecord record) {
-        return record != null && (isDefaultPhoneApp(record.sbn.getPackageName())
+        return record != null && (isDefaultPhoneApp(record.getSbn().getPackageName())
                 || record.isCategory(Notification.CATEGORY_CALL));
     }
 
@@ -273,7 +290,11 @@
     }
 
     protected boolean isMessage(NotificationRecord record) {
-        return mMessagingUtil.isMessaging(record.sbn);
+        return mMessagingUtil.isMessaging(record.getSbn());
+    }
+
+    protected boolean isConversation(NotificationRecord record) {
+        return record.isConversation();
     }
 
     private static boolean audienceMatches(int source, float contactAffinity) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 696d2ea..3b564c3 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -641,9 +641,11 @@
     }
 
     public void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("mZenMode=");
+        pw.print(prefix);
+        pw.print("mZenMode=");
         pw.println(Global.zenModeToString(mZenMode));
-        pw.print("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
+        pw.print(prefix);
+        pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
         final int N = mConfigs.size();
         for (int i = 0; i < N; i++) {
             dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
@@ -665,13 +667,17 @@
             return;
         }
         pw.printf("allow(alarms=%b,media=%b,system=%b,calls=%b,callsFrom=%s,repeatCallers=%b,"
-                + "messages=%b,messagesFrom=%s,events=%b,reminders=%b)\n",
+                + "messages=%b,messagesFrom=%s,conversations=%b,conversationsFrom=%s,"
+                        + "events=%b,reminders=%b)\n",
                 config.allowAlarms, config.allowMedia, config.allowSystem,
                 config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
                 config.allowRepeatCallers, config.allowMessages,
                 ZenModeConfig.sourceToString(config.allowMessagesFrom),
+                config.allowConversations,
+                ZenPolicy.conversationTypeToString(config.allowConversationsFrom),
                 config.allowEvents, config.allowReminders);
-        pw.printf(" disallow(visualEffects=%s)\n", config.suppressedVisualEffects);
+        pw.print(prefix);
+        pw.printf("  disallow(visualEffects=%s)\n", config.suppressedVisualEffects);
         pw.print(prefix); pw.print("  manualRule="); pw.println(config.manualRule);
         if (config.automaticRules.isEmpty()) return;
         final int N = config.automaticRules.size();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 79cf23f..8e5ff66 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1537,15 +1537,16 @@
     final @NonNull String mRequiredPermissionControllerPackage;
     final @Nullable String mSetupWizardPackage;
     final @Nullable String mStorageManagerPackage;
-    final @Nullable String mSystemTextClassifierPackage;
+    final @Nullable String mDefaultTextClassifierPackage;
+    final @Nullable String mSystemTextClassifierPackageName;
     final @Nullable String mWellbeingPackage;
     final @Nullable String mDocumenterPackage;
     final @Nullable String mConfiguratorPackage;
     final @Nullable String mAppPredictionServicePackage;
     final @Nullable String mIncidentReportApproverPackage;
     final @Nullable String[] mTelephonyPackages;
-    final @NonNull String mServicesExtensionPackageName;
-    final @NonNull String mSharedSystemSharedLibraryPackageName;
+    final @Nullable String mServicesExtensionPackageName;
+    final @Nullable String mSharedSystemSharedLibraryPackageName;
     final @Nullable String mRetailDemoPackage;
 
     private final PackageUsage mPackageUsage = new PackageUsage();
@@ -3155,8 +3156,8 @@
             mSetupWizardPackage = getSetupWizardPackageNameImpl();
             mComponentResolver.fixProtectedFilterPriorities();
 
-            mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
-
+            mDefaultTextClassifierPackage = getDefaultTextClassifierPackageName();
+            mSystemTextClassifierPackageName = getSystemTextClassifierPackageName();
             mWellbeingPackage = getWellbeingPackageName();
             mDocumenterPackage = getDocumenterPackageName();
             mConfiguratorPackage = getDeviceConfiguratorPackageName();
@@ -3351,6 +3352,7 @@
                 mServicesExtensionPackageName = null;
                 mSharedSystemSharedLibraryPackageName = null;
             }
+
             // PermissionController hosts default permission granting and role management, so it's a
             // critical part of the core system.
             mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
@@ -19626,15 +19628,15 @@
     }
 
     @Override
-    public String getSystemTextClassifierPackageName() {
-        return ensureSystemPackageName(mContext.getString(
-                R.string.config_defaultTextClassifierPackage));
+    public String getDefaultTextClassifierPackageName() {
+        return ensureSystemPackageName(
+                mContext.getString(R.string.config_servicesExtensionPackage));
     }
 
     @Override
-    public String[] getSystemTextClassifierPackages() {
-        return ensureSystemPackageNames(mContext.getResources().getStringArray(
-                R.array.config_defaultTextClassifierPackages));
+    public String getSystemTextClassifierPackageName() {
+        return ensureSystemPackageName(
+                mContext.getString(R.string.config_defaultTextClassifierPackage));
     }
 
     @Override
@@ -23063,7 +23065,7 @@
         }
 
         private String[] getKnownPackageNamesInternal(int knownPackage, int userId) {
-            switch(knownPackage) {
+            switch (knownPackage) {
                 case PackageManagerInternal.PACKAGE_BROWSER:
                     return new String[]{mPermissionManager.getDefaultBrowser(userId)};
                 case PackageManagerInternal.PACKAGE_INSTALLER:
@@ -23075,7 +23077,8 @@
                 case PackageManagerInternal.PACKAGE_VERIFIER:
                     return filterOnlySystemPackages(mRequiredVerifierPackage);
                 case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
-                    return filterOnlySystemPackages(mSystemTextClassifierPackage);
+                    return filterOnlySystemPackages(
+                            mDefaultTextClassifierPackage, mSystemTextClassifierPackageName);
                 case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
                     return filterOnlySystemPackages(mRequiredPermissionControllerPackage);
                 case PackageManagerInternal.PACKAGE_WELLBEING:
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 46893b2..e6eaf21 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -720,12 +720,9 @@
                 userId, STORAGE_PERMISSIONS);
 
         // TextClassifier Service
-        final String[] packages = mContext.getPackageManager().getSystemTextClassifierPackages();
-        if (packages.length > 0) {
-            // We have a list of supported system TextClassifier package names, the first one
-            // package is the default system TextClassifier service. Grant permissions to default
-            // TextClassifier Service.
-            grantPermissionsToSystemPackage(packages[0], userId,
+        for (String textClassifierPackage :
+                getKnownPackages(PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER, userId)) {
+            grantPermissionsToSystemPackage(textClassifierPackage, userId,
                     COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
         }
 
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index bf7413b..4d7af9c 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -93,19 +93,6 @@
      */
     static final int ROLLBACK_STATE_DELETED = 4;
 
-    @IntDef(flag = true, prefix = { "MATCH_" }, value = {
-            MATCH_APK_IN_APEX,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface RollbackInfoFlags {}
-
-    /**
-     * {@link RollbackInfo} flag: include {@code RollbackInfo} packages that are apk-in-apex.
-     * These packages do not have their own sessions. They are embedded in an apex which has a
-     * session id.
-     */
-    static final int MATCH_APK_IN_APEX = 1;
-
     /**
      * The session ID for the staged session if this rollback data represents a staged session,
      * {@code -1} otherwise.
@@ -783,33 +770,6 @@
     }
 
     /**
-     * Returns the number of {@link PackageRollbackInfo} we are storing in this {@link Rollback}
-     * instance. By default, this method does not include apk-in-apex package in the count.
-     *
-     * @param flags Apk-in-apex packages can be included in the count by passing
-     * {@link Rollback#MATCH_APK_IN_APEX}
-     *
-     * @return Counts number of {@link PackageRollbackInfo} stored in the {@link Rollback}
-     * according to {@code flags} passed
-     */
-    int getPackageCount(@RollbackInfoFlags int flags) {
-        synchronized (mLock) {
-            List<PackageRollbackInfo> packages = info.getPackages();
-            if ((flags & MATCH_APK_IN_APEX) != 0) {
-                return packages.size();
-            }
-
-            int packagesWithoutApkInApex = 0;
-            for (PackageRollbackInfo rollbackInfo : packages) {
-                if (!rollbackInfo.isApkInApex()) {
-                    packagesWithoutApkInApex++;
-                }
-            }
-            return packagesWithoutApkInApex;
-        }
-    }
-
-    /**
      * Adds a rollback token to be associated with this rollback. This may be used to
      * identify which rollback should be removed in case {@link PackageManager} sends an
      * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} intent.
@@ -842,13 +802,6 @@
     }
 
     /**
-     * Returns the number of package session ids in this rollback.
-     */
-    int getPackageSessionIdCount() {
-        return mPackageSessionIds.length;
-    }
-
-    /**
      * Called when a child session finished with success.
      * Returns true when all child sessions are notified with success. This rollback will be
      * enabled only after all child sessions finished with success.
@@ -859,6 +812,23 @@
         }
     }
 
+    /**
+     * Returns true if all packages in this rollback are enabled. We won't enable this rollback
+     * until all packages are enabled. Note we don't count apk-in-apex here since they are enabled
+     * automatically when the embedding apex is enabled.
+     */
+    boolean allPackagesEnabled() {
+        synchronized (mLock) {
+            int packagesWithoutApkInApex = 0;
+            for (PackageRollbackInfo rollbackInfo : info.getPackages()) {
+                if (!rollbackInfo.isApkInApex()) {
+                    packagesWithoutApkInApex++;
+                }
+            }
+            return packagesWithoutApkInApex == mPackageSessionIds.length;
+        }
+    }
+
     static String rollbackStateToString(@RollbackState int state) {
         switch (state) {
             case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 3fa114e..8bd9533 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1237,8 +1237,7 @@
         // equal to the number of sessions we are installing, to ensure we didn't skip enabling
         // of any sessions. If we successfully enable an apex, then we can assume we enabled
         // rollback for the embedded apk-in-apex, if any.
-        // TODO: add a helper instead of exposing 2 methods from Rollback
-        if (rollback.getPackageCount(0 /*flags*/) != rollback.getPackageSessionIdCount()) {
+        if (!rollback.allPackagesEnabled()) {
             Slog.e(TAG, "Failed to enable rollback for all packages in session.");
             rollback.delete(mAppDataRollbackHelper);
             return null;
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 4f89482..7b046c1 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -199,11 +199,6 @@
      * Creates a new Rollback instance for a non-staged rollback with
      * backupDir assigned.
      */
-    Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName) {
-        File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
-        return new Rollback(rollbackId, backupDir, -1, userId, installerPackageName);
-    }
-
     Rollback createNonStagedRollback(int rollbackId, int userId, String installerPackageName,
             int[] packageSessionIds) {
         File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
@@ -216,16 +211,6 @@
      * backupDir assigned.
      */
     Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
-            String installerPackageName) {
-        File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
-        return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName);
-    }
-
-    /**
-     * TODO: Now we have 4 factory methods for creating Rollback objects which is verbose and
-     * cumbersome. Need to merge them for simplicity.
-     */
-    Rollback createStagedRollback(int rollbackId, int stagedSessionId, int userId,
             String installerPackageName, int[] packageSessionIds) {
         File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
         return new Rollback(rollbackId, backupDir, stagedSessionId, userId, installerPackageName,
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 3dee853..34d2c16 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -36,7 +36,7 @@
 import android.service.textclassifier.TextClassifierService;
 import android.service.textclassifier.TextClassifierService.ConnectionState;
 import android.text.TextUtils;
-import android.util.ArrayMap;
+import android.util.LruCache;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.textclassifier.ConversationActions;
@@ -63,7 +63,8 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 import java.util.Queue;
 
@@ -132,9 +133,7 @@
             synchronized (mManagerService.mLock) {
                 UserState userState = mManagerService.peekUserStateLocked(userId);
                 if (userState != null) {
-                    if (userState.mConnection != null) {
-                        userState.mConnection.cleanupService();
-                    }
+                    userState.cleanupServiceLocked();
                     mManagerService.mUserStates.remove(userId);
                 }
             }
@@ -147,22 +146,31 @@
     private final Object mLock;
     @GuardedBy("mLock")
     final SparseArray<UserState> mUserStates = new SparseArray<>();
+    // SystemTextClassifier.onDestroy() is not guaranteed to be called, use LruCache here
+    // to avoid leak.
     @GuardedBy("mLock")
-    private final Map<TextClassificationSessionId, Integer> mSessionUserIds = new ArrayMap<>();
-    @GuardedBy("mLock")
-    private TextClassificationConstants mSettings;
+    private final LruCache<TextClassificationSessionId, TextClassificationContext>
+            mSessionContextCache = new LruCache<>(40);
+    private final TextClassificationConstants mSettings;
+    @Nullable
+    private final String mDefaultTextClassifierPackage;
+    @Nullable
+    private final String mSystemTextClassifierPackage;
 
     private TextClassificationManagerService(Context context) {
         mContext = Objects.requireNonNull(context);
         mLock = new Object();
+        mSettings = new TextClassificationConstants();
         mSettingsListener = new TextClassifierSettingsListener(mContext);
+        PackageManager packageManager = mContext.getPackageManager();
+        mDefaultTextClassifierPackage = packageManager.getDefaultTextClassifierPackageName();
+        mSystemTextClassifierPackage = packageManager.getSystemTextClassifierPackageName();
     }
 
     private void startListenSettings() {
         mSettingsListener.registerObserver();
     }
 
-
     @Override
     public void onConnectedStateChanged(@ConnectionState int connected) {
     }
@@ -178,6 +186,7 @@
                 request.getUserId(),
                 request.getCallingPackageName(),
                 /* attemptToBind= */ true,
+                request.getUseDefaultTextClassifier(),
                 service -> service.onSuggestSelection(sessionId, request, callback),
                 "onSuggestSelection",
                 callback);
@@ -194,6 +203,7 @@
                 request.getUserId(),
                 request.getCallingPackageName(),
                 /* attemptToBind= */ true,
+                request.getUseDefaultTextClassifier(),
                 service -> service.onClassifyText(sessionId, request, callback),
                 "onClassifyText",
                 callback);
@@ -210,6 +220,7 @@
                 request.getUserId(),
                 request.getCallingPackageName(),
                 /* attemptToBind= */ true,
+                request.getUseDefaultTextClassifier(),
                 service -> service.onGenerateLinks(sessionId, request, callback),
                 "onGenerateLinks",
                 callback);
@@ -223,28 +234,32 @@
 
         handleRequest(
                 event.getUserId(),
-                event.getPackageName(),
+                /* callingPackageName= */ null,
                 /* attemptToBind= */ false,
+                event.getUseDefaultTextClassifier(),
                 service -> service.onSelectionEvent(sessionId, event),
                 "onSelectionEvent",
                 NO_OP_CALLBACK);
     }
+
     @Override
     public void onTextClassifierEvent(
             @Nullable TextClassificationSessionId sessionId,
             TextClassifierEvent event) throws RemoteException {
         Objects.requireNonNull(event);
 
-        final String packageName = event.getEventContext() == null
-                ? null
-                : event.getEventContext().getPackageName();
         final int userId = event.getEventContext() == null
                 ? UserHandle.getCallingUserId()
                 : event.getEventContext().getUserId();
+        final boolean useDefaultTextClassifier =
+                event.getEventContext() != null
+                        ? event.getEventContext().getUseDefaultTextClassifier()
+                        : true;
         handleRequest(
                 userId,
-                packageName,
+                /* callingPackageName= */ null,
                 /* attemptToBind= */ false,
+                useDefaultTextClassifier,
                 service -> service.onTextClassifierEvent(sessionId, event),
                 "onTextClassifierEvent",
                 NO_OP_CALLBACK);
@@ -261,6 +276,7 @@
                 request.getUserId(),
                 request.getCallingPackageName(),
                 /* attemptToBind= */ true,
+                request.getUseDefaultTextClassifier(),
                 service -> service.onDetectLanguage(sessionId, request, callback),
                 "onDetectLanguage",
                 callback);
@@ -277,6 +293,7 @@
                 request.getUserId(),
                 request.getCallingPackageName(),
                 /* attemptToBind= */ true,
+                request.getUseDefaultTextClassifier(),
                 service -> service.onSuggestConversationActions(sessionId, request, callback),
                 "onSuggestConversationActions",
                 callback);
@@ -294,9 +311,10 @@
                 userId,
                 classificationContext.getPackageName(),
                 /* attemptToBind= */ false,
+                classificationContext.getUseDefaultTextClassifier(),
                 service -> {
                     service.onCreateTextClassificationSession(classificationContext, sessionId);
-                    mSessionUserIds.put(sessionId, userId);
+                    mSessionContextCache.put(sessionId, classificationContext);
                 },
                 "onCreateTextClassificationSession",
                 NO_OP_CALLBACK);
@@ -308,16 +326,23 @@
         Objects.requireNonNull(sessionId);
 
         synchronized (mLock) {
-            final int userId = mSessionUserIds.containsKey(sessionId)
-                    ? mSessionUserIds.get(sessionId)
+            TextClassificationContext textClassificationContext =
+                    mSessionContextCache.get(sessionId);
+            final int userId = textClassificationContext != null
+                    ? textClassificationContext.getUserId()
                     : UserHandle.getCallingUserId();
+            final boolean useDefaultTextClassifier =
+                    textClassificationContext != null
+                            ? textClassificationContext.getUseDefaultTextClassifier()
+                            : true;
             handleRequest(
                     userId,
                     /* callingPackageName= */ null,
                     /* attemptToBind= */ false,
+                    useDefaultTextClassifier,
                     service -> {
                         service.onDestroyTextClassificationSession(sessionId);
-                        mSessionUserIds.remove(sessionId);
+                        mSessionContextCache.remove(sessionId);
                     },
                     "onDestroyTextClassificationSession",
                     NO_OP_CALLBACK);
@@ -328,7 +353,7 @@
     private UserState getUserStateLocked(int userId) {
         UserState result = mUserStates.get(userId);
         if (result == null) {
-            result = new UserState(userId, mContext, mLock);
+            result = new UserState(userId);
             mUserStates.put(userId, result);
         }
         return result;
@@ -339,6 +364,19 @@
         return mUserStates.get(userId);
     }
 
+    private int resolvePackageToUid(@Nullable String packageName, @UserIdInt int userId) {
+        if (packageName == null) {
+            return Process.INVALID_UID;
+        }
+        final PackageManager pm = mContext.getPackageManager();
+        try {
+            return pm.getPackageUidAsUser(packageName, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.e(LOG_TAG, "Could not get the UID for " + packageName);
+        }
+        return Process.INVALID_UID;
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
@@ -353,18 +391,25 @@
 
         pw.printPair("context", mContext);
         pw.println();
+        pw.printPair("defaultTextClassifierPackage", mDefaultTextClassifierPackage);
+        pw.println();
+        pw.printPair("systemTextClassifierPackage", mSystemTextClassifierPackage);
+        pw.println();
         synchronized (mLock) {
             int size = mUserStates.size();
-            pw.print("Number user states: "); pw.println(size);
+            pw.print("Number user states: ");
+            pw.println(size);
             if (size > 0) {
                 for (int i = 0; i < size; i++) {
                     pw.increaseIndent();
                     UserState userState = mUserStates.valueAt(i);
-                    pw.print(i); pw.print(":"); userState.dump(pw); pw.println();
+                    pw.printPair("User", mUserStates.keyAt(i));
+                    pw.println();
+                    userState.dump(pw);
                     pw.decreaseIndent();
                 }
             }
-            pw.println("Number of active sessions: " + mSessionUserIds.size());
+            pw.println("Number of active sessions: " + mSessionContextCache.size());
         }
     }
 
@@ -372,57 +417,55 @@
             @UserIdInt int userId,
             @Nullable String callingPackageName,
             boolean attemptToBind,
+            boolean useDefaultTextClassifier,
             @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
             @NonNull String methodName,
-            @NonNull ITextClassifierCallback callback)
-            throws RemoteException {
+            @NonNull ITextClassifierCallback callback) throws RemoteException {
         Objects.requireNonNull(textClassifierServiceConsumer);
         Objects.requireNonNull(methodName);
         Objects.requireNonNull(callback);
 
-        validateInput(mContext, callingPackageName, userId);
+        try {
+            validateCallingPackage(callingPackageName);
+            validateUser(userId);
+        } catch (Exception e) {
+            throw new RemoteException("Invalid request: " + e.getMessage(), e,
+                    /* enableSuppression */ true, /* writableStackTrace */ true);
+        }
         synchronized (mLock) {
             UserState userState = getUserStateLocked(userId);
-            if (attemptToBind && !userState.bindLocked()) {
+            ServiceState serviceState =
+                    userState.getServiceStateLocked(useDefaultTextClassifier);
+            if (serviceState == null) {
+                Slog.d(LOG_TAG, "No configured system TextClassifierService");
+                callback.onFailure();
+            } else if (attemptToBind && !serviceState.bindLocked()) {
                 Slog.d(LOG_TAG, "Unable to bind TextClassifierService at " + methodName);
                 callback.onFailure();
-            } else if (userState.isBoundLocked()) {
-                if (!userState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) {
+            } else if (serviceState.isBoundLocked()) {
+                if (!serviceState.checkRequestAcceptedLocked(Binder.getCallingUid(), methodName)) {
                     return;
                 }
-                textClassifierServiceConsumer.accept(userState.mService);
+                textClassifierServiceConsumer.accept(serviceState.mService);
             } else {
-                userState.mPendingRequests.add(
+                serviceState.mPendingRequests.add(
                         new PendingRequest(
                                 methodName,
-                                () -> textClassifierServiceConsumer.accept(userState.mService),
+                                () -> textClassifierServiceConsumer.accept(serviceState.mService),
                                 callback::onFailure, callback.asBinder(),
                                 this,
-                                userState,
+                                serviceState,
                                 Binder.getCallingUid()));
             }
         }
     }
 
-    private void unbindServiceIfNecessary() {
-        final ComponentName serviceComponentName =
-                TextClassifierService.getServiceComponentName(mContext);
-        if (serviceComponentName == null) {
-            // It should not occur if we had defined default service names in config.xml
-            Slog.w(LOG_TAG, "No default configured system TextClassifierService.");
-            return;
-        }
+    private void onTextClassifierServicePackageOverrideChanged(String overriddenPackage) {
         synchronized (mLock) {
             final int size = mUserStates.size();
             for (int i = 0; i < size; i++) {
                 UserState userState = mUserStates.valueAt(i);
-                // Only unbind for a new service
-                if (userState.isCurrentlyBoundToComponentLocked(serviceComponentName)) {
-                    return;
-                }
-                if (userState.isBoundLocked()) {
-                    userState.unbindLocked();
-                }
+                userState.onTextClassifierServicePackageOverrideChangedLocked(overriddenPackage);
             }
         }
     }
@@ -430,27 +473,35 @@
     private static final class PendingRequest implements IBinder.DeathRecipient {
 
         private final int mUid;
-        @Nullable private final String mName;
-        @Nullable private final IBinder mBinder;
-        @NonNull private final Runnable mRequest;
-        @Nullable private final Runnable mOnServiceFailure;
+        @Nullable
+        private final String mName;
+        @Nullable
+        private final IBinder mBinder;
+        @NonNull
+        private final Runnable mRequest;
+        @Nullable
+        private final Runnable mOnServiceFailure;
         @GuardedBy("mLock")
-        @NonNull private final UserState mOwningUser;
-        @NonNull private final TextClassificationManagerService mService;
+        @NonNull
+        private final ServiceState mServiceState;
+        @NonNull
+        private final TextClassificationManagerService mService;
 
         /**
          * Initializes a new pending request.
-         * @param request action to perform when the service is bound
+         *
+         * @param request          action to perform when the service is bound
          * @param onServiceFailure action to perform when the service dies or disconnects
-         * @param binder binder to the process that made this pending request
-         * @param service
-         * @param owningUser
+         * @param binder           binder to the process that made this pending request
+         * @parm service           the TCMS instance.
+         * @param serviceState     the service state of the service that will execute the request.
+         * @param uid              the calling uid of the request.
          */
         PendingRequest(@Nullable String name,
                 @NonNull ThrowingRunnable request, @Nullable ThrowingRunnable onServiceFailure,
                 @Nullable IBinder binder,
-                TextClassificationManagerService service,
-                UserState owningUser, int uid) {
+                @NonNull TextClassificationManagerService service,
+                @NonNull ServiceState serviceState, int uid) {
             mName = name;
             mRequest =
                     logOnFailure(Objects.requireNonNull(request), "handling pending request");
@@ -458,7 +509,7 @@
                     logOnFailure(onServiceFailure, "notifying callback of service failure");
             mBinder = binder;
             mService = service;
-            mOwningUser = owningUser;
+            mServiceState = Objects.requireNonNull(serviceState);
             if (mBinder != null) {
                 try {
                     mBinder.linkToDeath(this, 0);
@@ -479,7 +530,7 @@
 
         @GuardedBy("mLock")
         private void removeLocked() {
-            mOwningUser.mPendingRequests.remove(this);
+            mServiceState.mPendingRequests.remove(this);
             if (mBinder != null) {
                 mBinder.unlinkToDeath(this, 0);
             }
@@ -492,59 +543,172 @@
                 e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
     }
 
-    private static void validateInput(
-            Context context, @Nullable String packageName, @UserIdInt int userId)
-            throws RemoteException {
+    private void validateCallingPackage(@Nullable String callingPackage)
+            throws PackageManager.NameNotFoundException {
+        if (callingPackage != null) {
+            final int packageUid = mContext.getPackageManager()
+                    .getPackageUidAsUser(callingPackage, UserHandle.getCallingUserId());
+            final int callingUid = Binder.getCallingUid();
+            Preconditions.checkArgument(
+                    callingUid == packageUid
+                            // Trust the system process:
+                            || callingUid == android.os.Process.SYSTEM_UID,
+                    "Invalid package name. callingPackage=" + callingPackage
+                            + ", callingUid=" + callingUid);
+        }
+    }
 
-        try {
-            if (packageName != null) {
-                final int packageUid = context.getPackageManager()
-                        .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
-                final int callingUid = Binder.getCallingUid();
-                Preconditions.checkArgument(callingUid == packageUid
-                        // Trust the system process:
-                        || callingUid == android.os.Process.SYSTEM_UID,
-                        "Invalid package name. Package=" + packageName
-                                + ", CallingUid=" + callingUid);
-            }
-
-            Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId");
-            final int callingUserId = UserHandle.getCallingUserId();
-            if (callingUserId != userId) {
-                context.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                        "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId);
-            }
-        } catch (Exception e) {
-            throw new RemoteException("Invalid request: " + e.getMessage(), e,
-                    /* enableSuppression */ true, /* writableStackTrace */ true);
+    private void validateUser(@UserIdInt int userId) {
+        Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId");
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (callingUserId != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId);
         }
     }
 
     private final class UserState {
-        @UserIdInt final int mUserId;
+        @UserIdInt
+        final int mUserId;
+        @Nullable
+        private final ServiceState mDefaultServiceState;
+        @Nullable
+        private final ServiceState mSystemServiceState;
         @GuardedBy("mLock")
-        TextClassifierServiceConnection mConnection = null;
+        @Nullable
+        private ServiceState mUntrustedServiceState;
+
+        private UserState(int userId) {
+            mUserId = userId;
+            mDefaultServiceState = TextUtils.isEmpty(mDefaultTextClassifierPackage)
+                    ? null
+                    : new ServiceState(userId, mDefaultTextClassifierPackage, /* isTrusted= */true);
+            mSystemServiceState = TextUtils.isEmpty(mSystemTextClassifierPackage)
+                    ? null
+                    : new ServiceState(userId, mSystemTextClassifierPackage, /* isTrusted= */ true);
+        }
+
+        @GuardedBy("mLock")
+        @Nullable
+        ServiceState getServiceStateLocked(boolean useDefaultTextClassifier) {
+            if (useDefaultTextClassifier) {
+                return mDefaultServiceState;
+            }
+            String textClassifierServicePackageOverride =
+                    mSettings.getTextClassifierServicePackageOverride();
+            if (!TextUtils.isEmpty(textClassifierServicePackageOverride)) {
+                if (textClassifierServicePackageOverride.equals(mDefaultTextClassifierPackage)) {
+                    return mDefaultServiceState;
+                }
+                if (textClassifierServicePackageOverride.equals(mSystemTextClassifierPackage)
+                        && mSystemServiceState != null) {
+                    return mSystemServiceState;
+                }
+                if (mUntrustedServiceState == null) {
+                    mUntrustedServiceState =
+                            new ServiceState(
+                                    mUserId,
+                                    textClassifierServicePackageOverride,
+                                    /* isTrusted= */false);
+                }
+                return mUntrustedServiceState;
+            }
+            return mSystemServiceState != null ? mSystemServiceState : mDefaultServiceState;
+        }
+
+        @GuardedBy("mLock")
+        void onTextClassifierServicePackageOverrideChangedLocked(String overriddenPackageName) {
+            // The override config is just used for testing, and the flag value is not expected
+            // to change often. So, let's keep it simple and just unbind all the services here. The
+            // right service will be bound when the next request comes.
+            for (ServiceState serviceState : getAllServiceStatesLocked()) {
+                serviceState.unbindIfBoundLocked();
+            }
+            mUntrustedServiceState = null;
+        }
+
+        @GuardedBy("mLock")
+        void bindIfHasPendingRequestsLocked() {
+            for (ServiceState serviceState : getAllServiceStatesLocked()) {
+                serviceState.bindIfHasPendingRequestsLocked();
+            }
+        }
+
+        @GuardedBy("mLock")
+        void cleanupServiceLocked() {
+            for (ServiceState serviceState : getAllServiceStatesLocked()) {
+                if (serviceState.mConnection != null) {
+                    serviceState.mConnection.cleanupService();
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        @NonNull
+        private List<ServiceState> getAllServiceStatesLocked() {
+            List<ServiceState> serviceStates = new ArrayList<>();
+            if (mDefaultServiceState != null) {
+                serviceStates.add(mDefaultServiceState);
+            }
+            if (mSystemServiceState != null) {
+                serviceStates.add(mSystemServiceState);
+            }
+            if (mUntrustedServiceState != null) {
+                serviceStates.add(mUntrustedServiceState);
+            }
+            return serviceStates;
+        }
+
+        void dump(IndentingPrintWriter pw) {
+            synchronized (mLock) {
+                pw.increaseIndent();
+                dump(pw, mDefaultServiceState, "Default");
+                dump(pw, mSystemServiceState, "System");
+                dump(pw, mUntrustedServiceState, "Untrusted");
+                pw.decreaseIndent();
+            }
+        }
+
+        private void dump(
+                IndentingPrintWriter pw, @Nullable ServiceState serviceState, String name) {
+            synchronized (mLock) {
+                if (serviceState != null) {
+                    pw.print(name + ": ");
+                    serviceState.dump(pw);
+                    pw.println();
+                }
+            }
+        }
+    }
+
+    private final class ServiceState {
+        @UserIdInt
+        final int mUserId;
+        @NonNull
+        final String mPackageName;
+        @NonNull
+        final TextClassifierServiceConnection mConnection;
+        final boolean mIsTrusted;
+        @NonNull
         @GuardedBy("mLock")
         final Queue<PendingRequest> mPendingRequests = new ArrayDeque<>();
+        @Nullable
         @GuardedBy("mLock")
         ITextClassifierService mService;
         @GuardedBy("mLock")
         boolean mBinding;
+        @Nullable
         @GuardedBy("mLock")
         ComponentName mBoundComponentName = null;
         @GuardedBy("mLock")
-        boolean mBoundToDefaultTrustService;
-        @GuardedBy("mLock")
-        int mBoundServiceUid;
+        int mBoundServiceUid = Process.INVALID_UID;
 
-        private final Context mContext;
-        private final Object mLock;
-
-        private UserState(int userId, Context context, Object lock) {
+        private ServiceState(@UserIdInt int userId, String packageName, boolean isTrusted) {
             mUserId = userId;
-            mContext = Objects.requireNonNull(context);
-            mLock = Objects.requireNonNull(lock);
+            mPackageName = packageName;
+            mConnection = new TextClassifierServiceConnection(mUserId);
+            mIsTrusted = isTrusted;
         }
 
         @GuardedBy("mLock")
@@ -581,18 +745,12 @@
         }
 
         @GuardedBy("mLock")
-        private boolean isCurrentlyBoundToComponentLocked(@NonNull ComponentName componentName) {
-            return (mBoundComponentName != null
-                    && mBoundComponentName.getPackageName().equals(
-                    componentName.getPackageName()));
-        }
-
-        @GuardedBy("mLock")
-        private void unbindLocked() {
-            Slog.v(LOG_TAG, "unbinding from " + mBoundComponentName + " for " + mUserId);
-            mContext.unbindService(mConnection);
-            mConnection.cleanupService();
-            mConnection = null;
+        void unbindIfBoundLocked() {
+            if (isBoundLocked()) {
+                Slog.v(LOG_TAG, "Unbinding " + mBoundComponentName + " for " + mUserId);
+                mContext.unbindService(mConnection);
+                mConnection.cleanupService();
+            }
         }
 
         /**
@@ -609,8 +767,7 @@
             final boolean willBind;
             final long identity = Binder.clearCallingIdentity();
             try {
-                final ComponentName componentName =
-                        TextClassifierService.getServiceComponentName(mContext);
+                final ComponentName componentName = getTextClassifierServiceComponent();
                 if (componentName == null) {
                     // Might happen if the storage is encrypted and the user is not unlocked
                     return false;
@@ -618,7 +775,6 @@
                 Intent serviceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
                         .setComponent(componentName);
                 Slog.d(LOG_TAG, "Binding to " + serviceIntent.getComponent());
-                mConnection = new TextClassifierServiceConnection(mUserId);
                 willBind = mContext.bindServiceAsUser(
                         serviceIntent, mConnection,
                         Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
@@ -631,12 +787,21 @@
             return willBind;
         }
 
+        @Nullable
+        private ComponentName getTextClassifierServiceComponent() {
+            return TextClassifierService.getServiceComponentName(
+                    mContext,
+                    mPackageName,
+                    mIsTrusted ? PackageManager.MATCH_SYSTEM_ONLY : 0);
+        }
+
         private void dump(IndentingPrintWriter pw) {
             pw.printPair("context", mContext);
             pw.printPair("userId", mUserId);
             synchronized (mLock) {
+                pw.printPair("packageName", mPackageName);
                 pw.printPair("boundComponentName", mBoundComponentName);
-                pw.printPair("boundToDefaultTrustService", mBoundToDefaultTrustService);
+                pw.printPair("isTrusted", mIsTrusted);
                 pw.printPair("boundServiceUid", mBoundServiceUid);
                 pw.printPair("binding", mBinding);
                 pw.printPair("numberRequests", mPendingRequests.size());
@@ -645,7 +810,7 @@
 
         @GuardedBy("mLock")
         private boolean checkRequestAcceptedLocked(int requestUid, @NonNull String methodName) {
-            if (mBoundToDefaultTrustService || (requestUid == mBoundServiceUid)) {
+            if (mIsTrusted || (requestUid == mBoundServiceUid)) {
                 return true;
             }
             Slog.w(LOG_TAG, String.format(
@@ -654,47 +819,19 @@
             return false;
         }
 
-        private boolean isDefaultTrustService(@NonNull ComponentName currentService) {
-            final String[] defaultServiceNames =
-                    mContext.getPackageManager().getSystemTextClassifierPackages();
-            final String servicePackageName = currentService.getPackageName();
-
-            for (int i = 0; i < defaultServiceNames.length; i++) {
-                if (defaultServiceNames[i].equals(servicePackageName)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private int getServiceUid(@Nullable ComponentName service, int userId) {
-            if (service == null) {
-                return Process.INVALID_UID;
-            }
-            final String servicePackageName = service.getPackageName();
-            final PackageManager pm = mContext.getPackageManager();
-            final int serviceUid;
-
-            try {
-                serviceUid = pm.getPackageUidAsUser(servicePackageName, userId);
-            } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(LOG_TAG, "Could not verify UID for " + service);
-                return Process.INVALID_UID;
-            }
-            return serviceUid;
-        }
-
         @GuardedBy("mLock")
         private void updateServiceInfoLocked(int userId, @Nullable ComponentName componentName) {
             mBoundComponentName = componentName;
-            mBoundToDefaultTrustService = (mBoundComponentName != null && isDefaultTrustService(
-                    mBoundComponentName));
-            mBoundServiceUid = getServiceUid(mBoundComponentName, userId);
+            mBoundServiceUid =
+                    mBoundComponentName == null
+                            ? Process.INVALID_UID
+                            : resolvePackageToUid(mBoundComponentName.getPackageName(), userId);
         }
 
         private final class TextClassifierServiceConnection implements ServiceConnection {
 
-            @UserIdInt private final int mUserId;
+            @UserIdInt
+            private final int mUserId;
 
             TextClassifierServiceConnection(int userId) {
                 mUserId = userId;
@@ -745,18 +882,18 @@
 
     private final class TextClassifierSettingsListener implements
             DeviceConfig.OnPropertiesChangedListener {
+        @NonNull
+        private final Context mContext;
+        @Nullable
+        private String mServicePackageOverride;
 
-        @NonNull private final Context mContext;
-        @NonNull private final TextClassificationConstants mSettings;
-        @Nullable private String mServicePackageName;
 
         TextClassifierSettingsListener(Context context) {
             mContext = context;
-            mSettings = TextClassificationManager.getSettings(mContext);
-            mServicePackageName = mSettings.getTextClassifierServicePackageOverride();
+            mServicePackageOverride = mSettings.getTextClassifierServicePackageOverride();
         }
 
-        public void registerObserver() {
+        void registerObserver() {
             DeviceConfig.addOnPropertiesChangedListener(
                     DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
                     mContext.getMainExecutor(),
@@ -765,13 +902,13 @@
 
         @Override
         public void onPropertiesChanged(DeviceConfig.Properties properties) {
-            final String overrideServiceName = mSettings.getTextClassifierServicePackageOverride();
-
-            if (TextUtils.equals(overrideServiceName, mServicePackageName)) {
+            final String currentServicePackageOverride =
+                    mSettings.getTextClassifierServicePackageOverride();
+            if (TextUtils.equals(currentServicePackageOverride, mServicePackageOverride)) {
                 return;
             }
-            mServicePackageName = overrideServiceName;
-            unbindServiceIfNecessary();
+            mServicePackageOverride = currentServicePackageOverride;
+            onTextClassifierServicePackageOverrideChanged(currentServicePackageOverride);
         }
     }
 }
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index b7d6360..0bb0f94 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -37,6 +37,9 @@
 import java.io.PrintWriter;
 import java.util.Objects;
 
+/**
+ * The implementation of ITimeDetectorService.aidl.
+ */
 public final class TimeDetectorService extends ITimeDetectorService.Stub {
     private static final String TAG = "TimeDetectorService";
 
@@ -75,7 +78,7 @@
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
                 new ContentObserver(handler) {
                     public void onChange(boolean selfChange) {
-                        timeDetectorService.handleAutoTimeDetectionToggle();
+                        timeDetectorService.handleAutoTimeDetectionChanged();
                     }
                 });
 
@@ -114,8 +117,9 @@
         mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
     }
 
+    /** Internal method for handling the auto time setting being changed. */
     @VisibleForTesting
-    public void handleAutoTimeDetectionToggle() {
+    public void handleAutoTimeDetectionChanged() {
         mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
     }
 
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 468b806..a7c3b4d 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -26,8 +26,8 @@
 import java.io.PrintWriter;
 
 /**
- * The interface for classes that implement the time detection algorithm used by the
- * TimeDetectorService.
+ * The interface for the class that implements the time detection algorithm used by the
+ * {@link TimeDetectorService}.
  *
  * <p>Most calls will be handled by a single thread but that is not true for all calls. For example
  * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index a1e643f..19435ee 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -38,7 +38,7 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * An implementation of TimeDetectorStrategy that passes phone and manual suggestions to
+ * An implementation of {@link TimeDetectorStrategy} that passes phone and manual suggestions to
  * {@link AlarmManager}. When there are multiple phone sources, the one with the lowest ID is used
  * unless the data becomes too stale.
  *
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index adf6d7e..2520316 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -24,9 +24,9 @@
 import android.provider.Settings;
 
 /**
- * The real implementation of {@link TimeZoneDetectorStrategy.Callback}.
+ * The real implementation of {@link TimeZoneDetectorStrategyImpl.Callback}.
  */
-public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategy.Callback {
+public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategyImpl.Callback {
 
     private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 9a1fe65..381ee10 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -67,19 +67,21 @@
 
     private static TimeZoneDetectorService create(@NonNull Context context) {
         final TimeZoneDetectorStrategy timeZoneDetectorStrategy =
-                TimeZoneDetectorStrategy.create(context);
+                TimeZoneDetectorStrategyImpl.create(context);
 
         Handler handler = FgThread.getHandler();
+        TimeZoneDetectorService service =
+                new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
+
         ContentResolver contentResolver = context.getContentResolver();
         contentResolver.registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
                 new ContentObserver(handler) {
                     public void onChange(boolean selfChange) {
-                        timeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
+                        service.handleAutoTimeZoneDetectionChanged();
                     }
                 });
-
-        return new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
+        return service;
     }
 
     @VisibleForTesting
@@ -111,17 +113,25 @@
             @Nullable String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        mTimeZoneDetectorStrategy.dumpState(pw, args);
+        mTimeZoneDetectorStrategy.dump(pw, args);
+    }
+
+    /** Internal method for handling the auto time zone setting being changed. */
+    @VisibleForTesting
+    public void handleAutoTimeZoneDetectionChanged() {
+        mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneDetectionChanged);
     }
 
     private void enforceSuggestPhoneTimeZonePermission() {
         mContext.enforceCallingPermission(
-                android.Manifest.permission.SET_TIME_ZONE, "set time zone");
+                android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE,
+                "suggest phone time and time zone");
     }
 
     private void enforceSuggestManualTimeZonePermission() {
         mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.SET_TIME_ZONE, "set time zone");
+                android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
+                "suggest manual time and time zone");
     }
 }
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index b0e0069..1d439e9 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -15,192 +15,26 @@
  */
 package com.android.server.timezonedetector;
 
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
-import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
-
-import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion;
-import android.content.Context;
-import android.util.LocalLog;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
 
 /**
- * A singleton, stateful time zone detection strategy that is aware of user (manual) suggestions and
- * suggestions from multiple phone devices. Suggestions are acted on or ignored as needed, dependent
- * on the current "auto time zone detection" setting.
+ * The interface for the class that implement the time detection algorithm used by the
+ * {@link TimeZoneDetectorService}.
  *
- * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses
- * the best suggestion based on a scoring algorithm. If several phones provide the same score then
- * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer
- * possible to be confident about the time zone, phones must submit an empty suggestion in order to
- * "withdraw" their previous suggestion.
+ * <p>Most calls will be handled by a single thread but that is not true for all calls. For example
+ * {@link #dump(PrintWriter, String[])}) may be called on a different thread so implementations must
+ * handle thread safety.
+ *
+ * @hide
  */
-public class TimeZoneDetectorStrategy {
+public interface TimeZoneDetectorStrategy {
 
-    /**
-     * Used by {@link TimeZoneDetectorStrategy} to interact with the surrounding service. It can be
-     * faked for tests.
-     *
-     * <p>Note: Because the system properties-derived values like
-     * {@link #isAutoTimeZoneDetectionEnabled()}, {@link #isAutoTimeZoneDetectionEnabled()},
-     * {@link #getDeviceTimeZone()} can be modified independently and from different threads (and
-     * processes!), their use are prone to race conditions. That will be true until the
-     * responsibility for setting their values is moved to {@link TimeZoneDetectorStrategy}.
-     */
-    @VisibleForTesting
-    public interface Callback {
-
-        /**
-         * Returns true if automatic time zone detection is enabled in settings.
-         */
-        boolean isAutoTimeZoneDetectionEnabled();
-
-        /**
-         * Returns true if the device has had an explicit time zone set.
-         */
-        boolean isDeviceTimeZoneInitialized();
-
-        /**
-         * Returns the device's currently configured time zone.
-         */
-        String getDeviceTimeZone();
-
-        /**
-         * Sets the device's time zone.
-         */
-        void setDeviceTimeZone(@NonNull String zoneId);
-    }
-
-    private static final String LOG_TAG = "TimeZoneDetectorStrategy";
-    private static final boolean DBG = false;
-
-    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Origin {}
-
-    /** Used when a time value originated from a telephony signal. */
-    @Origin
-    private static final int ORIGIN_PHONE = 1;
-
-    /** Used when a time value originated from a user / manual settings. */
-    @Origin
-    private static final int ORIGIN_MANUAL = 2;
-
-    /**
-     * The abstract score for an empty or invalid phone suggestion.
-     *
-     * Used to score phone suggestions where there is no zone.
-     */
-    @VisibleForTesting
-    public static final int PHONE_SCORE_NONE = 0;
-
-    /**
-     * The abstract score for a low quality phone suggestion.
-     *
-     * Used to score suggestions where:
-     * The suggested zone ID is one of several possibilities, and the possibilities have different
-     * offsets.
-     *
-     * You would have to be quite desperate to want to use this choice.
-     */
-    @VisibleForTesting
-    public static final int PHONE_SCORE_LOW = 1;
-
-    /**
-     * The abstract score for a medium quality phone suggestion.
-     *
-     * Used for:
-     * The suggested zone ID is one of several possibilities but at least the possibilities have the
-     * same offset. Users would get the correct time but for the wrong reason. i.e. their device may
-     * switch to DST at the wrong time and (for example) their calendar events.
-     */
-    @VisibleForTesting
-    public static final int PHONE_SCORE_MEDIUM = 2;
-
-    /**
-     * The abstract score for a high quality phone suggestion.
-     *
-     * Used for:
-     * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
-     * the info available.
-     */
-    @VisibleForTesting
-    public static final int PHONE_SCORE_HIGH = 3;
-
-    /**
-     * The abstract score for a highest quality phone suggestion.
-     *
-     * Used for:
-     * Suggestions that must "win" because they constitute test or emulator zone ID.
-     */
-    @VisibleForTesting
-    public static final int PHONE_SCORE_HIGHEST = 4;
-
-    /**
-     * The threshold at which phone suggestions are good enough to use to set the device's time
-     * zone.
-     */
-    @VisibleForTesting
-    public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM;
-
-    /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
-    private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30;
-
-    @NonNull
-    private final Callback mCallback;
-
-    /**
-     * A log that records the decisions / decision metadata that affected the device's time zone
-     * (for use during debugging).
-     */
-    @NonNull
-    private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
-
-    /**
-     * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two
-     * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to
-     * be stable.
-     */
-    @GuardedBy("this")
-    private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex =
-            new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
-
-    /**
-     * Creates a new instance of {@link TimeZoneDetectorStrategy}.
-     */
-    public static TimeZoneDetectorStrategy create(Context context) {
-        Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context);
-        return new TimeZoneDetectorStrategy(timeZoneDetectionServiceHelper);
-    }
-
-    @VisibleForTesting
-    public TimeZoneDetectorStrategy(Callback callback) {
-        mCallback = Objects.requireNonNull(callback);
-    }
-
-    /** Process the suggested manually- / user-entered time zone. */
-    public synchronized void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
-        Objects.requireNonNull(suggestion);
-
-        String timeZoneId = suggestion.getZoneId();
-        String cause = "Manual time suggestion received: suggestion=" + suggestion;
-        setDeviceTimeZoneIfRequired(ORIGIN_MANUAL, timeZoneId, cause);
-    }
+    /** Process the suggested manually-entered (i.e. user sourced) time zone. */
+    void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion);
 
     /**
      * Suggests a time zone for the device, or withdraws a previous suggestion if
@@ -210,312 +44,15 @@
      * suggestion. The strategy uses suggestions to decide whether to modify the device's time zone
      * setting and what to set it to.
      */
-    public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
-        if (DBG) {
-            Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion);
-        }
-        Objects.requireNonNull(suggestion);
-
-        // Score the suggestion.
-        int score = scorePhoneSuggestion(suggestion);
-        QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
-                new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
-
-        // Store the suggestion against the correct slotIndex.
-        mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
-
-        // Now perform auto time zone detection. The new suggestion may be used to modify the time
-        // zone setting.
-        String reason = "New phone time suggested. suggestion=" + suggestion;
-        doAutoTimeZoneDetection(reason);
-    }
-
-    private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
-        int score;
-        if (suggestion.getZoneId() == null) {
-            score = PHONE_SCORE_NONE;
-        } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
-                || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
-            // Handle emulator / test cases : These suggestions should always just be used.
-            score = PHONE_SCORE_HIGHEST;
-        } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
-            score = PHONE_SCORE_HIGH;
-        } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
-            // The suggestion may be wrong, but at least the offset should be correct.
-            score = PHONE_SCORE_MEDIUM;
-        } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
-            // The suggestion has a good chance of being wrong.
-            score = PHONE_SCORE_LOW;
-        } else {
-            throw new AssertionError();
-        }
-        return score;
-    }
-
-    /**
-     * Finds the best available time zone suggestion from all phones. If it is high-enough quality
-     * and automatic time zone detection is enabled then it will be set on the device. The outcome
-     * can be that this strategy becomes / remains un-opinionated and nothing is set.
-     */
-    @GuardedBy("this")
-    private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
-        if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
-            // Avoid doing unnecessary work with this (race-prone) check.
-            return;
-        }
-
-        QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
-
-        // Work out what to do with the best suggestion.
-        if (bestPhoneSuggestion == null) {
-            // There is no phone suggestion available at all. Become un-opinionated.
-            if (DBG) {
-                Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion."
-                        + " detectionReason=" + detectionReason);
-            }
-            return;
-        }
-
-        // Special case handling for uninitialized devices. This should only happen once.
-        String newZoneId = bestPhoneSuggestion.suggestion.getZoneId();
-        if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
-            String cause = "Device has no time zone set. Attempting to set the device to the best"
-                    + " available suggestion."
-                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
-                    + ", detectionReason=" + detectionReason;
-            Slog.i(LOG_TAG, cause);
-            setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause);
-            return;
-        }
-
-        boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD;
-        if (!suggestionGoodEnough) {
-            if (DBG) {
-                Slog.d(LOG_TAG, "Best suggestion not good enough."
-                        + " bestPhoneSuggestion=" + bestPhoneSuggestion
-                        + ", detectionReason=" + detectionReason);
-            }
-            return;
-        }
-
-        // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
-        // zone ID.
-        if (newZoneId == null) {
-            Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
-                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
-                    + " detectionReason=" + detectionReason);
-            return;
-        }
-
-        String zoneId = bestPhoneSuggestion.suggestion.getZoneId();
-        String cause = "Found good suggestion."
-                + ", bestPhoneSuggestion=" + bestPhoneSuggestion
-                + ", detectionReason=" + detectionReason;
-        setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause);
-    }
-
-    @GuardedBy("this")
-    private void setDeviceTimeZoneIfRequired(
-            @Origin int origin, @NonNull String newZoneId, @NonNull String cause) {
-        Objects.requireNonNull(newZoneId);
-        Objects.requireNonNull(cause);
-
-        boolean isOriginAutomatic = isOriginAutomatic(origin);
-        if (isOriginAutomatic) {
-            if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
-                if (DBG) {
-                    Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
-                            + " origin=" + origin
-                            + ", newZoneId=" + newZoneId
-                            + ", cause=" + cause);
-                }
-                return;
-            }
-        } else {
-            if (mCallback.isAutoTimeZoneDetectionEnabled()) {
-                if (DBG) {
-                    Slog.d(LOG_TAG, "Auto time zone detection is enabled."
-                            + " origin=" + origin
-                            + ", newZoneId=" + newZoneId
-                            + ", cause=" + cause);
-                }
-                return;
-            }
-        }
-
-        String currentZoneId = mCallback.getDeviceTimeZone();
-
-        // Avoid unnecessary changes / intents.
-        if (newZoneId.equals(currentZoneId)) {
-            // No need to set the device time zone - the setting is already what we would be
-            // suggesting.
-            if (DBG) {
-                Slog.d(LOG_TAG, "No need to change the time zone;"
-                        + " device is already set to the suggested zone."
-                        + " origin=" + origin
-                        + ", newZoneId=" + newZoneId
-                        + ", cause=" + cause);
-            }
-            return;
-        }
-
-        mCallback.setDeviceTimeZone(newZoneId);
-        String msg = "Set device time zone."
-                + " origin=" + origin
-                + ", currentZoneId=" + currentZoneId
-                + ", newZoneId=" + newZoneId
-                + ", cause=" + cause;
-        if (DBG) {
-            Slog.d(LOG_TAG, msg);
-        }
-        mTimeZoneChangesLog.log(msg);
-    }
-
-    private static boolean isOriginAutomatic(@Origin int origin) {
-        return origin != ORIGIN_MANUAL;
-    }
-
-    @GuardedBy("this")
-    @Nullable
-    private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() {
-        QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
-
-        // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
-        // and find the best. Note that we deliberately do not look at age: the caller can
-        // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
-        // expected to withdraw suggestions they no longer have confidence in.
-        for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
-            QualifiedPhoneTimeZoneSuggestion candidateSuggestion =
-                    mSuggestionBySlotIndex.valueAt(i);
-            if (candidateSuggestion == null) {
-                // Unexpected
-                continue;
-            }
-
-            if (bestSuggestion == null) {
-                bestSuggestion = candidateSuggestion;
-            } else if (candidateSuggestion.score > bestSuggestion.score) {
-                bestSuggestion = candidateSuggestion;
-            } else if (candidateSuggestion.score == bestSuggestion.score) {
-                // Tie! Use the suggestion with the lowest slotIndex.
-                int candidateSlotIndex = candidateSuggestion.suggestion.getSlotIndex();
-                int bestSlotIndex = bestSuggestion.suggestion.getSlotIndex();
-                if (candidateSlotIndex < bestSlotIndex) {
-                    bestSuggestion = candidateSuggestion;
-                }
-            }
-        }
-        return bestSuggestion;
-    }
-
-    /**
-     * Returns the current best phone suggestion. Not intended for general use: it is used during
-     * tests to check strategy behavior.
-     */
-    @VisibleForTesting
-    @Nullable
-    public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() {
-        return findBestPhoneSuggestion();
-    }
+    void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion);
 
     /**
      * Called when there has been a change to the automatic time zone detection setting.
      */
-    @VisibleForTesting
-    public synchronized void handleAutoTimeZoneDetectionChange() {
-        if (DBG) {
-            Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
-        }
-        if (mCallback.isAutoTimeZoneDetectionEnabled()) {
-            // When the user enabled time zone detection, run the time zone detection and change the
-            // device time zone if possible.
-            String reason = "Auto time zone detection setting enabled.";
-            doAutoTimeZoneDetection(reason);
-        }
-    }
+    void handleAutoTimeZoneDetectionChanged();
 
     /**
      * Dumps internal state such as field values.
      */
-    public synchronized void dumpState(PrintWriter pw, String[] args) {
-        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
-        ipw.println("TimeZoneDetectorStrategy:");
-
-        ipw.increaseIndent(); // level 1
-        ipw.println("mCallback.isTimeZoneDetectionEnabled()="
-                + mCallback.isAutoTimeZoneDetectionEnabled());
-        ipw.println("mCallback.isDeviceTimeZoneInitialized()="
-                + mCallback.isDeviceTimeZoneInitialized());
-        ipw.println("mCallback.getDeviceTimeZone()="
-                + mCallback.getDeviceTimeZone());
-
-        ipw.println("Time zone change log:");
-        ipw.increaseIndent(); // level 2
-        mTimeZoneChangesLog.dump(ipw);
-        ipw.decreaseIndent(); // level 2
-
-        ipw.println("Phone suggestion history:");
-        ipw.increaseIndent(); // level 2
-        mSuggestionBySlotIndex.dump(ipw);
-        ipw.decreaseIndent(); // level 2
-        ipw.decreaseIndent(); // level 1
-        ipw.flush();
-    }
-
-    /**
-     * A method used to inspect strategy state during tests. Not intended for general use.
-     */
-    @VisibleForTesting
-    public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) {
-        return mSuggestionBySlotIndex.get(slotIndex);
-    }
-
-    /**
-     * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata.
-     */
-    @VisibleForTesting
-    public static class QualifiedPhoneTimeZoneSuggestion {
-
-        @VisibleForTesting
-        public final PhoneTimeZoneSuggestion suggestion;
-
-        /**
-         * The score the suggestion has been given. This can be used to rank against other
-         * suggestions of the same type.
-         */
-        @VisibleForTesting
-        public final int score;
-
-        @VisibleForTesting
-        public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) {
-            this.suggestion = suggestion;
-            this.score = score;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o;
-            return score == that.score
-                    && suggestion.equals(that.suggestion);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(score, suggestion);
-        }
-
-        @Override
-        public String toString() {
-            return "QualifiedPhoneTimeZoneSuggestion{"
-                    + "suggestion=" + suggestion
-                    + ", score=" + score
-                    + '}';
-        }
-    }
+    void dump(PrintWriter pw, String[] args);
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
new file mode 100644
index 0000000..f85f9fe
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.content.Context;
+import android.util.LocalLog;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * An implementation of {@link TimeZoneDetectorStrategy} that handle telephony and manual
+ * suggestions. Suggestions are acted on or ignored as needed, dependent on the current "auto time
+ * zone detection" setting.
+ *
+ * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses
+ * the best suggestion based on a scoring algorithm. If several phones provide the same score then
+ * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer
+ * possible to be confident about the time zone, phones must submit an empty suggestion in order to
+ * "withdraw" their previous suggestion.
+ *
+ * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
+ */
+public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrategy {
+
+    /**
+     * Used by {@link TimeZoneDetectorStrategyImpl} to interact with the surrounding service. It can
+     * be faked for tests.
+     *
+     * <p>Note: Because the system properties-derived values like
+     * {@link #isAutoTimeZoneDetectionEnabled()}, {@link #isAutoTimeZoneDetectionEnabled()},
+     * {@link #getDeviceTimeZone()} can be modified independently and from different threads (and
+     * processes!), their use are prone to race conditions. That will be true until the
+     * responsibility for setting their values is moved to {@link TimeZoneDetectorStrategyImpl}.
+     */
+    @VisibleForTesting
+    public interface Callback {
+
+        /**
+         * Returns true if automatic time zone detection is enabled in settings.
+         */
+        boolean isAutoTimeZoneDetectionEnabled();
+
+        /**
+         * Returns true if the device has had an explicit time zone set.
+         */
+        boolean isDeviceTimeZoneInitialized();
+
+        /**
+         * Returns the device's currently configured time zone.
+         */
+        String getDeviceTimeZone();
+
+        /**
+         * Sets the device's time zone.
+         */
+        void setDeviceTimeZone(@NonNull String zoneId);
+    }
+
+    private static final String LOG_TAG = "TimeZoneDetectorStrategy";
+    private static final boolean DBG = false;
+
+    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Origin {}
+
+    /** Used when a time value originated from a telephony signal. */
+    @Origin
+    private static final int ORIGIN_PHONE = 1;
+
+    /** Used when a time value originated from a user / manual settings. */
+    @Origin
+    private static final int ORIGIN_MANUAL = 2;
+
+    /**
+     * The abstract score for an empty or invalid phone suggestion.
+     *
+     * Used to score phone suggestions where there is no zone.
+     */
+    @VisibleForTesting
+    public static final int PHONE_SCORE_NONE = 0;
+
+    /**
+     * The abstract score for a low quality phone suggestion.
+     *
+     * Used to score suggestions where:
+     * The suggested zone ID is one of several possibilities, and the possibilities have different
+     * offsets.
+     *
+     * You would have to be quite desperate to want to use this choice.
+     */
+    @VisibleForTesting
+    public static final int PHONE_SCORE_LOW = 1;
+
+    /**
+     * The abstract score for a medium quality phone suggestion.
+     *
+     * Used for:
+     * The suggested zone ID is one of several possibilities but at least the possibilities have the
+     * same offset. Users would get the correct time but for the wrong reason. i.e. their device may
+     * switch to DST at the wrong time and (for example) their calendar events.
+     */
+    @VisibleForTesting
+    public static final int PHONE_SCORE_MEDIUM = 2;
+
+    /**
+     * The abstract score for a high quality phone suggestion.
+     *
+     * Used for:
+     * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
+     * the info available.
+     */
+    @VisibleForTesting
+    public static final int PHONE_SCORE_HIGH = 3;
+
+    /**
+     * The abstract score for a highest quality phone suggestion.
+     *
+     * Used for:
+     * Suggestions that must "win" because they constitute test or emulator zone ID.
+     */
+    @VisibleForTesting
+    public static final int PHONE_SCORE_HIGHEST = 4;
+
+    /**
+     * The threshold at which phone suggestions are good enough to use to set the device's time
+     * zone.
+     */
+    @VisibleForTesting
+    public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM;
+
+    /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
+    private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30;
+
+    @NonNull
+    private final Callback mCallback;
+
+    /**
+     * A log that records the decisions / decision metadata that affected the device's time zone
+     * (for use during debugging).
+     */
+    @NonNull
+    private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
+
+    /**
+     * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two
+     * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to
+     * be stable.
+     */
+    @GuardedBy("this")
+    private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex =
+            new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE);
+
+    /**
+     * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
+     */
+    public static TimeZoneDetectorStrategyImpl create(Context context) {
+        Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context);
+        return new TimeZoneDetectorStrategyImpl(timeZoneDetectionServiceHelper);
+    }
+
+    @VisibleForTesting
+    public TimeZoneDetectorStrategyImpl(Callback callback) {
+        mCallback = Objects.requireNonNull(callback);
+    }
+
+    @Override
+    public synchronized void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion suggestion) {
+        Objects.requireNonNull(suggestion);
+
+        String timeZoneId = suggestion.getZoneId();
+        String cause = "Manual time suggestion received: suggestion=" + suggestion;
+        setDeviceTimeZoneIfRequired(ORIGIN_MANUAL, timeZoneId, cause);
+    }
+
+    @Override
+    public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
+        if (DBG) {
+            Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion);
+        }
+        Objects.requireNonNull(suggestion);
+
+        // Score the suggestion.
+        int score = scorePhoneSuggestion(suggestion);
+        QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
+                new QualifiedPhoneTimeZoneSuggestion(suggestion, score);
+
+        // Store the suggestion against the correct slotIndex.
+        mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion);
+
+        // Now perform auto time zone detection. The new suggestion may be used to modify the time
+        // zone setting.
+        String reason = "New phone time suggested. suggestion=" + suggestion;
+        doAutoTimeZoneDetection(reason);
+    }
+
+    private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
+        int score;
+        if (suggestion.getZoneId() == null) {
+            score = PHONE_SCORE_NONE;
+        } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
+                || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
+            // Handle emulator / test cases : These suggestions should always just be used.
+            score = PHONE_SCORE_HIGHEST;
+        } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
+            score = PHONE_SCORE_HIGH;
+        } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
+            // The suggestion may be wrong, but at least the offset should be correct.
+            score = PHONE_SCORE_MEDIUM;
+        } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
+            // The suggestion has a good chance of being wrong.
+            score = PHONE_SCORE_LOW;
+        } else {
+            throw new AssertionError();
+        }
+        return score;
+    }
+
+    /**
+     * Finds the best available time zone suggestion from all phones. If it is high-enough quality
+     * and automatic time zone detection is enabled then it will be set on the device. The outcome
+     * can be that this strategy becomes / remains un-opinionated and nothing is set.
+     */
+    @GuardedBy("this")
+    private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
+        if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+            // Avoid doing unnecessary work with this (race-prone) check.
+            return;
+        }
+
+        QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
+
+        // Work out what to do with the best suggestion.
+        if (bestPhoneSuggestion == null) {
+            // There is no phone suggestion available at all. Become un-opinionated.
+            if (DBG) {
+                Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion."
+                        + " detectionReason=" + detectionReason);
+            }
+            return;
+        }
+
+        // Special case handling for uninitialized devices. This should only happen once.
+        String newZoneId = bestPhoneSuggestion.suggestion.getZoneId();
+        if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
+            String cause = "Device has no time zone set. Attempting to set the device to the best"
+                    + " available suggestion."
+                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                    + ", detectionReason=" + detectionReason;
+            Slog.i(LOG_TAG, cause);
+            setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause);
+            return;
+        }
+
+        boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD;
+        if (!suggestionGoodEnough) {
+            if (DBG) {
+                Slog.d(LOG_TAG, "Best suggestion not good enough."
+                        + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                        + ", detectionReason=" + detectionReason);
+            }
+            return;
+        }
+
+        // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
+        // zone ID.
+        if (newZoneId == null) {
+            Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
+                    + " bestPhoneSuggestion=" + bestPhoneSuggestion
+                    + " detectionReason=" + detectionReason);
+            return;
+        }
+
+        String zoneId = bestPhoneSuggestion.suggestion.getZoneId();
+        String cause = "Found good suggestion."
+                + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+                + ", detectionReason=" + detectionReason;
+        setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause);
+    }
+
+    @GuardedBy("this")
+    private void setDeviceTimeZoneIfRequired(
+            @Origin int origin, @NonNull String newZoneId, @NonNull String cause) {
+        Objects.requireNonNull(newZoneId);
+        Objects.requireNonNull(cause);
+
+        boolean isOriginAutomatic = isOriginAutomatic(origin);
+        if (isOriginAutomatic) {
+            if (!mCallback.isAutoTimeZoneDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
+                            + " origin=" + origin
+                            + ", newZoneId=" + newZoneId
+                            + ", cause=" + cause);
+                }
+                return;
+            }
+        } else {
+            if (mCallback.isAutoTimeZoneDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time zone detection is enabled."
+                            + " origin=" + origin
+                            + ", newZoneId=" + newZoneId
+                            + ", cause=" + cause);
+                }
+                return;
+            }
+        }
+
+        String currentZoneId = mCallback.getDeviceTimeZone();
+
+        // Avoid unnecessary changes / intents.
+        if (newZoneId.equals(currentZoneId)) {
+            // No need to set the device time zone - the setting is already what we would be
+            // suggesting.
+            if (DBG) {
+                Slog.d(LOG_TAG, "No need to change the time zone;"
+                        + " device is already set to the suggested zone."
+                        + " origin=" + origin
+                        + ", newZoneId=" + newZoneId
+                        + ", cause=" + cause);
+            }
+            return;
+        }
+
+        mCallback.setDeviceTimeZone(newZoneId);
+        String msg = "Set device time zone."
+                + " origin=" + origin
+                + ", currentZoneId=" + currentZoneId
+                + ", newZoneId=" + newZoneId
+                + ", cause=" + cause;
+        if (DBG) {
+            Slog.d(LOG_TAG, msg);
+        }
+        mTimeZoneChangesLog.log(msg);
+    }
+
+    private static boolean isOriginAutomatic(@Origin int origin) {
+        return origin != ORIGIN_MANUAL;
+    }
+
+    @GuardedBy("this")
+    @Nullable
+    private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() {
+        QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
+
+        // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
+        // and find the best. Note that we deliberately do not look at age: the caller can
+        // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
+        // expected to withdraw suggestions they no longer have confidence in.
+        for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) {
+            QualifiedPhoneTimeZoneSuggestion candidateSuggestion =
+                    mSuggestionBySlotIndex.valueAt(i);
+            if (candidateSuggestion == null) {
+                // Unexpected
+                continue;
+            }
+
+            if (bestSuggestion == null) {
+                bestSuggestion = candidateSuggestion;
+            } else if (candidateSuggestion.score > bestSuggestion.score) {
+                bestSuggestion = candidateSuggestion;
+            } else if (candidateSuggestion.score == bestSuggestion.score) {
+                // Tie! Use the suggestion with the lowest slotIndex.
+                int candidateSlotIndex = candidateSuggestion.suggestion.getSlotIndex();
+                int bestSlotIndex = bestSuggestion.suggestion.getSlotIndex();
+                if (candidateSlotIndex < bestSlotIndex) {
+                    bestSuggestion = candidateSuggestion;
+                }
+            }
+        }
+        return bestSuggestion;
+    }
+
+    /**
+     * Returns the current best phone suggestion. Not intended for general use: it is used during
+     * tests to check strategy behavior.
+     */
+    @VisibleForTesting
+    @Nullable
+    public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() {
+        return findBestPhoneSuggestion();
+    }
+
+    @Override
+    public synchronized void handleAutoTimeZoneDetectionChanged() {
+        if (DBG) {
+            Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
+        }
+        if (mCallback.isAutoTimeZoneDetectionEnabled()) {
+            // When the user enabled time zone detection, run the time zone detection and change the
+            // device time zone if possible.
+            String reason = "Auto time zone detection setting enabled.";
+            doAutoTimeZoneDetection(reason);
+        }
+    }
+
+    /**
+     * Dumps internal state such as field values.
+     */
+    @Override
+    public synchronized void dump(PrintWriter pw, String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+        ipw.println("TimeZoneDetectorStrategy:");
+
+        ipw.increaseIndent(); // level 1
+        ipw.println("mCallback.isTimeZoneDetectionEnabled()="
+                + mCallback.isAutoTimeZoneDetectionEnabled());
+        ipw.println("mCallback.isDeviceTimeZoneInitialized()="
+                + mCallback.isDeviceTimeZoneInitialized());
+        ipw.println("mCallback.getDeviceTimeZone()="
+                + mCallback.getDeviceTimeZone());
+
+        ipw.println("Time zone change log:");
+        ipw.increaseIndent(); // level 2
+        mTimeZoneChangesLog.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+
+        ipw.println("Phone suggestion history:");
+        ipw.increaseIndent(); // level 2
+        mSuggestionBySlotIndex.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+        ipw.decreaseIndent(); // level 1
+        ipw.flush();
+    }
+
+    /**
+     * A method used to inspect strategy state during tests. Not intended for general use.
+     */
+    @VisibleForTesting
+    public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) {
+        return mSuggestionBySlotIndex.get(slotIndex);
+    }
+
+    /**
+     * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata.
+     */
+    @VisibleForTesting
+    public static class QualifiedPhoneTimeZoneSuggestion {
+
+        @VisibleForTesting
+        public final PhoneTimeZoneSuggestion suggestion;
+
+        /**
+         * The score the suggestion has been given. This can be used to rank against other
+         * suggestions of the same type.
+         */
+        @VisibleForTesting
+        public final int score;
+
+        @VisibleForTesting
+        public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) {
+            this.suggestion = suggestion;
+            this.score = score;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o;
+            return score == that.score
+                    && suggestion.equals(that.suggestion);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(score, suggestion);
+        }
+
+        @Override
+        public String toString() {
+            return "QualifiedPhoneTimeZoneSuggestion{"
+                    + "suggestion=" + suggestion
+                    + ", score=" + score
+                    + '}';
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ed38e9a..a54f5d4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1629,12 +1629,7 @@
         requestedVrComponent = (aInfo.requestedVrComponent == null) ?
                 null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
 
-        lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
-        if (info.applicationInfo.isPrivilegedApp()
-                && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
-                || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
-            lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
-        }
+        lockTaskLaunchMode = getLockTaskLaunchMode(aInfo, options);
 
         if (options != null) {
             pendingOptions = options;
@@ -1642,15 +1637,27 @@
             if (usageReport != null) {
                 appTimeTracker = new AppTimeTracker(usageReport);
             }
-            final boolean useLockTask = pendingOptions.getLockTaskMode();
-            if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
-                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
-            }
             // Gets launch display id from options. It returns INVALID_DISPLAY if not set.
             mHandoverLaunchDisplayId = options.getLaunchDisplayId();
         }
     }
 
+    static int getLockTaskLaunchMode(ActivityInfo aInfo, @Nullable ActivityOptions options) {
+        int lockTaskLaunchMode = aInfo.lockTaskLaunchMode;
+        if (aInfo.applicationInfo.isPrivilegedApp()
+                && (lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_ALWAYS
+                || lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_NEVER)) {
+            lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+        }
+        if (options != null) {
+            final boolean useLockTask = options.getLockTaskMode();
+            if (useLockTask && lockTaskLaunchMode == LOCK_TASK_LAUNCH_MODE_DEFAULT) {
+                lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED;
+            }
+        }
+        return lockTaskLaunchMode;
+    }
+
     @Override
     ActivityRecord asActivityRecord() {
         // I am an activity record!
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 2fb0ac5..76aa1d1 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -52,6 +52,7 @@
 import android.os.UserManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.BlockedAppActivity;
 import com.android.internal.app.HarmfulAppWarningActivity;
 import com.android.internal.app.SuspendedAppActivity;
 import com.android.internal.app.UnlaunchableAppActivity;
@@ -166,6 +167,9 @@
             // no user action can undo this.
             return true;
         }
+        if (interceptLockTaskModeViolationPackageIfNeeded()) {
+            return true;
+        }
         if (interceptHarmfulAppIfNeeded()) {
             // If the app has a "harmful app" warning associated with it, we should ask to uninstall
             // before issuing the work challenge.
@@ -270,6 +274,25 @@
         return true;
     }
 
+    private boolean interceptLockTaskModeViolationPackageIfNeeded() {
+        if (mAInfo == null || mAInfo.applicationInfo == null) {
+            return false;
+        }
+        LockTaskController controller = mService.getLockTaskController();
+        String packageName = mAInfo.applicationInfo.packageName;
+        int lockTaskLaunchMode = ActivityRecord.getLockTaskLaunchMode(mAInfo, mActivityOptions);
+        if (controller.isActivityAllowed(mUserId, packageName, lockTaskLaunchMode)) {
+            return false;
+        }
+        mIntent = BlockedAppActivity.createIntent(mUserId, mAInfo.applicationInfo.packageName);
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+        return true;
+    }
+
     private boolean interceptWorkProfileChallengeIfNeeded() {
         final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
         if (interceptingIntent == null) {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 02413bb..3b25b74 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -23,6 +23,8 @@
 import static android.content.Context.DEVICE_POLICY_SERVICE;
 import static android.content.Context.STATUS_BAR_SERVICE;
 import static android.content.Intent.ACTION_CALL_EMERGENCY;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_CURRENT;
 import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
@@ -339,6 +341,25 @@
                 & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
     }
 
+    private boolean isBlockingInTaskEnabled(int userId) {
+        return (getLockTaskFeaturesForUser(userId)
+                & DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK) != 0;
+    }
+
+    boolean isActivityAllowed(int userId, String packageName, int lockTaskLaunchMode) {
+        if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED || !isBlockingInTaskEnabled(userId)) {
+            return true;
+        }
+        switch (lockTaskLaunchMode) {
+            case LOCK_TASK_LAUNCH_MODE_ALWAYS:
+                return true;
+            case LOCK_TASK_LAUNCH_MODE_NEVER:
+                return false;
+            default:
+        }
+        return isPackageWhitelisted(userId, packageName);
+    }
+
     private boolean isEmergencyCallTask(Task task) {
         final Intent intent = task.intent;
         if (intent == null) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6e243f0..59eee9c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -562,4 +562,14 @@
      */
     public abstract void setAccessibilityIdToSurfaceMetadata(
             IBinder windowToken, int accessibilityWindowId);
+
+    /**
+     * Transfers input focus from a given input token to that of the IME window.
+     *
+     * @param sourceInputToken The source token.
+     * @param displayId The display hosting the IME window.
+     * @return Whether transfer was successful.
+     */
+    public abstract boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
+            int displayId);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a5b99b0..6e1f46bb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7558,6 +7558,29 @@
                 }
             }
         }
+
+        @Override
+        public boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
+                int displayId) {
+            final IBinder destinationInputToken;
+
+            synchronized (mGlobalLock) {
+                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+                if (displayContent == null) {
+                    return false;
+                }
+                final WindowState imeWindow = displayContent.mInputMethodWindow;
+                if (imeWindow == null) {
+                    return false;
+                }
+                if (imeWindow.mInputChannel == null) {
+                    return false;
+                }
+                destinationInputToken = imeWindow.mInputChannel.getToken();
+            }
+
+            return mInputManager.transferTouchFocus(sourceInputToken, destinationInputToken);
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index 7644ade..aa7067e 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -137,7 +137,7 @@
 #define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
 #define FRAME_COUNTS_FIELD_NUMBER 2
 
-static void writeCpuHistogram(stats_event* event,
+static void writeCpuHistogram(AStatsEvent* event,
                               const uirenderer::protos::GraphicsStatsProto& stat) {
     util::ProtoOutputStream proto;
     for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
@@ -154,10 +154,10 @@
     }
     std::vector<uint8_t> outVector;
     proto.serializeToVector(&outVector);
-    stats_event_write_byte_array(event, outVector.data(), outVector.size());
+    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
 }
 
-static void writeGpuHistogram(stats_event* event,
+static void writeGpuHistogram(AStatsEvent* event,
                               const uirenderer::protos::GraphicsStatsProto& stat) {
     util::ProtoOutputStream proto;
     for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
@@ -174,20 +174,20 @@
     }
     std::vector<uint8_t> outVector;
     proto.serializeToVector(&outVector);
-    stats_event_write_byte_array(event, outVector.data(), outVector.size());
+    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
 }
 
 // graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
-static status_pull_atom_return_t graphicsStatsPullCallback(int32_t atom_tag,
-                                                           pulled_stats_event_list* data,
-                                                           void* cookie) {
+static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
+                                                                      AStatsEventList* data,
+                                                                      void* cookie) {
     JNIEnv* env = getJNIEnv();
     if (!env) {
         return false;
     }
     if (gGraphicsStatsServiceObject == nullptr) {
         ALOGE("Failed to get graphicsstats service");
-        return STATS_PULL_SKIP;
+        return AStatsManager_PULL_SKIP;
     }
 
     for (bool lastFullDay : {true, false}) {
@@ -199,7 +199,7 @@
             env->ExceptionDescribe();
             env->ExceptionClear();
             ALOGE("Failed to invoke graphicsstats service");
-            return STATS_PULL_SKIP;
+            return AStatsManager_PULL_SKIP;
         }
         if (!jdata) {
             // null means data is not available for that day.
@@ -218,49 +218,51 @@
         if (!success) {
             ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
                   serviceDump.InitializationErrorString().c_str(), dataSize);
-            return STATS_PULL_SKIP;
+            return AStatsManager_PULL_SKIP;
         }
 
         for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
             auto& stat = serviceDump.stats(stat_index);
-            stats_event* event = add_stats_event_to_pull_data(data);
-            stats_event_set_atom_id(event, android::util::GRAPHICS_STATS);
-            stats_event_write_string8(event, stat.package_name().c_str());
-            stats_event_write_int64(event, (int64_t)stat.version_code());
-            stats_event_write_int64(event, (int64_t)stat.stats_start());
-            stats_event_write_int64(event, (int64_t)stat.stats_end());
-            stats_event_write_int32(event, (int32_t)stat.pipeline());
-            stats_event_write_int32(event, (int32_t)stat.summary().total_frames());
-            stats_event_write_int32(event, (int32_t)stat.summary().missed_vsync_count());
-            stats_event_write_int32(event, (int32_t)stat.summary().high_input_latency_count());
-            stats_event_write_int32(event, (int32_t)stat.summary().slow_ui_thread_count());
-            stats_event_write_int32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
-            stats_event_write_int32(event, (int32_t)stat.summary().slow_draw_count());
-            stats_event_write_int32(event, (int32_t)stat.summary().missed_deadline_count());
+            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+            AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS);
+            AStatsEvent_writeString(event, stat.package_name().c_str());
+            AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
+            AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
+            AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
+            AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
+            AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
+            AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
+            AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
+            AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
+            AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
+            AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
+            AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
             writeCpuHistogram(event, stat);
             writeGpuHistogram(event, stat);
             // TODO: fill in UI mainline module version, when the feature is available.
-            stats_event_write_int64(event, (int64_t)0);
-            stats_event_write_bool(event, !lastFullDay);
-            stats_event_build(event);
+            AStatsEvent_writeInt64(event, (int64_t)0);
+            AStatsEvent_writeBool(event, !lastFullDay);
+            AStatsEvent_build(event);
         }
     }
-    return STATS_PULL_SUCCESS;
+    return AStatsManager_PULL_SUCCESS;
 }
 
 // Register a puller for GRAPHICS_STATS atom with the statsd service.
 static void nativeInit(JNIEnv* env, jobject javaObject) {
     gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
-    pull_atom_metadata metadata = {.cool_down_ns = 10 * 1000000, // 10 milliseconds
-                                   .timeout_ns = 2 * NS_PER_SEC, // 2 seconds
-                                   .additive_fields = nullptr,
-                                   .additive_fields_size = 0};
-    register_stats_pull_atom_callback(android::util::GRAPHICS_STATS, &graphicsStatsPullCallback,
-            &metadata, nullptr);
+    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
+    AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000);  // 10 milliseconds
+    AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
+
+    AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
+                                           &graphicsStatsPullCallback, metadata, nullptr);
+
+    AStatsManager_PullAtomMetadata_release(metadata);
 }
 
 static void nativeDestructor(JNIEnv* env, jobject javaObject) {
-    //TODO: Unregister the puller callback when a new API is available.
+    AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
     env->DeleteGlobalRef(gGraphicsStatsServiceObject);
     gGraphicsStatsServiceObject = nullptr;
 }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 212a3a6..49db3d5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1563,20 +1563,17 @@
 }
 
 static jboolean nativeTransferTouchFocus(JNIEnv* env,
-        jclass /* clazz */, jlong ptr, jobject fromChannelObj, jobject toChannelObj) {
-    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-
-    sp<InputChannel> fromChannel =
-            android_view_InputChannel_getInputChannel(env, fromChannelObj);
-    sp<InputChannel> toChannel =
-            android_view_InputChannel_getInputChannel(env, toChannelObj);
-
-    if (fromChannel == nullptr || toChannel == nullptr) {
+        jclass /* clazz */, jlong ptr, jobject fromChannelTokenObj, jobject toChannelTokenObj) {
+    if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) {
         return JNI_FALSE;
     }
 
+    sp<IBinder> fromChannelToken = ibinderForJavaObject(env, fromChannelTokenObj);
+    sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj);
+
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
     if (im->getInputManager()->getDispatcher()->transferTouchFocus(
-            fromChannel->getConnectionToken(), toChannel->getConnectionToken())) {
+            fromChannelToken, toChannelToken)) {
         return JNI_TRUE;
     } else {
         return JNI_FALSE;
@@ -1784,7 +1781,7 @@
             (void*) nativeSetInputDispatchMode },
     { "nativeSetSystemUiVisibility", "(JI)V",
             (void*) nativeSetSystemUiVisibility },
-    { "nativeTransferTouchFocus", "(JLandroid/view/InputChannel;Landroid/view/InputChannel;)Z",
+    { "nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z",
             (void*) nativeTransferTouchFocus },
     { "nativeSetPointerSpeed", "(JI)V",
             (void*) nativeSetPointerSpeed },
diff --git a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
index f5b778e..43cd0a2 100644
--- a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
+++ b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
@@ -31,32 +31,32 @@
 static server::stats::PowerStatsPuller gPowerStatsPuller;
 static server::stats::SubsystemSleepStatePuller gSubsystemSleepStatePuller;
 
-static status_pull_atom_return_t onDevicePowerMeasurementCallback(int32_t atom_tag,
-                                                                  pulled_stats_event_list* data,
-                                                                  void* cookie) {
+static AStatsManager_PullAtomCallbackReturn onDevicePowerMeasurementCallback(int32_t atom_tag,
+                                                                             AStatsEventList* data,
+                                                                             void* cookie) {
     return gPowerStatsPuller.Pull(atom_tag, data);
 }
 
-static status_pull_atom_return_t subsystemSleepStateCallback(int32_t atom_tag,
-                                                             pulled_stats_event_list* data,
-                                                             void* cookie) {
+static AStatsManager_PullAtomCallbackReturn subsystemSleepStateCallback(int32_t atom_tag,
+                                                                        AStatsEventList* data,
+                                                                        void* cookie) {
     return gSubsystemSleepStatePuller.Pull(atom_tag, data);
 }
 
 static void nativeInit(JNIEnv* env, jobject javaObject) {
     // on device power measurement
     gPowerStatsPuller = server::stats::PowerStatsPuller();
-    register_stats_pull_atom_callback(android::util::ON_DEVICE_POWER_MEASUREMENT,
-                                      onDevicePowerMeasurementCallback,
-                                      /* metadata= */ nullptr,
-                                      /* cookie= */ nullptr);
+    AStatsManager_registerPullAtomCallback(android::util::ON_DEVICE_POWER_MEASUREMENT,
+                                           onDevicePowerMeasurementCallback,
+                                           /* metadata= */ nullptr,
+                                           /* cookie= */ nullptr);
 
     // subsystem sleep state
     gSubsystemSleepStatePuller = server::stats::SubsystemSleepStatePuller();
-    register_stats_pull_atom_callback(android::util::SUBSYSTEM_SLEEP_STATE,
-                                      subsystemSleepStateCallback,
-                                      /* metadata= */ nullptr,
-                                      /* cookie= */ nullptr);
+    AStatsManager_registerPullAtomCallback(android::util::SUBSYSTEM_SLEEP_STATE,
+                                           subsystemSleepStateCallback,
+                                           /* metadata= */ nullptr,
+                                           /* cookie= */ nullptr);
 }
 
 static const JNINativeMethod sMethods[] = {{"nativeInit", "()V", (void*)nativeInit}};
diff --git a/services/core/jni/stats/PowerStatsPuller.cpp b/services/core/jni/stats/PowerStatsPuller.cpp
index e80b5cf..d8f6faa 100644
--- a/services/core/jni/stats/PowerStatsPuller.cpp
+++ b/services/core/jni/stats/PowerStatsPuller.cpp
@@ -78,11 +78,12 @@
 
 PowerStatsPuller::PowerStatsPuller() {}
 
-status_pull_atom_return_t PowerStatsPuller::Pull(int32_t atomTag, pulled_stats_event_list* data) {
+AStatsManager_PullAtomCallbackReturn PowerStatsPuller::Pull(int32_t atomTag,
+                                                            AStatsEventList* data) {
     std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
 
     if (!getPowerStatsHalLocked()) {
-        return STATS_PULL_SKIP;
+        return AStatsManager_PULL_SKIP;
     }
 
     // Pull getRailInfo if necessary
@@ -100,14 +101,14 @@
         if (!resultSuccess || !ret.isOk()) {
             ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
             gPowerStatsHal = nullptr;
-            return STATS_PULL_SKIP;
+            return AStatsManager_PULL_SKIP;
         }
         // If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
         if (gRailInfo.empty()) {
             ALOGE("power.stats has no rail information");
             gPowerStatsExist = false; // No rail info, so never try again.
             gPowerStatsHal = nullptr;
-            return STATS_PULL_SKIP;
+            return AStatsManager_PULL_SKIP;
         }
     }
 
@@ -134,15 +135,16 @@
                                             }
                                             const RailInfo& rail = gRailInfo[energyData.index];
 
-                                            stats_event* event = add_stats_event_to_pull_data(data);
-                                            stats_event_set_atom_id(event,
-                                                                    android::util::ON_DEVICE_POWER_MEASUREMENT);
-                                            stats_event_write_string8(event,
-                                                                      rail.subsysName.c_str());
-                                            stats_event_write_string8(event, rail.railName.c_str());
-                                            stats_event_write_int64(event, energyData.timestamp);
-                                            stats_event_write_int64(event, energyData.energy);
-                                            stats_event_build(event);
+                                            AStatsEvent* event =
+                                                    AStatsEventList_addStatsEvent(data);
+                                            AStatsEvent_setAtomId(
+                                                    event,
+                                                    android::util::ON_DEVICE_POWER_MEASUREMENT);
+                                            AStatsEvent_writeString(event, rail.subsysName.c_str());
+                                            AStatsEvent_writeString(event, rail.railName.c_str());
+                                            AStatsEvent_writeInt64(event, energyData.timestamp);
+                                            AStatsEvent_writeInt64(event, energyData.energy);
+                                            AStatsEvent_build(event);
 
                                             ALOGV("power.stat: %s.%s: %llu, %llu",
                                                   rail.subsysName.c_str(), rail.railName.c_str(),
@@ -153,9 +155,9 @@
     if (!resultSuccess || !ret.isOk()) {
         ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
         gPowerStatsHal = nullptr;
-        return STATS_PULL_SKIP;
+        return AStatsManager_PULL_SKIP;
     }
-    return STATS_PULL_SUCCESS;
+    return AStatsManager_PULL_SUCCESS;
 }
 
 } // namespace stats
diff --git a/services/core/jni/stats/PowerStatsPuller.h b/services/core/jni/stats/PowerStatsPuller.h
index 048dbb9..db07d60 100644
--- a/services/core/jni/stats/PowerStatsPuller.h
+++ b/services/core/jni/stats/PowerStatsPuller.h
@@ -29,7 +29,7 @@
 class PowerStatsPuller {
 public:
     PowerStatsPuller();
-    status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
+    AStatsManager_PullAtomCallbackReturn Pull(int32_t atomTag, AStatsEventList* data);
 };
 
 } // namespace stats
diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.cpp b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
index c6a836c..45afb5e 100644
--- a/services/core/jni/stats/SubsystemSleepStatePuller.cpp
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
@@ -55,7 +55,7 @@
 namespace server {
 namespace stats {
 
-static std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+static std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList* data)>
         gPuller = {};
 
 static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
@@ -176,12 +176,12 @@
 }
 
 // The caller must be holding gPowerHalMutex.
-static status_pull_atom_return_t getIPowerStatsDataLocked(int32_t atomTag,
-                                                          pulled_stats_event_list* data) {
+static AStatsManager_PullAtomCallbackReturn getIPowerStatsDataLocked(int32_t atomTag,
+                                                                     AStatsEventList* data) {
     using android::hardware::power::stats::V1_0::Status;
 
     if(!getPowerStatsHalLocked()) {
-        return STATS_PULL_SKIP;
+        return AStatsManager_PULL_SKIP;
     }
     // Get power entity state residency data
     bool success = false;
@@ -194,17 +194,17 @@
                 }
                 for (auto result : results) {
                     for (auto stateResidency : result.stateResidencyData) {
-                        stats_event* event = add_stats_event_to_pull_data(data);
-                        stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
-                        stats_event_write_string8(event,
-                                                  gEntityNames.at(result.powerEntityId).c_str());
-                        stats_event_write_string8(event,
-                                                  gStateNames.at(result.powerEntityId)
-                                                          .at(stateResidency.powerEntityStateId)
-                                                          .c_str());
-                        stats_event_write_int64(event, stateResidency.totalStateEntryCount);
-                        stats_event_write_int64(event, stateResidency.totalTimeInStateMs);
-                        stats_event_build(event);
+                        AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+                        AStatsEvent_setAtomId(event, android::util::SUBSYSTEM_SLEEP_STATE);
+                        AStatsEvent_writeString(event,
+                                                gEntityNames.at(result.powerEntityId).c_str());
+                        AStatsEvent_writeString(event,
+                                                gStateNames.at(result.powerEntityId)
+                                                        .at(stateResidency.powerEntityStateId)
+                                                        .c_str());
+                        AStatsEvent_writeInt64(event, stateResidency.totalStateEntryCount);
+                        AStatsEvent_writeInt64(event, stateResidency.totalTimeInStateMs);
+                        AStatsEvent_build(event);
                     }
                 }
                 success = true;
@@ -213,9 +213,9 @@
     // bool success determines if this succeeded or not.
     checkResultLocked(ret, __func__);
     if (!success) {
-        return STATS_PULL_SKIP;
+        return AStatsManager_PULL_SKIP;
     }
-    return STATS_PULL_SUCCESS;
+    return AStatsManager_PULL_SUCCESS;
 }
 
 // The caller must be holding gPowerHalMutex.
@@ -244,12 +244,12 @@
 }
 
 // The caller must be holding gPowerHalMutex.
-static status_pull_atom_return_t getIPowerDataLocked(int32_t atomTag,
-                                                     pulled_stats_event_list* data) {
+static AStatsManager_PullAtomCallbackReturn getIPowerDataLocked(int32_t atomTag,
+                                                                AStatsEventList* data) {
     using android::hardware::power::V1_0::Status;
 
     if(!getPowerHalLocked()) {
-        return STATS_PULL_SKIP;
+        return AStatsManager_PULL_SKIP;
     }
 
         Return<void> ret;
@@ -259,26 +259,26 @@
 
                     for (size_t i = 0; i < states.size(); i++) {
                         const PowerStatePlatformSleepState& state = states[i];
-                        stats_event* event = add_stats_event_to_pull_data(data);
-                        stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
-                        stats_event_write_string8(event, state.name.c_str());
-                        stats_event_write_string8(event, "");
-                        stats_event_write_int64(event, state.totalTransitions);
-                        stats_event_write_int64(event, state.residencyInMsecSinceBoot);
-                        stats_event_build(event);
+                        AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+                        AStatsEvent_setAtomId(event, android::util::SUBSYSTEM_SLEEP_STATE);
+                        AStatsEvent_writeString(event, state.name.c_str());
+                        AStatsEvent_writeString(event, "");
+                        AStatsEvent_writeInt64(event, state.totalTransitions);
+                        AStatsEvent_writeInt64(event, state.residencyInMsecSinceBoot);
+                        AStatsEvent_build(event);
 
                         ALOGV("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
                               (long long)state.residencyInMsecSinceBoot,
                               (long long)state.totalTransitions,
                               state.supportedOnlyInSuspend ? 1 : 0);
                         for (const auto& voter : state.voters) {
-                            stats_event* event = add_stats_event_to_pull_data(data);
-                            stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
-                            stats_event_write_string8(event, state.name.c_str());
-                            stats_event_write_string8(event, voter.name.c_str());
-                            stats_event_write_int64(event, voter.totalNumberOfTimesVotedSinceBoot);
-                            stats_event_write_int64(event, voter.totalTimeInMsecVotedForSinceBoot);
-                            stats_event_build(event);
+                            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+                            AStatsEvent_setAtomId(event, android::util::SUBSYSTEM_SLEEP_STATE);
+                            AStatsEvent_writeString(event, state.name.c_str());
+                            AStatsEvent_writeString(event, voter.name.c_str());
+                            AStatsEvent_writeInt64(event, voter.totalNumberOfTimesVotedSinceBoot);
+                            AStatsEvent_writeInt64(event, voter.totalTimeInMsecVotedForSinceBoot);
+                            AStatsEvent_build(event);
 
                             ALOGV("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
                                   voter.name.c_str(),
@@ -288,7 +288,7 @@
                     }
                 });
         if (!checkResultLocked(ret, __func__)) {
-            return STATS_PULL_SKIP;
+            return AStatsManager_PULL_SKIP;
         }
 
         // Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
@@ -305,14 +305,14 @@
                                 for (size_t j = 0; j < subsystem.states.size(); j++) {
                                     const PowerStateSubsystemSleepState& state =
                                             subsystem.states[j];
-                                    stats_event* event = add_stats_event_to_pull_data(data);
-                                    stats_event_set_atom_id(event,
-                                                            android::util::SUBSYSTEM_SLEEP_STATE);
-                                    stats_event_write_string8(event, subsystem.name.c_str());
-                                    stats_event_write_string8(event, state.name.c_str());
-                                    stats_event_write_int64(event, state.totalTransitions);
-                                    stats_event_write_int64(event, state.residencyInMsecSinceBoot);
-                                    stats_event_build(event);
+                                    AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+                                    AStatsEvent_setAtomId(event,
+                                                          android::util::SUBSYSTEM_SLEEP_STATE);
+                                    AStatsEvent_writeString(event, subsystem.name.c_str());
+                                    AStatsEvent_writeString(event, state.name.c_str());
+                                    AStatsEvent_writeInt64(event, state.totalTransitions);
+                                    AStatsEvent_writeInt64(event, state.residencyInMsecSinceBoot);
+                                    AStatsEvent_build(event);
 
                                     ALOGV("subsystemstate: %s, %s, %lld, %lld, %lld",
                                           subsystem.name.c_str(), state.name.c_str(),
@@ -324,14 +324,14 @@
                         }
                     });
         }
-        return STATS_PULL_SUCCESS;
+        return AStatsManager_PULL_SUCCESS;
 }
 
 // The caller must be holding gPowerHalMutex.
-std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList* data)>
 getPullerLocked() {
-    std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list * data)> ret =
-            {};
+    std::function<AStatsManager_PullAtomCallbackReturn(int32_t atomTag, AStatsEventList * data)>
+            ret = {};
 
     // First see if power.stats HAL is available. Fall back to power HAL if
     // power.stats HAL is unavailable.
@@ -346,8 +346,8 @@
     return ret;
 }
 
-status_pull_atom_return_t SubsystemSleepStatePuller::Pull(int32_t atomTag,
-                                                          pulled_stats_event_list* data) {
+AStatsManager_PullAtomCallbackReturn SubsystemSleepStatePuller::Pull(int32_t atomTag,
+                                                                     AStatsEventList* data) {
     std::lock_guard<std::mutex> lock(gPowerHalMutex);
 
     if(!gPuller) {
@@ -359,7 +359,7 @@
     }
 
     ALOGE("Unable to load Power Hal or power.stats HAL");
-    return STATS_PULL_SKIP;
+    return AStatsManager_PULL_SKIP;
 }
 
 } // namespace stats
diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.h b/services/core/jni/stats/SubsystemSleepStatePuller.h
index 59dbbd2..da9679c 100644
--- a/services/core/jni/stats/SubsystemSleepStatePuller.h
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.h
@@ -29,7 +29,7 @@
 class SubsystemSleepStatePuller {
 public:
     SubsystemSleepStatePuller();
-    status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
+    AStatsManager_PullAtomCallbackReturn Pull(int32_t atomTag, AStatsEventList* data);
 };
 
 } // namespace stats
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 65cabad..28e44f1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4680,12 +4680,15 @@
 
     private void ensureMinimumQuality(
             int userId, ActiveAdmin admin, int minimumQuality, String operation) {
-        if (admin.mPasswordPolicy.quality < minimumQuality
-                && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
-                userId)) {
-            throw new IllegalStateException(String.format(
-                    "password quality should be at least %d for %s", minimumQuality, operation));
-        }
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            if (admin.mPasswordPolicy.quality < minimumQuality
+                    && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
+                    userId)) {
+                throw new IllegalStateException(String.format(
+                        "password quality should be at least %d for %s",
+                        minimumQuality, operation));
+            }
+        });
     }
 
     @Override
@@ -5343,7 +5346,7 @@
         synchronized (getLockObject()) {
             ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked(
                     userHandle, parent);
-            return admin != null ? admin.getUserHandle().getIdentifier() : UserHandle.USER_NULL;
+            return admin != null ? getUserIdToWipeForFailedPasswords(admin) : UserHandle.USER_NULL;
         }
     }
 
@@ -5354,7 +5357,8 @@
      *   <li>this user and all profiles that don't have their own challenge otherwise.
      * </ul>
      * <p>If the policy for the primary and any other profile are equal, it returns the admin for
-     * the primary profile.
+     * the primary profile. Policy of a PO on an organization-owned device applies to the primary
+     * profile.
      * Returns {@code null} if no participating admin has that policy set.
      */
     private ActiveAdmin getAdminWithMinimumFailedPasswordsForWipeLocked(
@@ -5373,7 +5377,7 @@
             }
 
             // We always favor the primary profile if several profiles have the same value set.
-            int userId = admin.getUserHandle().getIdentifier();
+            final int userId = getUserIdToWipeForFailedPasswords(admin);
             if (count == 0 ||
                     count > admin.maximumFailedPasswordsForWipe ||
                     (count == admin.maximumFailedPasswordsForWipe &&
@@ -7170,7 +7174,7 @@
         }
 
         if (wipeData && strictestAdmin != null) {
-            final int userId = strictestAdmin.getUserHandle().getIdentifier();
+            final int userId = getUserIdToWipeForFailedPasswords(strictestAdmin);
             Slog.i(LOG_TAG, "Max failed password attempts policy reached for admin: "
                     + strictestAdmin.info.getComponent().flattenToShortString()
                     + ". Calling wipeData for user " + userId);
@@ -7201,6 +7205,17 @@
         }
     }
 
+    /**
+     * Returns which user should be wiped if this admin's maximum filed password attempts policy is
+     * violated.
+     */
+    private int getUserIdToWipeForFailedPasswords(ActiveAdmin admin) {
+        final int userId = admin.getUserHandle().getIdentifier();
+        final ComponentName component = admin.info.getComponent();
+        return isProfileOwnerOfOrganizationOwnedDevice(component, userId)
+                ? getProfileParentId(userId) : userId;
+    }
+
     @Override
     public void reportSuccessfulPasswordAttempt(int userHandle) {
         enforceFullCrossUsersPermission(userHandle);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index b193a34..7d5cb13 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -96,6 +96,7 @@
 import android.util.ArraySet;
 import android.util.Pair;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
@@ -4535,6 +4536,86 @@
                 .removeUserEvenWhenDisallowed(anyInt());
     }
 
+    public void testMaximumFailedDevicePasswordAttemptsReachedOrgOwnedManagedProfile()
+            throws Exception {
+        final int MANAGED_PROFILE_USER_ID = 15;
+        final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
+        addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+
+        // Even if the caller is the managed profile, the current user is the user 0
+        when(getServices().iactivityManager.getCurrentUser())
+                .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+
+        configureProfileOwnerOfOrgOwnedDevice(admin1, MANAGED_PROFILE_USER_ID);
+
+        mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+        dpm.setMaximumFailedPasswordsForWipe(admin1, 3);
+
+        assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(admin1));
+        assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(null));
+
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+
+        assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(null, UserHandle.USER_SYSTEM));
+        // Check that primary will be wiped as a result of failed primary user unlock attempts.
+        assertEquals(UserHandle.USER_SYSTEM,
+                dpm.getProfileWithMinimumFailedPasswordsForWipe(UserHandle.USER_SYSTEM));
+
+        // Failed password attempts on the parent user are taken into account, as there isn't a
+        // separate work challenge.
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+        dpm.reportFailedPasswordAttempt(UserHandle.USER_SYSTEM);
+
+        // For managed profile on an organization owned device, the whole device should be wiped.
+        verify(getServices().recoverySystem).rebootWipeUserData(
+                /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true),
+                /*wipeEuicc=*/ eq(false));
+    }
+
+    public void testMaximumFailedProfilePasswordAttemptsReachedOrgOwnedManagedProfile()
+            throws Exception {
+        final int MANAGED_PROFILE_USER_ID = 15;
+        final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
+        addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+
+        // Even if the caller is the managed profile, the current user is the user 0
+        when(getServices().iactivityManager.getCurrentUser())
+                .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+
+        doReturn(true).when(getServices().lockPatternUtils)
+                .isSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID);
+
+        // Configure separate challenge.
+        configureProfileOwnerOfOrgOwnedDevice(admin1, MANAGED_PROFILE_USER_ID);
+
+        mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+        dpm.setMaximumFailedPasswordsForWipe(admin1, 3);
+
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
+
+        assertEquals(0, dpm.getMaximumFailedPasswordsForWipe(null, UserHandle.USER_SYSTEM));
+        assertEquals(3, dpm.getMaximumFailedPasswordsForWipe(null, MANAGED_PROFILE_USER_ID));
+        // Check that the policy is not affecting primary profile challenge.
+        assertEquals(UserHandle.USER_NULL,
+                dpm.getProfileWithMinimumFailedPasswordsForWipe(UserHandle.USER_SYSTEM));
+        // Check that primary will be wiped as a result of failed profile unlock attempts.
+        assertEquals(UserHandle.USER_SYSTEM,
+                dpm.getProfileWithMinimumFailedPasswordsForWipe(MANAGED_PROFILE_USER_ID));
+
+        // Simulate three failed attempts at solving the separate challenge.
+        dpm.reportFailedPasswordAttempt(MANAGED_PROFILE_USER_ID);
+        dpm.reportFailedPasswordAttempt(MANAGED_PROFILE_USER_ID);
+        dpm.reportFailedPasswordAttempt(MANAGED_PROFILE_USER_ID);
+
+        // For managed profile on an organization owned device, the whole device should be wiped.
+        verify(getServices().recoverySystem).rebootWipeUserData(
+                /*shutdown=*/ eq(false), anyString(), /*force=*/ eq(true),
+                /*wipeEuicc=*/ eq(false));
+    }
+
     public void testGetPermissionGrantState() throws Exception {
         final String permission = "some.permission";
         final String app1 = "com.example.app1";
@@ -5366,6 +5447,7 @@
         assertTrue(dpms.isAdminActive(admin1, UserHandle.USER_SYSTEM));
     }
 
+    @FlakyTest(bugId = 148934649)
     public void testRevertDeviceOwnership_adminAndDeviceMigrated() throws Exception {
         DpmTestUtils.writeInputStreamToFile(
                 getRawStream(com.android.frameworks.servicestests.R.raw.active_admin_migrated),
@@ -5376,6 +5458,7 @@
         assertDeviceOwnershipRevertedWithFakeTransferMetadata();
     }
 
+    @FlakyTest(bugId = 148934649)
     public void testRevertDeviceOwnership_deviceNotMigrated()
             throws Exception {
         DpmTestUtils.writeInputStreamToFile(
@@ -5407,6 +5490,7 @@
         UserHandle userHandle = UserHandle.of(DpmMockContext.CALLER_USER_HANDLE);
     }
 
+    @FlakyTest(bugId = 148934649)
     public void testRevertProfileOwnership_adminAndProfileMigrated() throws Exception {
         getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
                 UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM);
@@ -5419,6 +5503,7 @@
         assertProfileOwnershipRevertedWithFakeTransferMetadata();
     }
 
+    @FlakyTest(bugId = 148934649)
     public void testRevertProfileOwnership_profileNotMigrated() throws Exception {
         getServices().addUser(DpmMockContext.CALLER_USER_HANDLE, 0,
                 UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM);
@@ -5770,8 +5855,7 @@
         when(getServices().userManager.getProfileParent(eq(UserHandle.of(userId))))
                 .thenReturn(UserHandle.SYSTEM);
         final long ident = mServiceContext.binder.clearCallingIdentity();
-        mServiceContext.binder.callingUid =
-                UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
+        mServiceContext.binder.callingUid = UserHandle.getUid(userId, DpmMockContext.SYSTEM_UID);
 
         configureContextForAccess(mServiceContext, true);
         runAsCaller(mServiceContext, dpms, dpm -> {
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index d0d2edc..64d05f0 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -119,7 +119,7 @@
 
     @Test
     public void createNonStaged() {
-        Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+        Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
 
         assertThat(rollback.getBackupDir().getAbsolutePath())
                 .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -132,7 +132,7 @@
 
     @Test
     public void createStaged() {
-        Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER);
+        Rollback rollback = mRollbackStore.createStagedRollback(ID, 897, USER, INSTALLER, null);
 
         assertThat(rollback.getBackupDir().getAbsolutePath())
                 .isEqualTo(mFolder.getRoot().getAbsolutePath() + "/" + ID);
@@ -147,7 +147,7 @@
 
     @Test
     public void saveAndLoadRollback() {
-        Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+        Rollback origRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
 
         origRb.setRestoreUserDataInProgress(true);
         origRb.info.getCausePackages().add(new VersionedPackage("com.made.up", 2));
@@ -197,7 +197,7 @@
 
     @Test
     public void loadFromJson() throws Exception {
-        Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+        Rollback expectedRb = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
 
         expectedRb.setTimestamp(Instant.parse("2019-10-01T12:29:08.855Z"));
         expectedRb.setRestoreUserDataInProgress(true);
@@ -246,7 +246,7 @@
 
     @Test
     public void saveAndDelete() {
-        Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER);
+        Rollback rollback = mRollbackStore.createNonStagedRollback(ID, USER, INSTALLER, null);
 
         RollbackStore.saveRollback(rollback);
 
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index 9719509..804c1b9 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -302,6 +302,22 @@
         assertThat(rollback.notifySessionWithSuccess()).isTrue();
     }
 
+    @Test
+    public void allPackagesEnabled() {
+        int[] sessionIds = new int[]{ 7777, 8888 };
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER,
+                sessionIds);
+        // #allPackagesEnabled returns false when 1 out of 2 packages is enabled.
+        rollback.info.getPackages().add(newPkgInfoFor(PKG_1, 12, 10, false));
+        assertThat(rollback.allPackagesEnabled()).isFalse();
+        // #allPackagesEnabled returns false for ApkInApex doesn't count.
+        rollback.info.getPackages().add(newPkgInfoForApkInApex(PKG_3, 157, 156));
+        assertThat(rollback.allPackagesEnabled()).isFalse();
+        // #allPackagesEnabled returns true when 2 out of 2 packages are enabled.
+        rollback.info.getPackages().add(newPkgInfoFor(PKG_2, 18, 12, true));
+        assertThat(rollback.allPackagesEnabled()).isTrue();
+    }
+
     private static PackageRollbackInfo newPkgInfoFor(
             String packageName, long fromVersion, long toVersion, boolean isApex) {
         return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
@@ -310,6 +326,20 @@
                 new SparseLongArray());
     }
 
+    /**
+     * TODO: merge newPkgInfoFor and newPkgInfoForApkInApex by using enums to specify
+     * 1. IS_APK
+     * 2. IS_APEX
+     * 3. IS_APK_IN_APEX
+     */
+    private static PackageRollbackInfo newPkgInfoForApkInApex(
+            String packageName, long fromVersion, long toVersion) {
+        return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
+                new VersionedPackage(packageName, toVersion),
+                new IntArray(), new ArrayList<>(), false, true, new IntArray(),
+                new SparseLongArray());
+    }
+
     private static class PackageRollbackInfoForPackage implements
             ArgumentMatcher<PackageRollbackInfo> {
         private final String mPkg;
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index ae53692..218f43c 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -33,14 +33,13 @@
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
 import android.os.TimestampedValue;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.timezonedetector.TestHandler;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -108,7 +107,7 @@
                 eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
                 anyString());
 
-        mTestHandler.waitForEmptyQueue();
+        mTestHandler.waitForMessagesToBeProcessed();
         mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
     }
 
@@ -140,7 +139,7 @@
                 eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
                 anyString());
 
-        mTestHandler.waitForEmptyQueue();
+        mTestHandler.waitForMessagesToBeProcessed();
         mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion);
     }
 
@@ -170,7 +169,7 @@
         verify(mMockContext).enforceCallingOrSelfPermission(
                 eq(android.Manifest.permission.SET_TIME), anyString());
 
-        mTestHandler.waitForEmptyQueue();
+        mTestHandler.waitForMessagesToBeProcessed();
         mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion);
     }
 
@@ -187,21 +186,23 @@
 
     @Test
     public void testAutoTimeDetectionToggle() throws Exception {
-        mTimeDetectorService.handleAutoTimeDetectionToggle();
+        mTimeDetectorService.handleAutoTimeDetectionChanged();
         mTestHandler.assertTotalMessagesEnqueued(1);
-        mTestHandler.waitForEmptyQueue();
-        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
+        mTestHandler.waitForMessagesToBeProcessed();
+        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled();
 
-        mTimeDetectorService.handleAutoTimeDetectionToggle();
+        mStubbedTimeDetectorStrategy.resetCallTracking();
+
+        mTimeDetectorService.handleAutoTimeDetectionChanged();
         mTestHandler.assertTotalMessagesEnqueued(2);
-        mTestHandler.waitForEmptyQueue();
-        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled();
+        mTestHandler.waitForMessagesToBeProcessed();
+        mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled();
     }
 
     private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
-        int phoneId = 1234;
+        int slotIndex = 1234;
         TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
-        return new PhoneTimeSuggestion.Builder(phoneId)
+        return new PhoneTimeSuggestion.Builder(slotIndex)
                 .setUtcTime(timeValue)
                 .build();
     }
@@ -222,7 +223,7 @@
         private PhoneTimeSuggestion mLastPhoneSuggestion;
         private ManualTimeSuggestion mLastManualSuggestion;
         private NetworkTimeSuggestion mLastNetworkSuggestion;
-        private boolean mLastAutoTimeDetectionToggleCalled;
+        private boolean mHandleAutoTimeDetectionChangedCalled;
         private boolean mDumpCalled;
 
         @Override
@@ -231,31 +232,26 @@
 
         @Override
         public void suggestPhoneTime(PhoneTimeSuggestion timeSuggestion) {
-            resetCallTracking();
             mLastPhoneSuggestion = timeSuggestion;
         }
 
         @Override
         public void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
-            resetCallTracking();
             mLastManualSuggestion = timeSuggestion;
         }
 
         @Override
         public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
-            resetCallTracking();
             mLastNetworkSuggestion = timeSuggestion;
         }
 
         @Override
         public void handleAutoTimeDetectionChanged() {
-            resetCallTracking();
-            mLastAutoTimeDetectionToggleCalled = true;
+            mHandleAutoTimeDetectionChangedCalled = true;
         }
 
         @Override
         public void dump(PrintWriter pw, String[] args) {
-            resetCallTracking();
             mDumpCalled = true;
         }
 
@@ -263,7 +259,7 @@
             mLastPhoneSuggestion = null;
             mLastManualSuggestion = null;
             mLastNetworkSuggestion = null;
-            mLastAutoTimeDetectionToggleCalled = false;
+            mHandleAutoTimeDetectionChangedCalled = false;
             mDumpCalled = false;
         }
 
@@ -279,45 +275,12 @@
             assertEquals(expectedSuggestion, mLastNetworkSuggestion);
         }
 
-        void verifyHandleAutoTimeDetectionToggleCalled() {
-            assertTrue(mLastAutoTimeDetectionToggleCalled);
+        void verifyHandleAutoTimeDetectionChangedCalled() {
+            assertTrue(mHandleAutoTimeDetectionChangedCalled);
         }
 
         void verifyDumpCalled() {
             assertTrue(mDumpCalled);
         }
     }
-
-    /**
-     * A Handler that can track posts/sends and wait for work to be completed.
-     */
-    private static class TestHandler extends Handler {
-
-        private int mMessagesSent;
-
-        TestHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
-            mMessagesSent++;
-            return super.sendMessageAtTime(msg, uptimeMillis);
-        }
-
-        /** Asserts the number of messages posted or sent is as expected. */
-        void assertTotalMessagesEnqueued(int expected) {
-            assertEquals(expected, mMessagesSent);
-        }
-
-        /**
-         * Waits for all currently enqueued work due to be processed to be completed before
-         * returning.
-         */
-        void waitForEmptyQueue() throws InterruptedException {
-            while (!getLooper().getQueue().isIdle()) {
-                Thread.sleep(100);
-            }
-        }
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java
new file mode 100644
index 0000000..21c9685
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * A Handler that can track posts/sends and wait for them to be completed.
+ */
+public class TestHandler extends Handler {
+
+    private final Object mMonitor = new Object();
+    private int mMessagesProcessed = 0;
+    private int mMessagesSent = 0;
+
+    public TestHandler(Looper looper) {
+        super(looper);
+    }
+
+    @Override
+    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+        synchronized (mMonitor) {
+            mMessagesSent++;
+        }
+
+        Runnable callback = msg.getCallback();
+        // Have the callback increment the mMessagesProcessed when it is done. It will notify
+        // any threads waiting for all messages to be processed if appropriate.
+        Runnable newCallback = () -> {
+            callback.run();
+            synchronized (mMonitor) {
+                mMessagesProcessed++;
+                if (mMessagesSent == mMessagesProcessed) {
+                    mMonitor.notifyAll();
+                }
+            }
+        };
+        msg.setCallback(newCallback);
+        return super.sendMessageAtTime(msg, uptimeMillis);
+    }
+
+    /** Asserts the number of messages posted or sent is as expected. */
+    public void assertTotalMessagesEnqueued(int expected) {
+        synchronized (mMonitor) {
+            assertEquals(expected, mMessagesSent);
+        }
+    }
+
+    /**
+     * Waits for all enqueued work to be completed before returning.
+     */
+    public void waitForMessagesToBeProcessed() throws InterruptedException {
+        synchronized (mMonitor) {
+            if (mMessagesSent != mMessagesProcessed) {
+                mMonitor.wait();
+            }
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
new file mode 100644
index 0000000..3e7d40a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.HandlerThread;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.PrintWriter;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeZoneDetectorServiceTest {
+
+    private Context mMockContext;
+    private StubbedTimeZoneDetectorStrategy mStubbedTimeZoneDetectorStrategy;
+
+    private TimeZoneDetectorService mTimeZoneDetectorService;
+    private HandlerThread mHandlerThread;
+    private TestHandler mTestHandler;
+
+
+    @Before
+    public void setUp() {
+        mMockContext = mock(Context.class);
+
+        // Create a thread + handler for processing the work that the service posts.
+        mHandlerThread = new HandlerThread("TimeZoneDetectorServiceTest");
+        mHandlerThread.start();
+        mTestHandler = new TestHandler(mHandlerThread.getLooper());
+
+        mStubbedTimeZoneDetectorStrategy = new StubbedTimeZoneDetectorStrategy();
+
+        mTimeZoneDetectorService = new TimeZoneDetectorService(
+                mMockContext, mTestHandler, mStubbedTimeZoneDetectorStrategy);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mHandlerThread.quit();
+        mHandlerThread.join();
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSuggestPhoneTime_withoutPermission() {
+        doThrow(new SecurityException("Mock"))
+                .when(mMockContext).enforceCallingPermission(anyString(), any());
+        PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion();
+
+        try {
+            mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+            fail();
+        } finally {
+            verify(mMockContext).enforceCallingPermission(
+                    eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+                    anyString());
+        }
+    }
+
+    @Test
+    public void testSuggestPhoneTimeZone() throws Exception {
+        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+
+        PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion();
+        mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
+
+        verify(mMockContext).enforceCallingPermission(
+                eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+                anyString());
+
+        mTestHandler.waitForMessagesToBeProcessed();
+        mStubbedTimeZoneDetectorStrategy.verifySuggestPhoneTimeZoneCalled(timeZoneSuggestion);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void testSuggestManualTime_withoutPermission() {
+        doThrow(new SecurityException("Mock"))
+                .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+        ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
+
+        try {
+            mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
+            fail();
+        } finally {
+            verify(mMockContext).enforceCallingOrSelfPermission(
+                    eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
+                    anyString());
+        }
+    }
+
+    @Test
+    public void testSuggestManualTimeZone() throws Exception {
+        doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+
+        ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
+        mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
+
+        verify(mMockContext).enforceCallingOrSelfPermission(
+                eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
+                anyString());
+
+        mTestHandler.waitForMessagesToBeProcessed();
+        mStubbedTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled(timeZoneSuggestion);
+    }
+
+    @Test
+    public void testDump() {
+        when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        mTimeZoneDetectorService.dump(null, null, null);
+
+        verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP));
+        mStubbedTimeZoneDetectorStrategy.verifyDumpCalled();
+    }
+
+    @Test
+    public void testAutoTimeZoneDetectionChanged() throws Exception {
+        mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+        mTestHandler.assertTotalMessagesEnqueued(1);
+        mTestHandler.waitForMessagesToBeProcessed();
+        mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneDetectionChangedCalled();
+
+        mStubbedTimeZoneDetectorStrategy.resetCallTracking();
+
+        mTimeZoneDetectorService.handleAutoTimeZoneDetectionChanged();
+        mTestHandler.assertTotalMessagesEnqueued(2);
+        mTestHandler.waitForMessagesToBeProcessed();
+        mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneDetectionChangedCalled();
+    }
+
+    private static PhoneTimeZoneSuggestion createPhoneTimeZoneSuggestion() {
+        int slotIndex = 1234;
+        return new PhoneTimeZoneSuggestion.Builder(slotIndex)
+                .setZoneId("TestZoneId")
+                .setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET)
+                .setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE)
+                .build();
+    }
+
+    private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() {
+        return new ManualTimeZoneSuggestion("TestZoneId");
+    }
+
+    private static class StubbedTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
+
+        // Call tracking.
+        private PhoneTimeZoneSuggestion mLastPhoneSuggestion;
+        private ManualTimeZoneSuggestion mLastManualSuggestion;
+        private boolean mHandleAutoTimeZoneDetectionChangedCalled;
+        private boolean mDumpCalled;
+
+        @Override
+        public void suggestPhoneTimeZone(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+            mLastPhoneSuggestion = timeZoneSuggestion;
+        }
+
+        @Override
+        public void suggestManualTimeZone(ManualTimeZoneSuggestion timeZoneSuggestion) {
+            mLastManualSuggestion = timeZoneSuggestion;
+        }
+
+        @Override
+        public void handleAutoTimeZoneDetectionChanged() {
+            mHandleAutoTimeZoneDetectionChangedCalled = true;
+        }
+
+        @Override
+        public void dump(PrintWriter pw, String[] args) {
+            mDumpCalled = true;
+        }
+
+        void resetCallTracking() {
+            mLastPhoneSuggestion = null;
+            mLastManualSuggestion = null;
+            mHandleAutoTimeZoneDetectionChangedCalled = false;
+            mDumpCalled = false;
+        }
+
+        void verifySuggestPhoneTimeZoneCalled(PhoneTimeZoneSuggestion expectedSuggestion) {
+            assertEquals(expectedSuggestion, mLastPhoneSuggestion);
+        }
+
+        public void verifySuggestManualTimeZoneCalled(ManualTimeZoneSuggestion expectedSuggestion) {
+            assertEquals(expectedSuggestion, mLastManualSuggestion);
+        }
+
+        void verifyHandleAutoTimeZoneDetectionChangedCalled() {
+            assertTrue(mHandleAutoTimeZoneDetectionChangedCalled);
+        }
+
+        void verifyDumpCalled() {
+            assertTrue(mDumpCalled);
+        }
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 2429cfc..1e38711 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -24,12 +24,12 @@
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
 import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
 
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGH;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_HIGHEST;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_LOW;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_MEDIUM;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_NONE;
-import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.PHONE_SCORE_USAGE_THRESHOLD;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGH;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGHEST;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_LOW;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_MEDIUM;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_NONE;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_USAGE_THRESHOLD;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -41,7 +41,7 @@
 import android.app.timezonedetector.PhoneTimeZoneSuggestion.MatchType;
 import android.app.timezonedetector.PhoneTimeZoneSuggestion.Quality;
 
-import com.android.server.timezonedetector.TimeZoneDetectorStrategy.QualifiedPhoneTimeZoneSuggestion;
+import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedPhoneTimeZoneSuggestion;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -52,9 +52,9 @@
 import java.util.LinkedList;
 
 /**
- * White-box unit tests for {@link TimeZoneDetectorStrategy}.
+ * White-box unit tests for {@link TimeZoneDetectorStrategyImpl}.
  */
-public class TimeZoneDetectorStrategyTest {
+public class TimeZoneDetectorStrategyImplTest {
 
     /** A time zone used for initialization that does not occur elsewhere in tests. */
     private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
@@ -78,14 +78,14 @@
             newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGHEST),
     };
 
-    private TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
+    private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
     private FakeTimeZoneDetectorStrategyCallback mFakeTimeZoneDetectorStrategyCallback;
 
     @Before
     public void setUp() {
         mFakeTimeZoneDetectorStrategyCallback = new FakeTimeZoneDetectorStrategyCallback();
         mTimeZoneDetectorStrategy =
-                new TimeZoneDetectorStrategy(mFakeTimeZoneDetectorStrategyCallback);
+                new TimeZoneDetectorStrategyImpl(mFakeTimeZoneDetectorStrategyCallback);
     }
 
     @Test
@@ -364,7 +364,7 @@
     }
 
     /**
-     * The {@link TimeZoneDetectorStrategy.Callback} is left to detect whether changing the time
+     * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
      * zone is actually necessary. This test proves that the service doesn't assume it knows the
      * current setting.
      */
@@ -441,7 +441,8 @@
         return new PhoneTimeZoneSuggestion.Builder(PHONE2_ID).build();
     }
 
-    static class FakeTimeZoneDetectorStrategyCallback implements TimeZoneDetectorStrategy.Callback {
+    static class FakeTimeZoneDetectorStrategyCallback
+            implements TimeZoneDetectorStrategyImpl.Callback {
 
         private boolean mAutoTimeZoneDetectionEnabled;
         private TestState<String> mTimeZoneId = new TestState<>();
@@ -560,7 +561,7 @@
 
         Script autoTimeZoneDetectionEnabled(boolean enabled) {
             mFakeTimeZoneDetectorStrategyCallback.setAutoTimeZoneDetectionEnabled(enabled);
-            mTimeZoneDetectorStrategy.handleAutoTimeZoneDetectionChange();
+            mTimeZoneDetectorStrategy.handleAutoTimeZoneDetectionChanged();
             return this;
         }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index d16c232a..47ad831 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -97,7 +97,7 @@
         NotificationChannel updatedChannel =
                 new NotificationChannel("a", "", IMPORTANCE_HIGH);
         when(mConfig.getConversationNotificationChannel(
-                any(), anyInt(), eq("a"), eq(r.sbn.getShortcutId(mContext)), eq(true), eq(false)))
+                any(), anyInt(), eq("a"), eq(r.getSbn().getShortcutId(mContext)), eq(true), eq(false)))
                 .thenReturn(updatedChannel);
 
         assertNull(extractor.process(r));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 34872db..e0ee3ce 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -30,6 +30,9 @@
 import static android.app.NotificationManager.IMPORTANCE_MAX;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
@@ -67,6 +70,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -684,8 +688,9 @@
         NotificationRecord nrBubble = generateMessageBubbleNotifRecord(true /* addMetadata */,
                 mTestNotificationChannel, 1 /* id */, "tag", groupKey, false /* isSummary */);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nrBubble.sbn.getTag(),
-                nrBubble.sbn.getId(), nrBubble.sbn.getNotification(), nrBubble.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nrBubble.getSbn().getTag(),
+                nrBubble.getSbn().getId(), nrBubble.getSbn().getNotification(),
+                nrBubble.getSbn().getUserId());
         waitForIdle();
 
         // Make sure we are a bubble
@@ -697,8 +702,9 @@
         NotificationRecord nrPlain = generateMessageBubbleNotifRecord(false /* addMetadata */,
                 mTestNotificationChannel, 2 /* id */, "tag", groupKey, false /* isSummary */);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nrPlain.sbn.getTag(),
-                nrPlain.sbn.getId(), nrPlain.sbn.getNotification(), nrPlain.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nrPlain.getSbn().getTag(),
+                nrPlain.getSbn().getId(), nrPlain.getSbn().getNotification(),
+                nrPlain.getSbn().getUserId());
         waitForIdle();
 
         notifsAfter = mBinderService.getActiveNotifications(PKG);
@@ -711,8 +717,9 @@
         if (summaryAutoCancel) {
             nrSummary.getNotification().flags |= FLAG_AUTO_CANCEL;
         }
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nrSummary.sbn.getTag(),
-                nrSummary.sbn.getId(), nrSummary.sbn.getNotification(), nrSummary.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nrSummary.getSbn().getTag(),
+                nrSummary.getSbn().getId(), nrSummary.getSbn().getNotification(),
+                nrSummary.getSbn().getUserId());
         waitForIdle();
 
         notifsAfter = mBinderService.getActiveNotifications(PKG);
@@ -891,7 +898,7 @@
 
         mBinderService.createNotificationChannels(
                 PKG, new ParceledListSlice(Arrays.asList(channel)));
-        final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(channel).getSbn();
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testBlockedNotifications_blockedChannel",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -909,7 +916,7 @@
         mBinderService.createNotificationChannels(
                 PKG, new ParceledListSlice(Arrays.asList(channel)));
 
-        final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(channel).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -938,7 +945,7 @@
         assertEquals(IMPORTANCE_NONE, mBinderService.getNotificationChannel(
                 PKG, mContext.getUserId(), PKG, channel.getId()).getImportance());
 
-        StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
+        StatusBarNotification sbn = generateNotificationRecord(channel).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -960,7 +967,7 @@
         assertEquals(IMPORTANCE_NONE, mBinderService.getNotificationChannel(
                 PKG, mContext.getUserId(), PKG, channel.getId()).getImportance());
 
-        sbn = generateNotificationRecord(channel).sbn;
+        sbn = generateNotificationRecord(channel).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testEnqueuedBlockedNotifications_userBlockedChannelForegroundService",
@@ -994,7 +1001,7 @@
 
         mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
 
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testEnqueuedBlockedNotifications_blockedApp",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -1008,7 +1015,7 @@
 
         mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
 
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testEnqueuedBlockedNotifications_blockedAppForegroundService",
@@ -1031,7 +1038,7 @@
         int id = 0;
         for (String category: categories) {
             final StatusBarNotification sbn =
-                    generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
+                    generateNotificationRecord(mTestNotificationChannel, ++id, "", false).getSbn();
             sbn.getNotification().category = category;
             mBinderService.enqueueNotificationWithTag(PKG, PKG,
                     "testEnqueuedRestrictedNotifications_asSystem",
@@ -1056,7 +1063,7 @@
         int id = 0;
         for (String category: categories) {
             final StatusBarNotification sbn =
-                    generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
+                    generateNotificationRecord(mTestNotificationChannel, ++id, "", false).getSbn();
             sbn.getNotification().category = category;
             mBinderService.enqueueNotificationWithTag(PKG, PKG,
                     "testEnqueuedRestrictedNotifications_notAutomotive",
@@ -1079,7 +1086,7 @@
                 Notification.CATEGORY_CAR_WARNING,
                 Notification.CATEGORY_CAR_INFORMATION);
         for (String category: categories) {
-            final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+            final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
             sbn.getNotification().category = category;
             try {
                 mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -1107,7 +1114,7 @@
         Bundle bundle = new Bundle();
         bundle.putInt(KEY_IMPORTANCE, IMPORTANCE_NONE);
         Adjustment adjustment = new Adjustment(
-                r.sbn.getPackageName(), r.getKey(), bundle, "", r.getUser().getIdentifier());
+                r.getSbn().getPackageName(), r.getKey(), bundle, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
 
         NotificationManagerService.PostNotificationRunnable runnable =
@@ -1142,11 +1149,11 @@
         assertNull(call.old);
         assertEquals(0, call.position);
         assertEquals(0, call.buzzBeepBlink);
-        assertEquals(PKG, call.r.sbn.getPackageName());
-        assertEquals(0, call.r.sbn.getId());
-        assertEquals(tag, call.r.sbn.getTag());
-        assertNotNull(call.r.sbn.getInstanceId());
-        assertEquals(0, call.r.sbn.getInstanceId().getId());
+        assertEquals(PKG, call.r.getSbn().getPackageName());
+        assertEquals(0, call.r.getSbn().getId());
+        assertEquals(tag, call.r.getSbn().getTag());
+        assertNotNull(call.r.getSbn().getInstanceId());
+        assertEquals(0, call.r.getSbn().getInstanceId().getId());
     }
 
     @Test
@@ -1168,14 +1175,14 @@
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
                 mNotificationRecordLogger.get(0).getUiEvent());
-        assertEquals(0, mNotificationRecordLogger.get(0).r.sbn.getInstanceId().getId());
+        assertEquals(0, mNotificationRecordLogger.get(0).r.getSbn().getInstanceId().getId());
 
         assertTrue(mNotificationRecordLogger.get(1).shouldLog());
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_UPDATED,
                 mNotificationRecordLogger.get(1).getUiEvent());
         // Instance ID doesn't change on update of an active notification
-        assertEquals(0, mNotificationRecordLogger.get(1).r.sbn.getInstanceId().getId());
+        assertEquals(0, mNotificationRecordLogger.get(1).r.getSbn().getInstanceId().getId());
     }
 
     @Test
@@ -1209,14 +1216,14 @@
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
                 mNotificationRecordLogger.get(0).getUiEvent());
-        assertEquals(0, mNotificationRecordLogger.get(0).r.sbn.getInstanceId().getId());
+        assertEquals(0, mNotificationRecordLogger.get(0).r.getSbn().getInstanceId().getId());
 
         assertTrue(mNotificationRecordLogger.get(1).shouldLog());
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
                 mNotificationRecordLogger.get(1).getUiEvent());
         // New instance ID because notification was canceled before re-post
-        assertEquals(1, mNotificationRecordLogger.get(1).r.sbn.getInstanceId().getId());
+        assertEquals(1, mNotificationRecordLogger.get(1).r.getSbn().getInstanceId().getId());
     }
 
     @Test
@@ -1257,7 +1264,7 @@
     @Test
     public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
         NotificationRecord r = generateNotificationRecord(null);
-        final StatusBarNotification sbn = r.sbn;
+        final StatusBarNotification sbn = r.getSbn();
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelNotificationsFromListenerImmediatelyAfterEnqueue",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -1271,7 +1278,7 @@
 
     @Test
     public void testCancelAllNotificationsImmediatelyAfterEnqueue() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelAllNotificationsImmediatelyAfterEnqueue",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -1286,7 +1293,7 @@
     @Test
     public void testCancelImmediatelyAfterEnqueueNotifiesListeners_ForegroundServiceFlag()
             throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags =
                 Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
@@ -1304,14 +1311,14 @@
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testUserInitiatedClearAll_noLeak",
-                n.sbn.getId(), n.sbn.getNotification(), n.sbn.getUserId());
+                n.getSbn().getId(), n.getSbn().getNotification(), n.getSbn().getUserId());
         waitForIdle();
 
         mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
                 n.getUserId());
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(n.sbn.getPackageName());
+                mBinderService.getActiveNotifications(n.getSbn().getPackageName());
         assertEquals(0, notifs.length);
         assertEquals(0, mService.getNotificationRecordCount());
         ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
@@ -1328,20 +1335,22 @@
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelAllNotificationsCancelsChildren",
-                parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
+                parent.getSbn().getId(), parent.getSbn().getNotification(),
+                parent.getSbn().getUserId());
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelAllNotificationsCancelsChildren",
-                child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
+                child.getSbn().getId(), child.getSbn().getNotification(),
+                child.getSbn().getUserId());
         waitForIdle();
 
-        mBinderService.cancelAllNotifications(PKG, parent.sbn.getUserId());
+        mBinderService.cancelAllNotifications(PKG, parent.getSbn().getUserId());
         waitForIdle();
         assertEquals(0, mService.getNotificationRecordCount());
     }
 
     @Test
     public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         for (int i = 0; i < 10; i++) {
             mBinderService.enqueueNotificationWithTag(PKG, PKG,
                     "testCancelAllNotificationsMultipleEnqueuedDoesNotCrash",
@@ -1365,20 +1374,22 @@
         // fully post parent notification
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash",
-                parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
+                parent.getSbn().getId(), parent.getSbn().getNotification(),
+                parent.getSbn().getUserId());
         waitForIdle();
 
         // enqueue the child several times
         for (int i = 0; i < 10; i++) {
             mBinderService.enqueueNotificationWithTag(PKG, PKG,
                     "testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash",
-                    child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
+                    child.getSbn().getId(), child.getSbn().getNotification(),
+                    child.getSbn().getUserId());
         }
         // make the parent a child, which will cancel the child notification
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash",
-                parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
-                parentAsChild.sbn.getUserId());
+                parentAsChild.getSbn().getId(), parentAsChild.getSbn().getNotification(),
+                parentAsChild.getSbn().getUserId());
         waitForIdle();
 
         assertEquals(0, mService.getNotificationRecordCount());
@@ -1395,7 +1406,7 @@
         mService.mAutobundledSummaries.get(0).put("pkg", summary.getKey());
         mService.updateAutobundledSummaryFlags(0, "pkg", true, false);
 
-        assertTrue(summary.sbn.isOngoing());
+        assertTrue(summary.getSbn().isOngoing());
     }
 
     @Test
@@ -1411,12 +1422,12 @@
 
         mService.updateAutobundledSummaryFlags(0, "pkg", false, false);
 
-        assertFalse(summary.sbn.isOngoing());
+        assertFalse(summary.getSbn().isOngoing());
     }
 
     @Test
     public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelAllNotifications_IgnoreForegroundService",
@@ -1431,7 +1442,7 @@
 
     @Test
     public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelAllNotifications_IgnoreOtherPackages",
@@ -1446,7 +1457,7 @@
 
     @Test
     public void testCancelAllNotifications_NullPkgRemovesAll() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelAllNotifications_NullPkgRemovesAll",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -1460,7 +1471,7 @@
 
     @Test
     public void testCancelAllNotifications_NullPkgIgnoresUserAllNotifications() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelAllNotifications_NullPkgIgnoresUserAllNotifications",
                 sbn.getId(), sbn.getNotification(), UserHandle.USER_ALL);
@@ -1475,7 +1486,7 @@
 
     @Test
     public void testAppInitiatedCancelAllNotifications_CancelsNoClearFlag() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= Notification.FLAG_NO_CLEAR;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testAppInitiatedCancelAllNotifications_CancelsNoClearFlag",
@@ -1497,7 +1508,7 @@
                 notif.getUserId(), 0, null);
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+                mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
         assertEquals(0, notifs.length);
     }
 
@@ -1512,7 +1523,7 @@
                 notif.getUserId());
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+                mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
         assertEquals(1, notifs.length);
     }
 
@@ -1534,7 +1545,7 @@
         mService.getBinderService().cancelNotificationsFromListener(null, null);
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
         assertEquals(1, notifs.length);
     }
 
@@ -1557,7 +1568,7 @@
                 parent.getUserId());
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
         assertEquals(1, notifs.length);
     }
 
@@ -1582,7 +1593,7 @@
 
     @Test
     public void testCancelAfterSecondEnqueueDoesNotSpecifyForegroundFlag() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags =
                 Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
@@ -1616,7 +1627,7 @@
         mService.getBinderService().cancelNotificationsFromListener(null, null);
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
         assertEquals(0, notifs.length);
     }
 
@@ -1636,12 +1647,12 @@
         mService.addNotification(child);
         mService.addNotification(child2);
         mService.addNotification(newGroup);
-        String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
-                child2.sbn.getKey(), newGroup.sbn.getKey()};
+        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
         mService.getBinderService().cancelNotificationsFromListener(null, keys);
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
         assertEquals(1, notifs.length);
     }
 
@@ -1664,7 +1675,7 @@
                 parent.getUserId());
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
         assertEquals(0, notifs.length);
     }
 
@@ -1680,31 +1691,33 @@
         final NotificationRecord group2 = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group2", true);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
-                group2.sbn.getId(), group2.sbn.getNotification(), group2.sbn.getUserId());
+                group2.getSbn().getId(), group2.getSbn().getNotification(),
+                group2.getSbn().getUserId());
         waitForIdle();
 
         // should not be returned
         final NotificationRecord nonGroup = generateNotificationRecord(
                 mTestNotificationChannel, 3, null, false);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
-                nonGroup.sbn.getId(), nonGroup.sbn.getNotification(), nonGroup.sbn.getUserId());
+                nonGroup.getSbn().getId(), nonGroup.getSbn().getNotification(),
+                nonGroup.getSbn().getUserId());
         waitForIdle();
 
         // same group, child, should be returned
         final NotificationRecord group1Child = generateNotificationRecord(
                 mTestNotificationChannel, 4, "group1", false);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
-                group1Child.sbn.getId(),
-                group1Child.sbn.getNotification(), group1Child.sbn.getUserId());
+                group1Child.getSbn().getId(),
+                group1Child.getSbn().getNotification(), group1Child.getSbn().getUserId());
         waitForIdle();
 
         List<NotificationRecord> inGroup1 =
                 mService.findGroupNotificationsLocked(PKG, group1.getGroupKey(),
-                        group1.sbn.getUserId());
+                        group1.getSbn().getUserId());
         assertEquals(3, inGroup1.size());
         for (NotificationRecord record : inGroup1) {
             assertTrue(record.getGroupKey().equals(group1.getGroupKey()));
-            assertTrue(record.sbn.getId() == 1 || record.sbn.getId() == 4);
+            assertTrue(record.getSbn().getId() == 1 || record.getSbn().getId() == 4);
         }
     }
 
@@ -1718,7 +1731,7 @@
                 Notification.FLAG_ONGOING_EVENT, true, notif.getUserId(), 0, null);
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+                mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
         assertEquals(0, notifs.length);
     }
 
@@ -1738,18 +1751,18 @@
         mService.addNotification(child);
         mService.addNotification(child2);
         mService.addNotification(newGroup);
-        String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
-                child2.sbn.getKey(), newGroup.sbn.getKey()};
+        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
         mService.getBinderService().cancelNotificationsFromListener(null, keys);
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
         assertEquals(0, notifs.length);
     }
 
     @Test
     public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag",
@@ -1771,7 +1784,7 @@
                 notif.getUserId(), 0, null);
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+                mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
         assertEquals(0, notifs.length);
     }
 
@@ -1786,7 +1799,7 @@
                 notif.getUserId());
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(notif.sbn.getPackageName());
+                mBinderService.getActiveNotifications(notif.getSbn().getPackageName());
         assertEquals(1, notifs.length);
     }
 
@@ -1808,7 +1821,7 @@
         mService.getBinderService().cancelNotificationsFromListener(null, null);
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
         assertEquals(1, notifs.length);
     }
 
@@ -1828,12 +1841,12 @@
         mService.addNotification(child);
         mService.addNotification(child2);
         mService.addNotification(newGroup);
-        String[] keys = {parent.sbn.getKey(), child.sbn.getKey(),
-                child2.sbn.getKey(), newGroup.sbn.getKey()};
+        String[] keys = {parent.getSbn().getKey(), child.getSbn().getKey(),
+                child2.getSbn().getKey(), newGroup.getSbn().getKey()};
         mService.getBinderService().cancelNotificationsFromListener(null, keys);
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
         assertEquals(0, notifs.length);
     }
 
@@ -1856,7 +1869,7 @@
                 parent.getUserId());
         waitForIdle();
         StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(parent.sbn.getPackageName());
+                mBinderService.getActiveNotifications(parent.getSbn().getPackageName());
         assertEquals(1, notifs.length);
     }
 
@@ -1990,7 +2003,8 @@
     public void testUpdateGroupNotifyCreatorBlock() throws Exception {
         NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
         mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+        when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()),
+                eq(PKG), anyInt()))
                 .thenReturn(existing);
 
         NotificationChannelGroup updated = new NotificationChannelGroup("id", "name");
@@ -2013,7 +2027,8 @@
         NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
         existing.setBlocked(true);
         mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+        when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()),
+                eq(PKG), anyInt()))
                 .thenReturn(existing);
 
         mBinderService.updateNotificationChannelGroupForPackage(
@@ -2033,7 +2048,8 @@
     public void testUpdateGroupNoNotifyCreatorOtherChanges() throws Exception {
         NotificationChannelGroup existing = new NotificationChannelGroup("id", "name");
         mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getNotificationChannelGroup(eq(existing.getId()), eq(PKG), anyInt()))
+        when(mPreferencesHelper.getNotificationChannelGroup(
+                eq(existing.getId()), eq(PKG), anyInt()))
                 .thenReturn(existing);
 
         mBinderService.updateNotificationChannelGroupForPackage(
@@ -2492,7 +2508,7 @@
     public void testSnoozeRunnable_snoozeGroupChild_onlyChildOfSummary() throws Exception {
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
-        assertTrue(parent.sbn.getNotification().isGroupSummary());
+        assertTrue(parent.getSbn().getNotification().isGroupSummary());
         final NotificationRecord child = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group", false);
         mService.addNotification(parent);
@@ -2528,7 +2544,8 @@
                 mTestNotificationChannel, 2, "group", false);
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "testPostNonGroup_noUnsnoozing",
-                child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
+                child.getSbn().getId(), child.getSbn().getNotification(),
+                child.getSbn().getUserId());
         waitForIdle();
 
         verify(mSnoozeHelper, times(1)).repostGroupSummary(
@@ -2541,7 +2558,8 @@
                 mTestNotificationChannel, 2, null, false);
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "testPostNonGroup_noUnsnoozing",
-                record.sbn.getId(), record.sbn.getNotification(), record.sbn.getUserId());
+                record.getSbn().getId(), record.getSbn().getNotification(),
+                record.getSbn().getUserId());
         waitForIdle();
 
         verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString());
@@ -2553,7 +2571,8 @@
                 mTestNotificationChannel, 2, "group", true);
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "testPostGroupSummary_noUnsnoozing",
-                parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
+                parent.getSbn().getId(), parent.getSbn().getNotification(),
+                parent.getSbn().getUserId());
         waitForIdle();
 
         verify(mSnoozeHelper, never()).repostGroupSummary(anyString(), anyInt(), anyString());
@@ -2972,11 +2991,11 @@
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         NotificationRecord posted = mService.findNotificationLocked(
-                PKG, nr.sbn.getTag(), nr.sbn.getId(), nr.sbn.getUserId());
+                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
 
         assertFalse(posted.getNotification().isColorized());
     }
@@ -2995,7 +3014,7 @@
             NotificationRecord r =
                     generateNotificationRecord(mTestNotificationChannel, i, null, false);
             mService.addNotification(r);
-            sampleTagToExclude = r.sbn.getTag();
+            sampleTagToExclude = r.getSbn().getTag();
             sampleIdToExclude = i;
         }
 
@@ -3231,7 +3250,8 @@
 
         StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
                 "testBumpFGImportance_noChannelChangePreOApp",
-                Binder.getCallingUid(), 0, nb.build(), new UserHandle(Binder.getCallingUid()), null, 0);
+                Binder.getCallingUid(), 0, nb.build(), new UserHandle(Binder.getCallingUid()), null,
+                0);
 
         mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), sbn.getOpPkg(),
                 sbn.getTag(), sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -3269,7 +3289,7 @@
 
         mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
-        verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.sbn));
+        verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.getSbn()));
     }
 
     @Test
@@ -3279,12 +3299,14 @@
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true,
                 NOTIFICATION_LOCATION_UNKNOWN);
-        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((true)));
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
+                eq((true)));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false,
                 NOTIFICATION_LOCATION_UNKNOWN);
-        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((false)));
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
+                eq((false)));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
     }
 
@@ -3296,13 +3318,14 @@
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true,
                 NOTIFICATION_LOCATION_UNKNOWN);
         assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
-        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(false), eq((true)));
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(false),
+                eq((true)));
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false,
                 NOTIFICATION_LOCATION_UNKNOWN);
         assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
         verify(mAssistants).notifyAssistantExpansionChangedLocked(
-                eq(r.sbn), eq(false), eq((false)));
+                eq(r.getSbn()), eq(false), eq((false)));
     }
 
     @Test
@@ -3322,11 +3345,11 @@
         final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, 2, true);
         mService.mNotificationDelegate.onNotificationVisibilityChanged(
                 new NotificationVisibility[] {nv}, new NotificationVisibility[]{});
-        verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.sbn), eq(true));
+        verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.getSbn()), eq(true));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
         mService.mNotificationDelegate.onNotificationVisibilityChanged(
                 new NotificationVisibility[] {}, new NotificationVisibility[]{nv});
-        verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.sbn), eq(false));
+        verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.getSbn()), eq(false));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
     }
 
@@ -3336,8 +3359,8 @@
         mService.addNotification(r);
 
         final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
-        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.sbn.getTag(),
-                r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
+        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
                 NotificationStats.DISMISS_SENTIMENT_POSITIVE, nv);
         waitForIdle();
 
@@ -3350,8 +3373,8 @@
         mService.addNotification(r);
 
         final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 0, 1, true);
-        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.sbn.getTag(),
-                r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
+        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD,
                 NotificationStats.DISMISS_SENTIMENT_NEGATIVE, nv);
         waitForIdle();
 
@@ -3373,7 +3396,7 @@
         signals.putInt(Adjustment.KEY_USER_SENTIMENT,
                 USER_SENTIMENT_NEGATIVE);
         Adjustment adjustment = new Adjustment(
-                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+                r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyAdjustmentFromAssistant(null, adjustment);
 
         waitForIdle();
@@ -3392,7 +3415,7 @@
         Bundle signals = new Bundle();
         signals.putInt(KEY_IMPORTANCE, IMPORTANCE_NONE);
         Adjustment adjustment = new Adjustment(
-                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+                r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         mBinderService.applyAdjustmentFromAssistant(null, adjustment);
 
@@ -3415,7 +3438,7 @@
         signals.putInt(Adjustment.KEY_USER_SENTIMENT,
                 USER_SENTIMENT_NEGATIVE);
         Adjustment adjustment = new Adjustment(
-                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+                r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
 
         assertEquals(USER_SENTIMENT_NEGATIVE, r.getUserSentiment());
@@ -3433,7 +3456,7 @@
         Bundle signals = new Bundle();
         signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
         Adjustment adjustment = new Adjustment(
-                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+                r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
 
         assertEquals(IMPORTANCE_LOW, r.getImportance());
@@ -3452,7 +3475,7 @@
         signals.putInt(Adjustment.KEY_USER_SENTIMENT,
                 USER_SENTIMENT_NEGATIVE);
         Adjustment adjustment = new Adjustment(
-                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+                r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
 
         assertEquals(USER_SENTIMENT_NEUTRAL, r.getUserSentiment());
@@ -3472,7 +3495,7 @@
         signals.putInt(Adjustment.KEY_USER_SENTIMENT,
                 USER_SENTIMENT_NEGATIVE);
         Adjustment adjustment = new Adjustment(
-                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+                r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyAdjustmentFromAssistant(null, adjustment);
 
         waitForIdle();
@@ -3490,7 +3513,7 @@
         signals.putInt(Adjustment.KEY_USER_SENTIMENT,
                 USER_SENTIMENT_NEGATIVE);
         Adjustment adjustment = new Adjustment(
-                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+                r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
 
         waitForIdle();
@@ -3508,7 +3531,7 @@
         signals.putInt(Adjustment.KEY_USER_SENTIMENT,
                 USER_SENTIMENT_NEGATIVE);
         Adjustment adjustment = new Adjustment(
-                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+                r.getSbn().getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
 
         assertEquals(USER_SENTIMENT_NEGATIVE, r.getUserSentiment());
@@ -3622,6 +3645,33 @@
     }
 
     @Test
+    public void updateUriPermissions_posterDoesNotOwnUri() throws Exception {
+        NotificationChannel c = new NotificationChannel(
+                TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
+        c.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
+        Message message1 = new Message("", 0, "");
+        message1.setData("",
+                ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1));
+
+        Notification.Builder nbA = new Notification.Builder(mContext, c.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setStyle(new Notification.MessagingStyle("")
+                        .addMessage(message1));
+        NotificationRecord recordA = new NotificationRecord(mContext, new StatusBarNotification(
+                PKG, PKG, 0, "tag", mUid, 0, nbA.build(), new UserHandle(mUid), null, 0), c);
+
+        doThrow(new SecurityException("no access")).when(mUgm)
+                .grantUriPermissionFromOwner(
+                        any(), anyInt(), any(), any(), anyInt(), anyInt(), anyInt());
+
+        when(mUgmInternal.newUriPermissionOwner(any())).thenReturn(new Binder());
+        mService.updateUriPermissions(recordA, null, mContext.getPackageName(),  USER_SYSTEM);
+
+        // yay, no crash
+    }
+
+    @Test
     public void testVisitUris() throws Exception {
         final Uri audioContents = Uri.parse("content://com.example/audio");
         final Uri backgroundImage = Uri.parse("content://com.example/background");
@@ -4078,7 +4128,7 @@
         ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class);
         verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
         assertEquals(1, captorHide.getValue().size());
-        assertEquals("a", captorHide.getValue().get(0).sbn.getPackageName());
+        assertEquals("a", captorHide.getValue().get(0).getSbn().getPackageName());
 
         // on broadcast, unhide the package
         mService.simulatePackageDistractionBroadcast(
@@ -4086,7 +4136,7 @@
         ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class);
         verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
         assertEquals(1, captorUnhide.getValue().size());
-        assertEquals("a", captorUnhide.getValue().get(0).sbn.getPackageName());
+        assertEquals("a", captorUnhide.getValue().get(0).getSbn().getPackageName());
     }
 
     @Test
@@ -4107,8 +4157,8 @@
         // should be called only once.
         verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
         assertEquals(2, captorHide.getValue().size());
-        assertEquals("a", captorHide.getValue().get(0).sbn.getPackageName());
-        assertEquals("b", captorHide.getValue().get(1).sbn.getPackageName());
+        assertEquals("a", captorHide.getValue().get(0).getSbn().getPackageName());
+        assertEquals("b", captorHide.getValue().get(1).getSbn().getPackageName());
 
         // on broadcast, unhide the package
         mService.simulatePackageDistractionBroadcast(
@@ -4118,8 +4168,8 @@
         // should be called only once.
         verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
         assertEquals(2, captorUnhide.getValue().size());
-        assertEquals("a", captorUnhide.getValue().get(0).sbn.getPackageName());
-        assertEquals("b", captorUnhide.getValue().get(1).sbn.getPackageName());
+        assertEquals("a", captorUnhide.getValue().get(0).getSbn().getPackageName());
+        assertEquals("b", captorUnhide.getValue().get(1).getSbn().getPackageName());
     }
 
     @Test
@@ -4405,7 +4455,7 @@
         ai.uid = -1;
         when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
 
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         try {
             mInternalService.enqueueNotification(notReal, "android", 0, 0,
                     "testPostFromAndroidForNonExistentPackage",
@@ -4426,7 +4476,7 @@
         when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
 
         // unlike the post case, ignore instead of throwing
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
 
         mInternalService.cancelNotification(notReal, "android", 0, 0, "tag",
                 sbn.getId(), sbn.getUserId());
@@ -4457,7 +4507,7 @@
         mService.addEnqueuedNotification(r);
 
         mInternalService.removeForegroundServiceFlagFromNotification(
-                PKG, r.sbn.getId(), r.sbn.getUserId());
+                PKG, r.getSbn().getId(), r.getSbn().getUserId());
 
         waitForIdle();
 
@@ -4476,7 +4526,7 @@
         mService.addNotification(r);
 
         mInternalService.removeForegroundServiceFlagFromNotification(
-                PKG, r.sbn.getId(), r.sbn.getUserId());
+                PKG, r.getSbn().getId(), r.getSbn().getUserId());
 
         waitForIdle();
 
@@ -4674,7 +4724,7 @@
                 r.getKey(), replyIndex, reply, NOTIFICATION_LOCATION_UNKNOWN,
                 modifiedBeforeSending);
         verify(mAssistants).notifyAssistantSuggestedReplySent(
-                eq(r.sbn), eq(reply), eq(generatedByAssistant));
+                eq(r.getSbn()), eq(reply), eq(generatedByAssistant));
     }
 
     @Test
@@ -4693,7 +4743,7 @@
                 10, 10, r.getKey(), actionIndex, action, notificationVisibility,
                 generatedByAssistant);
         verify(mAssistants).notifyAssistantActionClicked(
-                eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant));
+                eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant));
     }
 
     @Test
@@ -4766,13 +4816,13 @@
         NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, r.sbn.getId(),
-                r.sbn.getTag(), mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, r.getSbn().getId(),
+                r.getSbn().getTag(), mUid, 0,
                 new Notification.Builder(mContext, mTestNotificationChannel.getId()).build(),
                 new UserHandle(mUid), null, 0);
         NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
         mService.addEnqueuedNotification(update);
-        assertNull(update.sbn.getNotification().getSmallIcon());
+        assertNull(update.getSbn().getNotification().getSmallIcon());
 
         NotificationManagerService.PostNotificationRunnable runnable =
                 mService.new PostNotificationRunnable(update.getKey());
@@ -4944,15 +4994,15 @@
         NotificationRecord nr =
                 generateMessageBubbleNotifRecord(mTestNotificationChannel, "testFlagBubble");
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifs.length);
         assertTrue((notifs[0].getNotification().flags & FLAG_BUBBLE) != 0);
         assertTrue(mService.getNotificationRecord(
-                nr.sbn.getKey()).getNotification().isBubbleNotification());
+                nr.getSbn().getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -4963,15 +5013,15 @@
         NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
                         "testFlagBubble_noFlag_appNotAllowed");
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifs.length);
         assertEquals((notifs[0].getNotification().flags & FLAG_BUBBLE), 0);
         assertFalse(mService.getNotificationRecord(
-                nr.sbn.getKey()).getNotification().isBubbleNotification());
+                nr.getSbn().getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -4990,16 +5040,16 @@
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         // Say we're foreground
-        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
+        when(mActivityManager.getPackageImportance(nr.getSbn().getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // if notif isn't configured properly it doesn't get to bubble just because app is
         // foreground.
         assertFalse(mService.getNotificationRecord(
-                nr.sbn.getKey()).getNotification().isBubbleNotification());
+                nr.getSbn().getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -5010,13 +5060,13 @@
         NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
                 "testFlagBubbleNotifs_flag_messaging");
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // yes allowed, yes messaging, yes bubble
         assertTrue(mService.getNotificationRecord(
-                nr.sbn.getKey()).getNotification().isBubbleNotification());
+                nr.getSbn().getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -5046,8 +5096,8 @@
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // yes phone call, yes person, yes foreground service, yes bubble
@@ -5079,8 +5129,8 @@
                 nb.build(), new UserHandle(mUid), null, 0);
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // yes phone call, yes person, NO foreground service, no bubble
@@ -5110,8 +5160,8 @@
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // yes phone call, yes foreground service, BUT NO person, no bubble
@@ -5145,8 +5195,8 @@
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // yes person, yes foreground service, BUT NO call, no bubble
@@ -5163,13 +5213,13 @@
                 "testFlagBubbleNotifs_noFlag_messaging_appNotAllowed");
 
         // Post the notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // not allowed, no bubble
         assertFalse(mService.getNotificationRecord(
-                nr.sbn.getKey()).getNotification().isBubbleNotification());
+                nr.getSbn().getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -5187,13 +5237,13 @@
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         // Post the notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // no bubble metadata, no bubble
         assertFalse(mService.getNotificationRecord(
-                nr.sbn.getKey()).getNotification().isBubbleNotification());
+                nr.getSbn().getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -5205,13 +5255,13 @@
                 "testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed");
 
         // Post the notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // channel not allowed, no bubble
         assertFalse(mService.getNotificationRecord(
-                nr.sbn.getKey()).getNotification().isBubbleNotification());
+                nr.getSbn().getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -5277,7 +5327,7 @@
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // yes phone call, yes person, yes foreground service, but channel not allowed, no bubble
@@ -5288,10 +5338,10 @@
     @Test
     public void testCancelAllNotifications_cancelsBubble() throws Exception {
         final NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
-        nr.sbn.getNotification().flags |= FLAG_BUBBLE;
+        nr.getSbn().getNotification().flags |= FLAG_BUBBLE;
         mService.addNotification(nr);
 
-        mBinderService.cancelAllNotifications(PKG, nr.sbn.getUserId());
+        mBinderService.cancelAllNotifications(PKG, nr.getSbn().getUserId());
         waitForIdle();
 
         StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
@@ -5302,12 +5352,13 @@
     @Test
     public void testAppCancelNotifications_cancelsBubbles() throws Exception {
         final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
-        nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;
+        nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
 
         // Post the notification
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testAppCancelNotifications_cancelsBubbles",
-                nrBubble.sbn.getId(), nrBubble.sbn.getNotification(), nrBubble.sbn.getUserId());
+                nrBubble.getSbn().getId(), nrBubble.getSbn().getNotification(),
+                nrBubble.getSbn().getUserId());
         waitForIdle();
 
         StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
@@ -5315,8 +5366,8 @@
         assertEquals(1, mService.getNotificationRecordCount());
 
         mBinderService.cancelNotificationWithTag(PKG, PKG,
-                "testAppCancelNotifications_cancelsBubbles", nrBubble.sbn.getId(),
-                nrBubble.sbn.getUserId());
+                "testAppCancelNotifications_cancelsBubbles", nrBubble.getSbn().getId(),
+                nrBubble.getSbn().getUserId());
         waitForIdle();
 
         StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG);
@@ -5328,7 +5379,7 @@
     public void testCancelAllNotificationsFromListener_ignoresBubbles() throws Exception {
         final NotificationRecord nrNormal = generateNotificationRecord(mTestNotificationChannel);
         final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
-        nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;
+        nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
 
         mService.addNotification(nrNormal);
         mService.addNotification(nrBubble);
@@ -5345,12 +5396,12 @@
     public void testCancelNotificationsFromListener_ignoresBubbles() throws Exception {
         final NotificationRecord nrNormal = generateNotificationRecord(mTestNotificationChannel);
         final NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel);
-        nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;
+        nrBubble.getSbn().getNotification().flags |= FLAG_BUBBLE;
 
         mService.addNotification(nrNormal);
         mService.addNotification(nrBubble);
 
-        String[] keys = {nrNormal.sbn.getKey(), nrBubble.sbn.getKey()};
+        String[] keys = {nrNormal.getSbn().getKey(), nrBubble.getSbn().getKey()};
         mService.getBinderService().cancelNotificationsFromListener(null, keys);
         waitForIdle();
 
@@ -5386,7 +5437,7 @@
         Bundle signals = new Bundle();
         signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
         signals.putInt(KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
-        Adjustment adjustment = new Adjustment(r.sbn.getPackageName(), r.getKey(), signals,
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals,
                "", r.getUser().getIdentifier());
 
         mBinderService.applyAdjustmentFromAssistant(null, adjustment);
@@ -5469,8 +5520,8 @@
         NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
                 "testNotificationBubbleChanged_false");
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // Reset as this is called when the notif is first sent
@@ -5499,8 +5550,8 @@
         // Notif that is not a bubble
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
                 1, null, false);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // Would be a normal notification because wouldn't have met requirements to bubble
@@ -5510,9 +5561,9 @@
 
         // Update the notification to be message style / meet bubble requirements
         NotificationRecord nr2 = generateMessageBubbleNotifRecord(mTestNotificationChannel,
-                nr.sbn.getTag());
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
-                nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
+                nr.getSbn().getTag());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.getSbn().getTag(),
+                nr2.getSbn().getId(), nr2.getSbn().getNotification(), nr2.getSbn().getUserId());
         waitForIdle();
 
         // Reset as this is called when the notif is first sent
@@ -5535,8 +5586,8 @@
 
         // Notif that is not a bubble
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // Reset as this is called when the notif is first sent
@@ -5563,11 +5614,11 @@
         NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, "tag");
 
         // Bubbles are allowed!
-        setUpPrefsForBubbles(PKG, nr.sbn.getUserId(), true /* global */,
+        setUpPrefsForBubbles(PKG, nr.getSbn().getUserId(), true /* global */,
                 true /* app */, true /* channel */);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // NOT suppressed
@@ -5601,7 +5652,7 @@
     public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // A notification exists for the given record
@@ -5613,12 +5664,13 @@
         Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
 
         mService.mNotificationDelegate.grantInlineReplyUriPermission(
-                nr.getKey(), uri, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+                nr.getKey(), uri, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+                nr.getSbn().getUid());
 
         // Grant permission called for the UID of SystemUI under the target user ID
         verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
-                eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
-                eq(nr.sbn.getUserId()));
+                eq(nr.getSbn().getUid()), eq(nr.getSbn().getPackageName()), eq(uri), anyInt(),
+                anyInt(), eq(nr.getSbn().getUserId()));
     }
 
     @Test
@@ -5634,12 +5686,13 @@
         int uid = 0; // sysui on primary user
 
         mService.mNotificationDelegate.grantInlineReplyUriPermission(
-                nr.getKey(), uri, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+                nr.getKey(), uri, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+                nr.getSbn().getUid());
 
         // Grant permission still called if no NotificationRecord exists for the given key
         verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
-                eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
-                eq(nr.sbn.getUserId()));
+                eq(nr.getSbn().getUid()), eq(nr.getSbn().getPackageName()), eq(uri), anyInt(),
+                anyInt(), eq(nr.getSbn().getUserId()));
     }
 
     @Test
@@ -5648,7 +5701,7 @@
         NotificationRecord nr =
                 generateNotificationRecord(mTestNotificationChannel, UserHandle.USER_ALL);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // A notification exists for the given record
@@ -5660,12 +5713,13 @@
         Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
 
         mService.mNotificationDelegate.grantInlineReplyUriPermission(
-                nr.getKey(), uri, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+                nr.getKey(), uri, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+                nr.getSbn().getUid());
 
         // Target user for the grant is USER_ALL instead of USER_SYSTEM
         verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
-                eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
-                eq(UserHandle.USER_SYSTEM));
+                eq(nr.getSbn().getUid()), eq(nr.getSbn().getPackageName()), eq(uri), anyInt(),
+                anyInt(), eq(UserHandle.USER_SYSTEM));
     }
 
     @Test
@@ -5675,7 +5729,7 @@
         NotificationRecord nr =
                 generateNotificationRecord(mTestNotificationChannel, otherUserId);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // A notification exists for the given record
@@ -5698,11 +5752,11 @@
                 .thenReturn(otherUserUid);
 
         mService.mNotificationDelegate.grantInlineReplyUriPermission(
-                nr.getKey(), uri, nr.sbn.getUser(), nr.sbn.getPackageName(), uid);
+                nr.getKey(), uri, nr.getSbn().getUser(), nr.getSbn().getPackageName(), uid);
 
         // Target user for the grant is USER_ALL instead of USER_SYSTEM
         verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
-                eq(otherUserUid), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+                eq(otherUserUid), eq(nr.getSbn().getPackageName()), eq(uri), anyInt(), anyInt(),
                 eq(otherUserId));
     }
 
@@ -5716,15 +5770,18 @@
 
         // create an inline record with two uris in it
         mService.mNotificationDelegate.grantInlineReplyUriPermission(
-                nr.getKey(), uri1, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+                nr.getKey(), uri1, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+                nr.getSbn().getUid());
         mService.mNotificationDelegate.grantInlineReplyUriPermission(
-                nr.getKey(), uri2, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+                nr.getKey(), uri2, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+                nr.getSbn().getUid());
 
         InlineReplyUriRecord record = mService.mInlineReplyRecordsByKey.get(nr.getKey());
         assertNotNull(record); // record exists
         assertEquals(record.getUris().size(), 2); // record has two uris in it
 
-        mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(), nr.sbn.getUid());
+        mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(),
+                nr.getSbn().getUid());
 
         // permissionOwner destroyed
         verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(
@@ -5737,7 +5794,8 @@
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
         reset(mPackageManager);
 
-        mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(), nr.sbn.getUid());
+        mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(),
+                nr.getSbn().getUid());
 
         // no permissionOwner destroyed
         verify(mUgmInternal, times(0)).revokeUriPermissionFromOwner(
@@ -5755,12 +5813,14 @@
 
         // create an inline record a uri in it
         mService.mNotificationDelegate.grantInlineReplyUriPermission(
-                nr.getKey(), uri1, nr.sbn.getUser(), nr.sbn.getPackageName(), nr.sbn.getUid());
+                nr.getKey(), uri1, nr.getSbn().getUser(), nr.getSbn().getPackageName(),
+                nr.getSbn().getUid());
 
         InlineReplyUriRecord record = mService.mInlineReplyRecordsByKey.get(nr.getKey());
         assertNotNull(record); // record exists
 
-        mService.mNotificationDelegate.clearInlineReplyUriPermissions(nr.getKey(), nr.sbn.getUid());
+        mService.mNotificationDelegate.clearInlineReplyUriPermissions(
+                nr.getKey(), nr.getSbn().getUid());
 
         // permissionOwner destroyed for USER_SYSTEM, not USER_ALL
         verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(
@@ -5778,8 +5838,8 @@
         // Notification that would typically bubble
         NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
                 "testNotificationBubbles_disabled_lowRamDevice");
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // But we wouldn't be a bubble because the device is low ram & all bubbles are disabled.
@@ -5859,20 +5919,20 @@
         NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
                 "testNotificationBubbles_flagAutoExpandForeground_fails_notForeground");
         // Modify metadata flags
-        nr.sbn.getNotification().getBubbleMetadata().setFlags(
+        nr.getSbn().getNotification().getBubbleMetadata().setFlags(
                 Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
                         | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
 
         // Ensure we're not foreground
-        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
+        when(mActivityManager.getPackageImportance(nr.getSbn().getPackageName())).thenReturn(
                 IMPORTANCE_VISIBLE);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // yes allowed, yes messaging, yes bubble
-        Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
+        Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
         assertTrue(notif.isBubbleNotification());
 
         // Our flags should have failed since we're not foreground
@@ -5889,20 +5949,20 @@
         NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
                 "testNotificationBubbles_flagAutoExpandForeground_succeeds_foreground");
         // Modify metadata flags
-        nr.sbn.getNotification().getBubbleMetadata().setFlags(
+        nr.getSbn().getNotification().getBubbleMetadata().setFlags(
                 Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
                         | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
 
         // Ensure we are in the foreground
-        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
+        when(mActivityManager.getPackageImportance(nr.getSbn().getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // yes allowed, yes messaging, yes bubble
-        Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
+        Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
         assertTrue(notif.isBubbleNotification());
 
         // Our flags should have passed since we are foreground
@@ -5935,8 +5995,8 @@
         when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
 
         // Test: Send the bubble notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         // Verify:
@@ -5945,7 +6005,7 @@
         verify(mLauncherApps, times(1)).registerCallback(launcherAppsCallback.capture(), any());
 
         // yes allowed, yes messaging w/shortcut, yes bubble
-        Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
+        Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
         assertTrue(notif.isBubbleNotification());
 
         // Test: Remove the shortcut
@@ -5958,7 +6018,7 @@
         verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
 
         // We're no longer a bubble
-        Notification notif2 = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
+        Notification notif2 = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
         assertFalse(notif2.isBubbleNotification());
     }
 
@@ -5974,8 +6034,9 @@
         // Dismiss summary
         final NotificationVisibility nv = NotificationVisibility.obtain(nrSummary.getKey(), 1, 2,
                 true);
-        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, nrSummary.sbn.getTag(),
-                nrSummary.sbn.getId(), nrSummary.getUserId(), nrSummary.getKey(),
+        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG,
+                nrSummary.getSbn().getTag(),
+                nrSummary.getSbn().getId(), nrSummary.getUserId(), nrSummary.getKey(),
                 NotificationStats.DISMISSAL_SHADE,
                 NotificationStats.DISMISS_SENTIMENT_NEUTRAL, nv);
         waitForIdle();
@@ -6085,8 +6146,8 @@
     public void testNotificationHistory_addNoisyNotification() throws Exception {
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
                 null /* tvExtender */);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
         waitForIdle();
 
         verify(mHistoryManager, times(1)).addNotification(any());
@@ -6168,4 +6229,48 @@
         assertNull(mBinderService.getConversationNotificationChannel(
                 PKG, 0, PKG, callsParent.getId(), false, conversationId));
     }
+
+    @Test
+    public void testCorrectCategory_systemOn_appCannotTurnOff() {
+        int requested = 0;
+        int system = PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS;
+
+        int actual = mService.correctCategory(requested, PRIORITY_CATEGORY_CONVERSATIONS,
+                system);
+
+        assertEquals(PRIORITY_CATEGORY_CONVERSATIONS, actual);
+    }
+
+    @Test
+    public void testCorrectCategory_systemOff_appTurnOff_noChanges() {
+        int requested = PRIORITY_CATEGORY_CALLS;
+        int system = PRIORITY_CATEGORY_CALLS;
+
+        int actual = mService.correctCategory(requested, PRIORITY_CATEGORY_CONVERSATIONS,
+                system);
+
+        assertEquals(PRIORITY_CATEGORY_CALLS, actual);
+    }
+
+    @Test
+    public void testCorrectCategory_systemOn_appTurnOn_noChanges() {
+        int requested = PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS;
+        int system = PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS;
+
+        int actual = mService.correctCategory(requested, PRIORITY_CATEGORY_CONVERSATIONS,
+                system);
+
+        assertEquals(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS, actual);
+    }
+
+    @Test
+    public void testCorrectCategory_systemOff_appCannotTurnOn() {
+        int requested = PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS;
+        int system = PRIORITY_CATEGORY_CALLS;
+
+        int actual = mService.correctCategory(requested, PRIORITY_CATEGORY_CONVERSATIONS,
+                system);
+
+        assertEquals(PRIORITY_CATEGORY_CALLS, actual);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index c1c74da..bb84b04 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -171,7 +171,7 @@
                 .thenReturn(SOUND_URI);
 
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
-                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
         resetZenModeHelper();
@@ -1430,7 +1430,7 @@
         // start notification policy off with mAreChannelsBypassingDnd = true, but
         // RankingHelper should change to false
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
-                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
         assertFalse(mHelper.areChannelsBypassingDnd());
@@ -1441,7 +1441,7 @@
     @Test
     public void testSetupNewZenModeHelper_cannotBypass() {
         // start notification policy off with mAreChannelsBypassingDnd = false
-        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0);
+        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
         assertFalse(mHelper.areChannelsBypassingDnd());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 8774b63..5018166 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -140,7 +140,7 @@
                 .thenReturn(SOUND_URI);
 
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
-                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND);
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
         when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
         mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper,
                 mUsageStats, new String[] {ImportanceExtractor.class.getName()});
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 5841e59..3186d53 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -169,11 +169,11 @@
     public void testCleanupContextShouldRemovePersistedRecord() {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         mSnoozeHelper.snooze(r, "context");
-        mSnoozeHelper.cleanupPersistedContext(r.sbn.getKey());
+        mSnoozeHelper.cleanupPersistedContext(r.getSbn().getKey());
         assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
                 r.getUser().getIdentifier(),
-                r.sbn.getPackageName(),
-                r.sbn.getKey()
+                r.getSbn().getPackageName(),
+                r.getSbn().getKey()
         ));
     }
 
@@ -201,7 +201,7 @@
         long actualSnoozedUntilDuration = captor.getValue() - SystemClock.elapsedRealtime();
         assertTrue(Math.abs(actualSnoozedUntilDuration - 1000) < 250);
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
     }
 
     @Test
@@ -211,7 +211,7 @@
         verify(mAm, never()).setExactAndAllowWhileIdle(
                 anyInt(), anyLong(), any(PendingIntent.class));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
     }
 
     @Test
@@ -221,17 +221,17 @@
         mSnoozeHelper.snooze(r, 1000);
         mSnoozeHelper.snooze(r2 , 1000);
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
 
-        mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.sbn.getPackageName(), "one", 1);
+        mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), "one", 1);
         // 2 = one for each snooze, above, zero for the cancel.
         verify(mAm, times(2)).cancel(any(PendingIntent.class));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
     }
 
     @Test
@@ -243,21 +243,21 @@
         mSnoozeHelper.snooze(r2, 1000);
         mSnoozeHelper.snooze(r3, 1000);
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_ALL, r3.sbn.getPackageName(), r3.getKey()));
+                UserHandle.USER_ALL, r3.getSbn().getPackageName(), r3.getKey()));
 
         mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, false);
         // 3 = once for each snooze above (3), only.
         verify(mAm, times(3)).cancel(any(PendingIntent.class));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_ALL, r3.sbn.getPackageName(), r3.getKey()));
+                UserHandle.USER_ALL, r3.getSbn().getPackageName(), r3.getKey()));
     }
 
     @Test
@@ -269,21 +269,21 @@
         mSnoozeHelper.snooze(r2, 1000);
         mSnoozeHelper.snooze(r3, 1000);
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+                UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
 
         mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, "pkg2");
         // 3 = once for each snooze above (3), only.
         verify(mAm, times(3)).cancel(any(PendingIntent.class));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+                UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
     }
 
     @Test
@@ -291,12 +291,12 @@
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         mSnoozeHelper.snooze(r, 1000);
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
 
-        mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.sbn.getPackageName(), "one", 1);
+        mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), "one", 1);
 
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
     }
 
     @Test
@@ -306,11 +306,11 @@
         mSnoozeHelper.snooze(r, 1000);
         mSnoozeHelper.snooze(r2 , 1000);
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
 
-        mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.sbn.getPackageName(), "one", 1);
+        mSnoozeHelper.cancel(UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), "one", 1);
 
         mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
         verify(mCallback, never()).repost(UserHandle.USER_SYSTEM, r);
@@ -507,18 +507,18 @@
         mSnoozeHelper.snooze(r, 1000);
         mSnoozeHelper.snooze(r2, 1000);
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
 
         // clear data
         mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
 
         // nothing snoozed; alarms canceled
         assertFalse(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertFalse(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey()));
         // twice for initial snooze, twice for canceling the snooze
         verify(mAm, times(4)).cancel(any(PendingIntent.class));
     }
@@ -533,21 +533,21 @@
         mSnoozeHelper.snooze(r2, 1000);
         mSnoozeHelper.snooze(r3, 1000);
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+                UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
 
         // clear data
         mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
 
         assertFalse(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+                UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+                UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey()));
         assertTrue(mSnoozeHelper.isSnoozed(
-                UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+                UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey()));
         // once for each initial snooze, once for canceling one snooze
         verify(mAm, times(4)).cancel(any(PendingIntent.class));
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index b654764..f7b435e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -91,6 +91,7 @@
         int priorityCategories = originalPolicy.priorityCategories;
         int priorityCallSenders = originalPolicy.priorityCallSenders;
         int priorityMessageSenders = originalPolicy.priorityMessageSenders;
+        int priorityConversationsSenders = originalPolicy.priorityConversationSenders;
         int suppressedVisualEffects = originalPolicy.suppressedVisualEffects;
         priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
         priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
@@ -99,7 +100,7 @@
         suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
 
         Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders,
-                priorityMessageSenders, suppressedVisualEffects, 0);
+                priorityMessageSenders, suppressedVisualEffects, 0, priorityConversationsSenders);
 
         assertEquals(expectedPolicy, config.toNotificationPolicy(zenPolicy));
     }
@@ -235,6 +236,8 @@
         config.areChannelsBypassingDnd = false;
         config.allowCallsFrom = ZenModeConfig.SOURCE_ANYONE;
         config.allowMessagesFrom = ZenModeConfig.SOURCE_ANYONE;
+        config.allowConversations = true;
+        config.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
 
         config.suppressedVisualEffects = 0;
         return config;
@@ -252,6 +255,8 @@
         config.allowReminders = false;
         config.allowEvents = false;
         config.areChannelsBypassingDnd = false;
+        config.allowConversations = false;
+        config.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE;
 
         config.suppressedVisualEffects = 0;
         return config;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index 32f389a..fb15088 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -16,6 +16,16 @@
 
 package com.android.server.notification;
 
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.CATEGORY_MESSAGE;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
@@ -31,11 +41,10 @@
 
 import android.app.Notification;
 import android.app.NotificationChannel;
-import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.media.AudioAttributes;
+import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.service.notification.ZenModeConfig;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -76,6 +85,16 @@
         return new NotificationRecord(mContext, sbn, c);
     }
 
+    private NotificationRecord getConversationRecord(NotificationChannel c,
+            StatusBarNotification sbn) {
+        NotificationRecord r = mock(NotificationRecord.class);
+        when(r.getCriticality()).thenReturn(CriticalNotificationExtractor.NORMAL);
+        when(r.getSbn()).thenReturn(sbn);
+        when(r.getChannel()).thenReturn(c);
+        when(r.isConversation()).thenReturn(true);
+        return r;
+    }
+
     @Test
     public void testIsMessage() {
         NotificationRecord r = getNotificationRecord();
@@ -97,14 +116,14 @@
         assertTrue(mZenModeFiltering.isAlarm(r));
 
         r = getNotificationRecord();
-        r.sbn.getNotification().category = Notification.CATEGORY_ALARM;
+        r.getSbn().getNotification().category = Notification.CATEGORY_ALARM;
         assertTrue(mZenModeFiltering.isAlarm(r));
     }
 
     @Test
     public void testIsAlarm_wrongCategory() {
         NotificationRecord r = getNotificationRecord();
-        r.sbn.getNotification().category = Notification.CATEGORY_CALL;
+        r.getSbn().getNotification().category = CATEGORY_CALL;
         assertFalse(mZenModeFiltering.isAlarm(r));
     }
 
@@ -121,10 +140,10 @@
     @Test
     public void testSuppressDNDInfo_yes_VisEffectsAllowed() {
         NotificationRecord r = getNotificationRecord();
-        when(r.sbn.getPackageName()).thenReturn("android");
-        when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
+        when(r.getSbn().getPackageName()).thenReturn("android");
+        when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
         Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects()
-                - SUPPRESSED_EFFECT_STATUS_BAR);
+                - SUPPRESSED_EFFECT_STATUS_BAR, 0);
 
         assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
     }
@@ -132,9 +151,9 @@
     @Test
     public void testSuppressDNDInfo_yes_WrongId() {
         NotificationRecord r = getNotificationRecord();
-        when(r.sbn.getPackageName()).thenReturn("android");
-        when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION);
-        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
+        when(r.getSbn().getPackageName()).thenReturn("android");
+        when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION);
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
 
         assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
     }
@@ -142,9 +161,9 @@
     @Test
     public void testSuppressDNDInfo_yes_WrongPackage() {
         NotificationRecord r = getNotificationRecord();
-        when(r.sbn.getPackageName()).thenReturn("android2");
-        when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
-        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
+        when(r.getSbn().getPackageName()).thenReturn("android2");
+        when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
 
         assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
     }
@@ -152,9 +171,9 @@
     @Test
     public void testSuppressDNDInfo_no() {
         NotificationRecord r = getNotificationRecord();
-        when(r.sbn.getPackageName()).thenReturn("android");
-        when(r.sbn.getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
-        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
+        when(r.getSbn().getPackageName()).thenReturn("android");
+        when(r.getSbn().getId()).thenReturn(SystemMessage.NOTE_ZEN_UPGRADE);
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
 
         assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
         assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_ALARMS, policy, r));
@@ -164,7 +183,7 @@
     @Test
     public void testSuppressAnything_yes_ZenModeOff() {
         NotificationRecord r = getNotificationRecord();
-        when(r.sbn.getPackageName()).thenReturn("bananas");
+        when(r.getSbn().getPackageName()).thenReturn("bananas");
         Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
 
         assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_OFF, policy, r));
@@ -174,12 +193,120 @@
     public void testSuppressAnything_bypass_ZenModeOn() {
         NotificationRecord r = getNotificationRecord();
         r.setCriticality(CriticalNotificationExtractor.CRITICAL);
-        when(r.sbn.getPackageName()).thenReturn("bananas");
-        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects());
+        when(r.getSbn().getPackageName()).thenReturn("bananas");
+        Policy policy = new Policy(0, 0, 0, Policy.getAllSuppressedVisualEffects(), 0);
 
         assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, policy, r));
 
         r.setCriticality(CriticalNotificationExtractor.CRITICAL_LOW);
         assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, policy, r));
     }
+
+    @Test
+    public void testConversation_allAllowed() {
+        Notification n = new Notification.Builder(mContext, "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+                UserHandle.SYSTEM, null, 0);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "me, work");
+
+        NotificationRecord r = getConversationRecord(channel, sbn);
+        when(r.isConversation()).thenReturn(true);
+
+        Policy policy = new Policy(
+                PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_ANYONE);
+
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+    }
+
+    @Test
+    public void testConversation_importantAllowed_isImportant() {
+        Notification n = new Notification.Builder(mContext, "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+                UserHandle.SYSTEM, null, 0);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "me, work");
+        channel.setImportantConversation(true);
+
+        NotificationRecord r = getConversationRecord(channel, sbn);
+
+        Policy policy = new Policy(
+                PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_IMPORTANT);
+
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+    }
+
+    @Test
+    public void testConversation_importantAllowed_isNotImportant() {
+        Notification n = new Notification.Builder(mContext, "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+                UserHandle.SYSTEM, null, 0);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "me, work");
+
+        NotificationRecord r = getConversationRecord(channel, sbn);
+
+        Policy policy = new Policy(
+                PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_IMPORTANT);
+
+        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+    }
+
+    @Test
+    public void testConversation_noneAllowed_notCallOrMsg() {
+        Notification n = new Notification.Builder(mContext, "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+                UserHandle.SYSTEM, null, 0);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "me, work");
+
+        NotificationRecord r = getConversationRecord(channel, sbn);
+
+        Policy policy =
+                new Policy(PRIORITY_CATEGORY_CONVERSATIONS, 0, 0, 0, CONVERSATION_SENDERS_NONE);
+
+        assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+    }
+
+    @Test
+    public void testConversation_noneAllowed_callAllowed() {
+        Notification n = new Notification.Builder(mContext, "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+                UserHandle.SYSTEM, null, 0);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "me, work");
+
+        NotificationRecord r = getConversationRecord(channel, sbn);
+        when(r.isCategory(CATEGORY_CALL)).thenReturn(true);
+
+        Policy policy =
+                new Policy(PRIORITY_CATEGORY_CALLS,
+                        PRIORITY_SENDERS_ANY, 0, 0, CONVERSATION_SENDERS_NONE);
+
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+    }
+
+    @Test
+    public void testConversation_noneAllowed_msgAllowed() {
+        when(mMessagingUtil.isMessaging(any())).thenReturn(true);
+        Notification n = new Notification.Builder(mContext, "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0, n,
+                UserHandle.SYSTEM, null, 0);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "me, work");
+
+        NotificationRecord r = getConversationRecord(channel, sbn);
+
+        Policy policy =
+                new Policy(PRIORITY_CATEGORY_MESSAGES,
+                        0, PRIORITY_SENDERS_ANY, 0, CONVERSATION_SENDERS_NONE);
+
+        assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 99771b9..bc2766c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -142,7 +142,8 @@
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
                 + "reminders=\"false\" events=\"false\" callsFrom=\"1\" messagesFrom=\"2\" "
                 + "visualScreenOff=\"true\" alarms=\"true\" "
-                + "media=\"true\" system=\"false\" />\n"
+                + "media=\"true\" system=\"false\" conversations=\"true\""
+                + " conversationsFrom=\"2\"/>\n"
                 + "<automatic ruleId=\"" + EVENTS_DEFAULT_RULE_ID
                 + "\" enabled=\"false\" snoozing=\"false\""
                 + " name=\"Event\" zen=\"1\""
@@ -218,7 +219,7 @@
     public void testZenOff_NoMuteApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_OFF;
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
-                Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
+                Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         doNothing().when(mZenModeHelperSpy).applyRestrictions(eq(false), anyBoolean(), anyInt());
@@ -232,7 +233,7 @@
     public void testZenOn_AllowAlarmsMedia_NoAlarmMediaMuteApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
-                Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
+                Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
 
         mZenModeHelperSpy.applyRestrictions();
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false,
@@ -244,7 +245,7 @@
     @Test
     public void testZenOn_DisallowAlarmsMedia_AlarmMediaMuteApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, true,
                 AudioAttributes.USAGE_ALARM);
@@ -260,7 +261,7 @@
     public void testTotalSilence() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
         mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
-                Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0);
+                Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         // Total silence will silence alarms, media and system noises (but not vibrations)
@@ -281,7 +282,7 @@
     @Test
     public void testAlarmsOnly_alarmMediaMuteNotApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
-        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         // Alarms only mode will not silence alarms
@@ -304,7 +305,7 @@
     @Test
     public void testAlarmsOnly_callsMuteApplied() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
-        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         // Alarms only mode will silence calls despite priority-mode config
@@ -318,7 +319,7 @@
     public void testAlarmsOnly_allZenConfigToggledCannotBypass_alarmMuteNotApplied() {
         // Only audio attributes with SUPPRESIBLE_NEVER can bypass
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_ALARMS;
-        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false, false,
@@ -330,7 +331,7 @@
         // Only audio attributes with SUPPRESIBLE_NEVER can bypass
         // with special case USAGE_ASSISTANCE_SONIFICATION
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         for (int usage : AudioAttributes.SDK_USAGES) {
@@ -352,7 +353,7 @@
     public void testApplyRestrictions_whitelist_priorityOnlyMode() {
         mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
         mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         for (int usage : AudioAttributes.SDK_USAGES) {
@@ -367,7 +368,7 @@
     public void testApplyRestrictions_whitelist_alarmsOnlyMode() {
         mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
         mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_ALARMS;
-        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         for (int usage : AudioAttributes.SDK_USAGES) {
@@ -382,7 +383,7 @@
     public void testApplyRestrictions_whitelist_totalSilenceMode() {
         mZenModeHelperSpy.setPriorityOnlyDndExemptPackages(new String[] {PKG_O});
         mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_NO_INTERRUPTIONS;
-        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.applyRestrictions();
 
         for (int usage : AudioAttributes.SDK_USAGES) {
@@ -401,7 +402,7 @@
         Settings.Secure.putInt(mContentResolver, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, 1);
         Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_SETTINGS_UPDATED, 0);
         mZenModeHelperSpy.mIsBootComplete = true;
-        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0);
+        mZenModeHelperSpy.mConsolidatedPolicy = new Policy(0, 0, 0, 0, 0, 0);
         mZenModeHelperSpy.setZenModeSetting(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         verify(mZenModeHelperSpy, times(1)).createZenUpgradeNotification();
@@ -452,7 +453,8 @@
         mZenModeHelperSpy.mConfig.allowCalls = false;
         mZenModeHelperSpy.mConfig.allowMessages = false;
         mZenModeHelperSpy.mConfig.allowEvents = false;
-        mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
+        mZenModeHelperSpy.mConfig.allowRepeatCallers = false;
+        mZenModeHelperSpy.mConfig.allowConversations = false;
 
         // 2. apply priority only zen - verify ringer is unchanged
         mZenModeHelperSpy.applyZenToRingerMode();
@@ -509,7 +511,8 @@
         mZenModeHelperSpy.mConfig.allowCalls = false;
         mZenModeHelperSpy.mConfig.allowMessages = false;
         mZenModeHelperSpy.mConfig.allowEvents = false;
-        mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
+        mZenModeHelperSpy.mConfig.allowRepeatCallers = false;
+        mZenModeHelperSpy.mConfig.allowConversations = false;
         ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerNotMuted =
                 mZenModeHelperSpy.new RingerModeDelegate();
 
@@ -694,7 +697,9 @@
         mZenModeHelperSpy.mConfig.allowCalls = true;
         mZenModeHelperSpy.mConfig.allowMessages = true;
         mZenModeHelperSpy.mConfig.allowEvents = true;
-        mZenModeHelperSpy.mConfig.allowRepeatCallers= true;
+        mZenModeHelperSpy.mConfig.allowRepeatCallers = true;
+        mZenModeHelperSpy.mConfig.allowConversations = true;
+        mZenModeHelperSpy.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
         mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
         mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
         mZenModeHelperSpy.mConfig.manualRule.component = new ComponentName("a", "a");
@@ -716,7 +721,9 @@
         mZenModeHelperSpy.mConfig.allowCalls = true;
         mZenModeHelperSpy.mConfig.allowMessages = true;
         mZenModeHelperSpy.mConfig.allowEvents = true;
-        mZenModeHelperSpy.mConfig.allowRepeatCallers= true;
+        mZenModeHelperSpy.mConfig.allowRepeatCallers = true;
+        mZenModeHelperSpy.mConfig.allowConversations = true;
+        mZenModeHelperSpy.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
         mZenModeHelperSpy.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
         mZenModeHelperSpy.mConfig.manualRule = new ZenModeConfig.ZenRule();
         mZenModeHelperSpy.mConfig.manualRule.zenMode =
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 399cf49..135d005 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -44,6 +45,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.app.BlockedAppActivity;
 import com.android.internal.app.HarmfulAppWarningActivity;
 import com.android.internal.app.SuspendedAppActivity;
 import com.android.internal.app.UnlaunchableAppActivity;
@@ -105,6 +107,8 @@
     private PackageManagerService mPackageManager;
     @Mock
     private ActivityManagerInternal mAmInternal;
+    @Mock
+    private LockTaskController mLockTaskController;
 
     private ActivityStartInterceptor mInterceptor;
     private ActivityInfo mAInfo = new ActivityInfo();
@@ -145,6 +149,13 @@
         when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
                 .thenReturn(null);
 
+        // Mock LockTaskController
+        mAInfo.lockTaskLaunchMode = LOCK_TASK_LAUNCH_MODE_DEFAULT;
+        when(mService.getLockTaskController()).thenReturn(mLockTaskController);
+        when(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
+                .thenReturn(true);
+
         // Initialise activity info
         mAInfo.applicationInfo = new ApplicationInfo();
         mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
@@ -196,6 +207,18 @@
     }
 
     @Test
+    public void testInterceptLockTaskModeViolationPackage() {
+        when(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
+                .thenReturn(false);
+
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+        assertTrue(BlockedAppActivity.createIntent(TEST_USER_ID, TEST_PACKAGE_NAME)
+                .filterEquals(mInterceptor.mIntent));
+    }
+
+    @Test
     public void testInterceptQuietProfile() {
         // GIVEN that the user the activity is starting as is currently in quiet mode
         when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 5acc0f2..956c200 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -126,7 +126,8 @@
         mTarget = new TestDragDropController(mWm, mWm.mH.getLooper());
         mWindow = createDropTargetWindow("Drag test window", 0);
         doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
-        when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true);
+        when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class),
+                any(InputChannel.class))).thenReturn(true);
 
         mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
     }
@@ -176,7 +177,8 @@
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
-            assertTrue(mWm.mInputManager.transferTouchFocus(null, null));
+            assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
+                    new InputChannel()));
             mToken = mTarget.performDrag(
                     new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0,
                     data);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 039ff60..a137cde 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -25,10 +25,14 @@
 import static android.app.StatusBarManager.DISABLE_HOME;
 import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
 import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
+import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
 import static android.os.Process.SYSTEM_UID;
 import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 
@@ -693,6 +697,45 @@
         assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0);
     }
 
+    @Test
+    public void testIsActivityAllowed() {
+        // WHEN lock task mode is not enabled
+        assertTrue(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+        // Start lock task mode
+        Task tr = getTask(Task.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // WHEN LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK is not enabled
+        assertTrue(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+        // Enable LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK feature
+        mLockTaskController.updateLockTaskFeatures(
+                TEST_USER_ID, LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK);
+
+        // package with LOCK_TASK_LAUNCH_MODE_ALWAYS should always be allowed
+        assertTrue(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_ALWAYS));
+
+        // unwhitelisted package should not be allowed
+        assertFalse(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+        // update the whitelist
+        String[] whitelist = new String[] { TEST_PACKAGE_NAME };
+        mLockTaskController.updateLockTaskPackages(TEST_USER_ID, whitelist);
+
+        // whitelisted package should be allowed
+        assertTrue(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT));
+
+        // package with LOCK_TASK_LAUNCH_MODE_NEVER should never be allowed
+        assertFalse(mLockTaskController.isActivityAllowed(
+                TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_NEVER));
+    }
+
     private Task getTask(int lockTaskAuth) {
         return getTask(TEST_PACKAGE_NAME, lockTaskAuth);
     }
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 14b847f..5f95bc1 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -1249,12 +1249,4 @@
             int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
         throw new UnsupportedOperationException();
     }
-
-    /**
-     * @hide
-     */
-    @Override
-    public String getSystemTextClassifierPackageName() {
-        throw new UnsupportedOperationException();
-    }
 }
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 2d5df4f..0628691 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -38,6 +38,8 @@
 import android.content.Context;
 import android.os.PersistableBundle;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -58,21 +60,26 @@
 
     private static final Executor INLINE_EXECUTOR = x -> x.run();
 
-    @Mock private Context mContext;
     @Mock private IConnectivityManager mService;
     @Mock private ConnectivityDiagnosticsCallback mCb;
 
+    private Context mContext;
     private ConnectivityDiagnosticsBinder mBinder;
     private ConnectivityDiagnosticsManager mManager;
 
+    private String mPackageName;
+
     @Before
     public void setUp() {
-        mContext = mock(Context.class);
+        mContext = InstrumentationRegistry.getContext();
+
         mService = mock(IConnectivityManager.class);
         mCb = mock(ConnectivityDiagnosticsCallback.class);
 
         mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR);
         mManager = new ConnectivityDiagnosticsManager(mContext, mService);
+
+        mPackageName = mContext.getOpPackageName();
     }
 
     @After
@@ -271,7 +278,7 @@
         mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
 
         verify(mService).registerConnectivityDiagnosticsCallback(
-                any(ConnectivityDiagnosticsBinder.class), eq(request));
+                any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
         assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
     }
 
@@ -302,7 +309,7 @@
         // verify that re-registering is successful
         mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
         verify(mService, times(2)).registerConnectivityDiagnosticsCallback(
-                any(ConnectivityDiagnosticsBinder.class), eq(request));
+                any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
         assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
     }
 
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 2573bba..b4f32e7 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,6 +23,8 @@
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
@@ -119,6 +121,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.app.AlarmManager;
+import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -132,6 +135,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.location.LocationManager;
 import android.net.CaptivePortalData;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
@@ -177,6 +181,7 @@
 import android.net.util.MultinetworkPolicyTracker;
 import android.os.BadParcelableException;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.ConditionVariable;
 import android.os.Handler;
@@ -187,6 +192,7 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -218,6 +224,7 @@
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.Nat464Xlat;
+import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.Vpn;
@@ -292,6 +299,8 @@
 
     private static final int UNREASONABLY_LONG_ALARM_WAIT_MS = 1000;
 
+    private static final long TIMESTAMP = 1234L;
+
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
@@ -327,6 +336,8 @@
     @Mock AlarmManager mAlarmManager;
     @Mock IConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback;
     @Mock IBinder mIBinder;
+    @Mock LocationManager mLocationManager;
+    @Mock AppOpsManager mAppOpsManager;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -412,6 +423,8 @@
             if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack;
             if (Context.USER_SERVICE.equals(name)) return mUserManager;
             if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
+            if (Context.LOCATION_SERVICE.equals(name)) return mLocationManager;
+            if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
             return super.getSystemService(name);
         }
 
@@ -558,12 +571,17 @@
                 | NETWORK_VALIDATION_RESULT_PARTIAL;
         private static final int VALIDATION_RESULT_INVALID = 0;
 
+        private static final long DATA_STALL_TIMESTAMP = 10L;
+        private static final int DATA_STALL_DETECTION_METHOD = 1;
+
         private INetworkMonitor mNetworkMonitor;
         private INetworkMonitorCallbacks mNmCallbacks;
         private int mNmValidationResult = VALIDATION_RESULT_BASE;
         private int mProbesCompleted;
         private int mProbesSucceeded;
         private String mNmValidationRedirectUrl = null;
+        private PersistableBundle mValidationExtras = PersistableBundle.EMPTY;
+        private PersistableBundle mDataStallExtras = PersistableBundle.EMPTY;
         private boolean mNmProvNotificationRequested = false;
 
         private final ConditionVariable mNetworkStatusReceived = new ConditionVariable();
@@ -631,8 +649,8 @@
             }
 
             mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded);
-            mNmCallbacks.notifyNetworkTested(
-                    mNmValidationResult, mNmValidationRedirectUrl);
+            mNmCallbacks.notifyNetworkTestedWithExtras(
+                    mNmValidationResult, mNmValidationRedirectUrl, TIMESTAMP, mValidationExtras);
 
             if (mNmValidationRedirectUrl != null) {
                 mNmCallbacks.showProvisioningNotification(
@@ -791,6 +809,11 @@
         public void expectPreventReconnectReceived() {
             expectPreventReconnectReceived(TIMEOUT_MS);
         }
+
+        void notifyDataStallSuspected() throws Exception {
+            mNmCallbacks.notifyDataStallSuspected(
+                    DATA_STALL_TIMESTAMP, DATA_STALL_DETECTION_METHOD, mDataStallExtras);
+        }
     }
 
     /**
@@ -970,6 +993,8 @@
         // not inherit from NetworkAgent.
         private TestNetworkAgentWrapper mMockNetworkAgent;
 
+        private VpnInfo mVpnInfo;
+
         public MockVpn(int userId) {
             super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
                     userId);
@@ -1041,6 +1066,17 @@
             mConnected = false;
             mConfig = null;
         }
+
+        @Override
+        public synchronized VpnInfo getVpnInfo() {
+            if (mVpnInfo != null) return mVpnInfo;
+
+            return super.getVpnInfo();
+        }
+
+        private void setVpnInfo(VpnInfo vpnInfo) {
+            mVpnInfo = vpnInfo;
+        }
     }
 
     private void mockVpn(int uid) {
@@ -6400,7 +6436,7 @@
                         new NetworkCapabilities(), TYPE_ETHERNET, 0, NetworkRequest.Type.NONE);
         try {
             mService.registerConnectivityDiagnosticsCallback(
-                    mConnectivityDiagnosticsCallback, request);
+                    mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
             fail("registerConnectivityDiagnosticsCallback should throw on invalid NetworkRequest");
         } catch (IllegalArgumentException expected) {
         }
@@ -6414,7 +6450,7 @@
         when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
 
         mService.registerConnectivityDiagnosticsCallback(
-                mConnectivityDiagnosticsCallback, wifiRequest);
+                mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
 
         verify(mIBinder, timeout(TIMEOUT_MS))
                 .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
@@ -6438,7 +6474,7 @@
         when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
 
         mService.registerConnectivityDiagnosticsCallback(
-                mConnectivityDiagnosticsCallback, wifiRequest);
+                mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
 
         verify(mIBinder, timeout(TIMEOUT_MS))
                 .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt());
@@ -6449,7 +6485,7 @@
 
         // Register the same callback again
         mService.registerConnectivityDiagnosticsCallback(
-                mConnectivityDiagnosticsCallback, wifiRequest);
+                mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
 
         // Block until all other events are done processing.
         HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
@@ -6458,4 +6494,182 @@
                 mService.mConnectivityDiagnosticsCallbacks.containsKey(
                         mConnectivityDiagnosticsCallback));
     }
+
+    @Test
+    public void testCheckConnectivityDiagnosticsPermissionsNetworkStack() throws Exception {
+        final NetworkAgentInfo naiWithoutUid =
+                new NetworkAgentInfo(
+                        null, null, null, null, null, new NetworkCapabilities(), null,
+                        mServiceContext, null, null, mService, null, null, null, 0);
+
+        mServiceContext.setPermission(
+                android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+        assertTrue(
+                "NetworkStack permission not applied",
+                mService.checkConnectivityDiagnosticsPermissions(
+                        Process.myPid(), Process.myUid(), naiWithoutUid,
+                        mContext.getOpPackageName()));
+    }
+
+    @Test
+    public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
+        final NetworkAgentInfo naiWithoutUid =
+                new NetworkAgentInfo(
+                        null, null, null, null, null, new NetworkCapabilities(), null,
+                        mServiceContext, null, null, mService, null, null, null, 0);
+
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+        assertFalse(
+                "ACCESS_FINE_LOCATION permission necessary for Connectivity Diagnostics",
+                mService.checkConnectivityDiagnosticsPermissions(
+                        Process.myPid(), Process.myUid(), naiWithoutUid,
+                        mContext.getOpPackageName()));
+    }
+
+    @Test
+    public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception {
+        final NetworkAgentInfo naiWithoutUid =
+                new NetworkAgentInfo(
+                        null, null, null, null, null, new NetworkCapabilities(), null,
+                        mServiceContext, null, null, mService, null, null, null, 0);
+
+        setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+        // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be
+        // active
+        final VpnInfo info = new VpnInfo();
+        info.ownerUid = Process.myUid();
+        info.vpnIface = "interface";
+        mMockVpn.setVpnInfo(info);
+        assertTrue(
+                "Active VPN permission not applied",
+                mService.checkConnectivityDiagnosticsPermissions(
+                        Process.myPid(), Process.myUid(), naiWithoutUid,
+                        mContext.getOpPackageName()));
+    }
+
+    @Test
+    public void testCheckConnectivityDiagnosticsPermissionsNetworkAdministrator() throws Exception {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+        final NetworkAgentInfo naiWithUid =
+                new NetworkAgentInfo(
+                        null, null, null, null, null, nc, null, mServiceContext, null, null,
+                        mService, null, null, null, 0);
+
+        setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+        // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested
+        mMockVpn.disconnect();
+        assertTrue(
+                "NetworkCapabilities administrator uid permission not applied",
+                mService.checkConnectivityDiagnosticsPermissions(
+                        Process.myPid(), Process.myUid(), naiWithUid, mContext.getOpPackageName()));
+    }
+
+    @Test
+    public void testCheckConnectivityDiagnosticsPermissionsFails() throws Exception {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        nc.setOwnerUid(Process.myUid());
+        nc.setAdministratorUids(Arrays.asList(Process.myUid()));
+        final NetworkAgentInfo naiWithUid =
+                new NetworkAgentInfo(
+                        null, null, null, null, null, nc, null, mServiceContext, null, null,
+                        mService, null, null, null, 0);
+
+        setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+        // Use wrong pid and uid
+        assertFalse(
+                "Permissions allowed when they shouldn't be granted",
+                mService.checkConnectivityDiagnosticsPermissions(
+                        Process.myPid() + 1, Process.myUid() + 1, naiWithUid,
+                        mContext.getOpPackageName()));
+    }
+
+    private void setupLocationPermissions(
+            int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
+        final ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.targetSdkVersion = targetSdk;
+        when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+                .thenReturn(applicationInfo);
+
+        when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
+
+        when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+                .thenReturn(AppOpsManager.MODE_ALLOWED);
+
+        mServiceContext.setPermission(perm, PERMISSION_GRANTED);
+    }
+
+    private void setUpConnectivityDiagnosticsCallback() throws Exception {
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+        when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+
+        mServiceContext.setPermission(
+                android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+        mService.registerConnectivityDiagnosticsCallback(
+                mConnectivityDiagnosticsCallback, request, mContext.getPackageName());
+
+        // Block until all other events are done processing.
+        HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+        // Connect the cell agent verify that it notifies TestNetworkCallback that it is available
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(callback);
+        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+        callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+        callback.assertNoCallback();
+    }
+
+    @Test
+    public void testConnectivityDiagnosticsCallbackOnConnectivityReport() throws Exception {
+        setUpConnectivityDiagnosticsCallback();
+
+        // Wait for onConnectivityReport to fire
+        verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+                .onConnectivityReport(any(ConnectivityReport.class));
+    }
+
+    @Test
+    public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() throws Exception {
+        setUpConnectivityDiagnosticsCallback();
+
+        // Trigger notifyDataStallSuspected() on the INetworkMonitorCallbacks instance in the
+        // cellular network agent
+        mCellNetworkAgent.notifyDataStallSuspected();
+
+        // Wait for onDataStallSuspected to fire
+        verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+                .onDataStallSuspected(any(DataStallReport.class));
+    }
+
+    @Test
+    public void testConnectivityDiagnosticsCallbackOnConnectivityReported() throws Exception {
+        setUpConnectivityDiagnosticsCallback();
+
+        final Network n = mCellNetworkAgent.getNetwork();
+        final boolean hasConnectivity = true;
+        mService.reportNetworkConnectivity(n, hasConnectivity);
+
+        // Wait for onNetworkConnectivityReported to fire
+        verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+                .onNetworkConnectivityReported(eq(n), eq(hasConnectivity));
+
+        final boolean noConnectivity = false;
+        mService.reportNetworkConnectivity(n, noConnectivity);
+
+        // Wait for onNetworkConnectivityReported to fire
+        verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+                .onNetworkConnectivityReported(eq(n), eq(noConnectivity));
+    }
 }
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 0e32aee..d3958a6 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -30,6 +30,7 @@
         "utils.cpp",
     ],
     cflags: [
+        "-DSTATS_SCHEMA_LEGACY",
         "-Wall",
         "-Werror",
     ],
diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp
index c7a34fe..285514d 100644
--- a/tools/stats_log_api_gen/native_writer.cpp
+++ b/tools/stats_log_api_gen/native_writer.cpp
@@ -22,15 +22,6 @@
 namespace stats_log_api_gen {
 
 #if !defined(STATS_SCHEMA_LEGACY)
-static void write_native_key_value_pairs_for_type(FILE* out, const int argIndex,
-        const int typeIndex, const string& type, const string& valueFieldName) {
-    fprintf(out, "    for (const auto& it : arg%d_%d) {\n", argIndex, typeIndex);
-    fprintf(out, "        pairs.push_back("
-            "{ .key = it.first, .valueType = %s, .%s = it.second });\n",
-            type.c_str(), valueFieldName.c_str());
-    fprintf(out, "    }\n");
-
-}
 
 static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
         const AtomDecl& attributionDecl, const string& moduleName, const bool supportQ) {
@@ -41,7 +32,10 @@
             continue;
         }
         vector<java_type_t> signature = signature_to_modules_it->first;
-
+        // Key value pairs not supported in native.
+        if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+            continue;
+        }
         write_native_method_signature(out, "int stats_write", signature,
                 attributionDecl, " {");
 
@@ -59,11 +53,6 @@
                                 uidName, uidName, tagName);
                         break;
                     }
-                    case JAVA_TYPE_KEY_VALUE_PAIR:
-                        fprintf(out, "    event.writeKeyValuePairs("
-                                "arg%d_1, arg%d_2, arg%d_3, arg%d_4);\n",
-                                argIndex, argIndex, argIndex, argIndex);
-                        break;
                     case JAVA_TYPE_BYTE_ARRAY:
                         fprintf(out, "    event.writeByteArray(arg%d.arg, arg%d.arg_length);\n",
                                 argIndex, argIndex);
@@ -85,7 +74,7 @@
                         fprintf(out, "    event.writeString(arg%d);\n", argIndex);
                         break;
                     default:
-                        // Unsupported types: OBJECT, DOUBLE.
+                        // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS.
                         fprintf(stderr, "Encountered unsupported type.");
                         return 1;
                 }
@@ -93,8 +82,8 @@
             }
             fprintf(out, "    return event.writeToSocket();\n");
         } else {
-            fprintf(out, "    struct stats_event* event = stats_event_obtain();\n");
-            fprintf(out, "    stats_event_set_atom_id(event, code);\n");
+            fprintf(out, "    AStatsEvent* event = AStatsEvent_obtain();\n");
+            fprintf(out, "    AStatsEvent_setAtomId(event, code);\n");
             for (vector<java_type_t>::const_iterator arg = signature.begin();
                     arg != signature.end(); arg++) {
                 switch (*arg) {
@@ -102,57 +91,43 @@
                         const char* uidName = attributionDecl.fields.front().name.c_str();
                         const char* tagName = attributionDecl.fields.back().name.c_str();
                         fprintf(out,
-                                "    stats_event_write_attribution_chain(event, "
+                                "    AStatsEvent_writeAttributionChain(event, "
                                 "reinterpret_cast<const uint32_t*>(%s), %s.data(), "
                                 "static_cast<uint8_t>(%s_length));\n",
                                 uidName, tagName, uidName);
                         break;
                     }
-                    case JAVA_TYPE_KEY_VALUE_PAIR:
-                        fprintf(out, "    std::vector<key_value_pair> pairs;\n");
-                        write_native_key_value_pairs_for_type(
-                                out, argIndex, 1, "INT32_TYPE", "int32Value");
-                        write_native_key_value_pairs_for_type(
-                                out, argIndex, 2, "INT64_TYPE", "int64Value");
-                        write_native_key_value_pairs_for_type(
-                                out, argIndex, 3, "STRING_TYPE", "stringValue");
-                        write_native_key_value_pairs_for_type(
-                                out, argIndex, 4, "FLOAT_TYPE", "floatValue");
-                        fprintf(out,
-                                "    stats_event_write_key_value_pairs(event, pairs.data(), "
-                                "static_cast<uint8_t>(pairs.size()));\n");
-                        break;
                     case JAVA_TYPE_BYTE_ARRAY:
                         fprintf(out,
-                                "    stats_event_write_byte_array(event, "
+                                "    AStatsEvent_writeByteArray(event, "
                                 "reinterpret_cast<const uint8_t*>(arg%d.arg), arg%d.arg_length);\n",
                                 argIndex, argIndex);
                         break;
                     case JAVA_TYPE_BOOLEAN:
-                        fprintf(out, "    stats_event_write_bool(event, arg%d);\n", argIndex);
+                        fprintf(out, "    AStatsEvent_writeBool(event, arg%d);\n", argIndex);
                         break;
                     case JAVA_TYPE_INT: // Fall through.
                     case JAVA_TYPE_ENUM:
-                        fprintf(out, "    stats_event_write_int32(event, arg%d);\n", argIndex);
+                        fprintf(out, "    AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
                         break;
                     case JAVA_TYPE_FLOAT:
-                        fprintf(out, "    stats_event_write_float(event, arg%d);\n", argIndex);
+                        fprintf(out, "    AStatsEvent_writeFloat(event, arg%d);\n", argIndex);
                         break;
                     case JAVA_TYPE_LONG:
-                        fprintf(out, "    stats_event_write_int64(event, arg%d);\n", argIndex);
+                        fprintf(out, "    AStatsEvent_writeInt64(event, arg%d);\n", argIndex);
                         break;
                     case JAVA_TYPE_STRING:
-                        fprintf(out, "    stats_event_write_string8(event, arg%d);\n", argIndex);
+                        fprintf(out, "    AStatsEvent_writeString(event, arg%d);\n", argIndex);
                         break;
                     default:
-                        // Unsupported types: OBJECT, DOUBLE.
+                        // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS
                         fprintf(stderr, "Encountered unsupported type.");
                         return 1;
                 }
                 argIndex++;
             }
-            fprintf(out, "    const int ret = stats_event_write(event);\n");
-            fprintf(out, "    stats_event_release(event);\n");
+            fprintf(out, "    const int ret = AStatsEvent_write(event);\n");
+            fprintf(out, "    AStatsEvent_release(event);\n");
             fprintf(out, "    return ret;\n");
         }
         fprintf(out, "}\n\n");
@@ -169,6 +144,10 @@
             continue;
         }
         vector<java_type_t> signature = signature_it->first;
+        // Key value pairs not supported in native.
+        if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+            continue;
+        }
 
         write_native_method_signature(out, "int stats_write_non_chained", signature,
                 attributionDecl, " {");
@@ -210,8 +189,14 @@
         if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
             continue;
         }
-
         vector<java_type_t> signature = signature_to_modules_it->first;
+
+#if !defined(STATS_SCHEMA_LEGACY)
+        // Key value pairs not supported in native.
+        if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+            continue;
+        }
+#endif
         write_native_method_signature(out, methodName, signature, attributionDecl, ";");
     }
 }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 91b7df3..62d6067 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -568,14 +568,12 @@
      * 2GHz band.
      * @hide
      */
-    @SystemApi
     public static final int AP_BAND_2GHZ = 0;
 
     /**
      * 5GHz band.
      * @hide
      */
-    @SystemApi
     public static final int AP_BAND_5GHZ = 1;
 
     /**
@@ -583,7 +581,6 @@
      * operating country code and current radio conditions.
      * @hide
      */
-    @SystemApi
     public static final int AP_BAND_ANY = -1;
 
     /**
@@ -593,7 +590,7 @@
      *
      * @hide
      */
-    @SystemApi
+    @UnsupportedAppUsage
     @ApBand
     public int apBand = AP_BAND_2GHZ;
 
diff --git a/wifi/java/android/net/wifi/WifiFrameworkInitializer.java b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
index 002820b..1507199 100644
--- a/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
+++ b/wifi/java/android/net/wifi/WifiFrameworkInitializer.java
@@ -102,15 +102,6 @@
                 }
         );
         SystemServiceRegistry.registerContextAwareService(
-                Context.WIFI_RTT_SERVICE,
-                RttManager.class,
-                (context, serviceBinder) -> {
-                    IWifiRttManager service = IWifiRttManager.Stub.asInterface(serviceBinder);
-                    WifiRttManager wifiRttManager = new WifiRttManager(context, service);
-                    return new RttManager(context, wifiRttManager);
-                }
-        );
-        SystemServiceRegistry.registerContextAwareService(
                 Context.WIFI_RTT_RANGING_SERVICE,
                 WifiRttManager.class,
                 (context, serviceBinder) -> {
@@ -118,5 +109,13 @@
                     return new WifiRttManager(context, service);
                 }
         );
+        SystemServiceRegistry.registerContextAwareService(
+                Context.WIFI_RTT_SERVICE,
+                RttManager.class,
+                context -> {
+                    WifiRttManager wifiRttManager = context.getSystemService(WifiRttManager.class);
+                    return new RttManager(context, wifiRttManager);
+                }
+        );
     }
 }