Merge "Fix broken tests in TextClassifierServiceTest." into rvc-dev
diff --git a/Android.bp b/Android.bp
index 8adf48d..da5d624 100644
--- a/Android.bp
+++ b/Android.bp
@@ -791,10 +791,6 @@
         "libphonenumber-platform",
         "tagsoup",
         "rappor",
-        "libtextclassifier-java",
-    ],
-    required: [
-        "libtextclassifier",
     ],
     dxflags: ["--core-library"],
 }
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 7f23df7..da9f165 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -41,7 +41,7 @@
 ]
 
 stubs_defaults {
-    name: "metalava-non-updatable-api-stubs-default",
+    name: "metalava-base-api-stubs-default",
     srcs: [
         ":framework-non-updatable-sources",
         "core/java/**/*.logtags",
@@ -70,12 +70,18 @@
 }
 
 stubs_defaults {
-    name: "metalava-api-stubs-default",
-    defaults: ["metalava-non-updatable-api-stubs-default"],
+    name: "metalava-full-api-stubs-default",
+    defaults: ["metalava-base-api-stubs-default"],
     srcs: [":framework-updatable-sources"],
     sdk_version: "core_platform",
 }
 
+stubs_defaults {
+    name: "metalava-non-updatable-api-stubs-default",
+    defaults: ["metalava-base-api-stubs-default"],
+    sdk_version: "system_current",
+}
+
 /////////////////////////////////////////////////////////////////////
 // *-api-stubs-docs modules providing source files for the stub libraries
 /////////////////////////////////////////////////////////////////////
@@ -85,7 +91,7 @@
 // modules
 droidstubs {
     name: "api-stubs-docs",
-    defaults: ["metalava-api-stubs-default"],
+    defaults: ["metalava-full-api-stubs-default"],
     api_filename: "public_api.txt",
     private_api_filename: "private.txt",
     removed_api_filename: "removed.txt",
@@ -124,7 +130,7 @@
 
 droidstubs {
     name: "system-api-stubs-docs",
-    defaults: ["metalava-api-stubs-default"],
+    defaults: ["metalava-full-api-stubs-default"],
     api_tag_name: "SYSTEM",
     api_filename: "system-api.txt",
     private_api_filename: "system-private.txt",
@@ -155,7 +161,7 @@
 
 droidstubs {
     name: "test-api-stubs-docs",
-    defaults: ["metalava-api-stubs-default"],
+    defaults: ["metalava-full-api-stubs-default"],
     api_tag_name: "TEST",
     api_filename: "test-api.txt",
     removed_api_filename: "test-removed.txt",
@@ -188,7 +194,7 @@
 
 droidstubs {
     name: "module-lib-api",
-    defaults: ["metalava-api-stubs-default"],
+    defaults: ["metalava-full-api-stubs-default"],
     arg_files: ["core/res/AndroidManifest.xml"],
     args: metalava_framework_docs_args + module_libs,
     check_api: {
@@ -216,7 +222,7 @@
 
 droidstubs {
     name: "module-lib-api-stubs-docs",
-    defaults: ["metalava-api-stubs-default"],
+    defaults: ["metalava-non-updatable-api-stubs-default"],
     arg_files: ["core/res/AndroidManifest.xml"],
     args: metalava_framework_docs_args + priv_apps + module_libs,
 }
@@ -266,6 +272,7 @@
     name: "android_module_lib_stubs_current",
     srcs: [ ":module-lib-api-stubs-docs" ],
     defaults: ["framework-stubs-default"],
+    libs: ["android_system_stubs_current"],
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -317,7 +324,7 @@
 
 droidstubs {
     name: "hiddenapi-lists-docs",
-    defaults: ["metalava-api-stubs-default"],
+    defaults: ["metalava-full-api-stubs-default"],
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
@@ -332,7 +339,7 @@
 
 droidstubs {
     name: "hiddenapi-mappings",
-    defaults: ["metalava-api-stubs-default"],
+    defaults: ["metalava-full-api-stubs-default"],
     srcs: [
         ":opt-telephony-common-srcs",
     ],
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
index f61ea85..46250d7 100644
--- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
+++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
@@ -77,7 +77,6 @@
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             textClassificationManager.getTextClassifier();
-            textClassificationManager.invalidateForTesting();
         }
     }
 
@@ -90,7 +89,6 @@
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             textClassificationManager.getTextClassifier();
-            textClassificationManager.invalidateForTesting();
         }
     }
 
diff --git a/apex/Android.bp b/apex/Android.bp
index 1510911..39137fb 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -49,7 +49,7 @@
 stubs_defaults {
     name: "framework-module-stubs-defaults-systemapi",
     args: mainline_stubs_args + priv_apps,
-    srcs: [":framework-annotations"],
+    libs: ["framework-annotations-lib"],
     installable: false,
     sdk_version: "system_current",
 }
@@ -62,7 +62,7 @@
 stubs_defaults {
     name: "framework-module-api-defaults-module_libs_api",
     args: mainline_stubs_args + module_libs,
-    srcs: [":framework-annotations"],
+    libs: ["framework-annotations-lib"],
     installable: false,
     sdk_version: "module_current",
 }
@@ -70,7 +70,7 @@
 stubs_defaults {
     name: "framework-module-stubs-defaults-module_libs_api",
     args: mainline_stubs_args + module_libs + priv_apps,
-    srcs: [":framework-annotations"],
+    libs: ["framework-annotations-lib"],
     installable: false,
     sdk_version: "module_current",
 }
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index f8b598a..6d9e3ed 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -5,6 +5,7 @@
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManager.SystemForcedReasons;
 import android.content.Context;
 import android.os.Looper;
 
@@ -123,9 +124,10 @@
      * appropriate time.
      *
      * @param restrictReason The restrictReason for restricting the app. Should be one of the
-     *                       UsageStatsManager.REASON_SUB_RESTRICT_* reasons.
+     *                       UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_* reasons.
      */
-    void restrictApp(@NonNull String packageName, int userId, int restrictReason);
+    void restrictApp(@NonNull String packageName, int userId,
+            @SystemForcedReasons int restrictReason);
 
     void addActiveDeviceAdmin(String adminPkg, int userId);
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index fc29c9c..819f253 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -974,7 +974,7 @@
             if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
                 Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
                 mAppStandbyInternal.restrictApp(
-                        pkg, userId, UsageStatsManager.REASON_SUB_RESTRICT_BUGGY);
+                        pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
                 if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
                     final boolean isDebuggable;
                     synchronized (mLock) {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 249bc52..e14ca99 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -58,6 +58,7 @@
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManager.StandbyBuckets;
+import android.app.usage.UsageStatsManager.SystemForcedReasons;
 import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -1153,6 +1154,13 @@
         }
     }
 
+    @VisibleForTesting
+    int getAppStandbyBucketReason(String packageName, int userId, long elapsedRealtime) {
+        synchronized (mAppIdleLock) {
+            return mAppIdleHistory.getAppStandbyReason(packageName, userId, elapsedRealtime);
+        }
+    }
+
     @Override
     public List<AppStandbyInfo> getAppStandbyBuckets(int userId) {
         synchronized (mAppIdleLock) {
@@ -1161,7 +1169,8 @@
     }
 
     @Override
-    public void restrictApp(@NonNull String packageName, int userId, int restrictReason) {
+    public void restrictApp(@NonNull String packageName, int userId,
+            @SystemForcedReasons int restrictReason) {
         // If the package is not installed, don't allow the bucket to be set.
         if (!mInjector.isPackageInstalled(packageName, 0, userId)) {
             Slog.e(TAG, "Tried to restrict uninstalled app: " + packageName);
@@ -1248,30 +1257,52 @@
             // Don't allow changing bucket if higher than ACTIVE
             if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
 
-            // Don't allow prediction to change from/to NEVER or from RESTRICTED.
-            if ((app.currentBucket == STANDBY_BUCKET_NEVER
-                    || app.currentBucket == STANDBY_BUCKET_RESTRICTED
-                    || newBucket == STANDBY_BUCKET_NEVER)
+            // Don't allow prediction to change from/to NEVER.
+            if ((app.currentBucket == STANDBY_BUCKET_NEVER || newBucket == STANDBY_BUCKET_NEVER)
                     && predicted) {
                 return;
             }
 
+            final boolean wasForcedBySystem =
+                    (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
+
             // If the bucket was forced, don't allow prediction to override
             if (predicted
                     && ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER
-                    || (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM)) {
+                    || wasForcedBySystem)) {
+                return;
+            }
+
+            final boolean isForcedBySystem =
+                    (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_SYSTEM;
+
+            if (app.currentBucket == newBucket && wasForcedBySystem && isForcedBySystem) {
+                mAppIdleHistory
+                        .noteRestrictionAttempt(packageName, userId, elapsedRealtime, reason);
+                // Keep track of all restricting reasons
+                reason = REASON_MAIN_FORCED_BY_SYSTEM
+                        | (app.bucketingReason & REASON_SUB_MASK)
+                        | (reason & REASON_SUB_MASK);
+                mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
+                        newBucket, reason, resetTimeout);
                 return;
             }
 
             final boolean isForcedByUser =
                     (reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
 
-            // If the current bucket is RESTRICTED, only user force or usage should bring it out,
-            // unless the app was put into the bucket due to timing out.
-            if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason)
-                    && !isForcedByUser
-                    && (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) {
-                return;
+            if (app.currentBucket == STANDBY_BUCKET_RESTRICTED) {
+                if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_TIMEOUT) {
+                    if (predicted && newBucket >= STANDBY_BUCKET_RARE) {
+                        // Predicting into RARE or below means we don't expect the user to use the
+                        // app anytime soon, so don't elevate it from RESTRICTED.
+                        return;
+                    }
+                } else if (!isUserUsage(reason) && !isForcedByUser) {
+                    // If the current bucket is RESTRICTED, only user force or usage should bring
+                    // it out, unless the app was put into the bucket due to timing out.
+                    return;
+                }
             }
 
             if (newBucket == STANDBY_BUCKET_RESTRICTED) {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 29c48ad..0d9cbf0 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -25,6 +25,7 @@
 
 import com.google.android.exoplayer2.C;
 import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.ParserException;
 import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
 import com.google.android.exoplayer2.extractor.Extractor;
 import com.google.android.exoplayer2.extractor.ExtractorInput;
@@ -431,6 +432,14 @@
         }
     }
 
+    /** Thrown when an error occurs while parsing a media stream. */
+    public static final class ParsingException extends IOException {
+
+        private ParsingException(ParserException cause) {
+            super(cause);
+        }
+    }
+
     // Public constants.
 
     /**
@@ -768,6 +777,8 @@
         int result = 0;
         try {
             result = mExtractor.read(mExtractorInput, mPositionHolder);
+        } catch (ParserException e) {
+            throw new ParsingException(e);
         } catch (InterruptedException e) {
             // TODO: Remove this exception replacement once we update the ExoPlayer version.
             throw new InterruptedIOException();
diff --git a/api/current.txt b/api/current.txt
index 8e61e85..05c7cbc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5995,7 +5995,7 @@
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(String);
     method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
-    method @Nullable public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
+    method @NonNull public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(String);
@@ -11721,6 +11721,7 @@
     ctor public LauncherApps.ShortcutQuery();
     method public android.content.pm.LauncherApps.ShortcutQuery setActivity(@Nullable android.content.ComponentName);
     method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+    method @NonNull public android.content.pm.LauncherApps.ShortcutQuery setLocusIds(@Nullable java.util.List<android.content.LocusId>);
     method public android.content.pm.LauncherApps.ShortcutQuery setPackage(@Nullable String);
     method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
     method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(@Nullable java.util.List<java.lang.String>);
@@ -26461,6 +26462,9 @@
     method public void onTrackDataFound(int, @NonNull android.media.MediaParser.TrackData);
   }
 
+  public static final class MediaParser.ParsingException extends java.io.IOException {
+  }
+
   public static final class MediaParser.SeekMap {
     method public long getDurationMicros();
     method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
@@ -27051,7 +27055,7 @@
     method public abstract void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo, int);
   }
 
-  public class MediaRouter2 {
+  public final class MediaRouter2 {
     method @NonNull public java.util.List<android.media.MediaRouter2.RoutingController> getControllers();
     method @NonNull public static android.media.MediaRouter2 getInstance(@NonNull android.content.Context);
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getRoutes();
@@ -29818,7 +29822,7 @@
 
   public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
     ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
-    method public void onConnectivityReport(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
+    method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
     method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
     method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
   }
@@ -37160,12 +37164,10 @@
     method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
     method @NonNull public android.os.VibrationEffect compose();
     field public static final int PRIMITIVE_CLICK = 1; // 0x1
-    field public static final int PRIMITIVE_LIGHT_TICK = 7; // 0x7
     field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6
     field public static final int PRIMITIVE_QUICK_RISE = 4; // 0x4
     field public static final int PRIMITIVE_SLOW_RISE = 5; // 0x5
-    field public static final int PRIMITIVE_SPIN = 3; // 0x3
-    field public static final int PRIMITIVE_THUD = 2; // 0x2
+    field public static final int PRIMITIVE_TICK = 7; // 0x7
   }
 
   public abstract class Vibrator {
@@ -47835,6 +47837,7 @@
     method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
     method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
     method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long);
+    method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
     method public void sendTextMessage(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
     method public void sendTextMessage(@NonNull String, @Nullable String, @NonNull String, @Nullable android.app.PendingIntent, @Nullable android.app.PendingIntent, long);
     method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.SEND_SMS}) public void sendTextMessageWithoutPersisting(String, String, String, android.app.PendingIntent, android.app.PendingIntent);
@@ -48211,7 +48214,6 @@
     method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
     method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
     method public boolean isConcurrentVoiceAndDataSupported();
-    method public boolean isDataCapable();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
     method public boolean isEmergencyNumber(@NonNull String);
@@ -49000,8 +49002,8 @@
     ctor public RegistrationManager.RegistrationCallback();
     method public void onRegistered(int);
     method public void onRegistering(int);
-    method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
-    method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
+    method public void onTechnologyChangeFailed(int, @NonNull android.telephony.ims.ImsReasonInfo);
+    method public void onUnregistered(@NonNull android.telephony.ims.ImsReasonInfo);
   }
 
 }
@@ -55588,7 +55590,7 @@
 
   public interface WindowInsetsController {
     method public void addOnControllableInsetsChangedListener(@NonNull android.view.WindowInsetsController.OnControllableInsetsChangedListener);
-    method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
+    method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @Nullable android.os.CancellationSignal, @NonNull android.view.WindowInsetsAnimationControlListener);
     method public int getSystemBarsAppearance();
     method public int getSystemBarsBehavior();
     method public void hide(int);
@@ -56812,7 +56814,7 @@
   public static final class InlinePresentationSpec.Builder {
     ctor public InlinePresentationSpec.Builder(@NonNull android.util.Size, @NonNull android.util.Size);
     method @NonNull public android.view.inline.InlinePresentationSpec build();
-    method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@Nullable android.os.Bundle);
+    method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@NonNull android.os.Bundle);
   }
 
 }
@@ -57024,7 +57026,7 @@
     ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.view.inline.InlinePresentationSpec>);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(@NonNull android.view.inline.InlinePresentationSpec);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
-    method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@Nullable android.os.Bundle);
+    method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
     method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
   }
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 69b52693..ee997f1 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -29,7 +29,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
   }
 
-  public class TetheringConstants {
+  public final class TetheringConstants {
     field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
     field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
     field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
diff --git a/api/system-current.txt b/api/system-current.txt
index 9eb5ed9..76d230a 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -148,6 +148,7 @@
     field public static final String NETWORK_SETUP_WIZARD = "android.permission.NETWORK_SETUP_WIZARD";
     field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP";
     field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
+    field public static final String NETWORK_STATS_PROVIDER = "android.permission.NETWORK_STATS_PROVIDER";
     field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
     field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
     field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
@@ -252,9 +253,6 @@
 
   public static final class R.array {
     field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
-    field public static final int config_restrictedPreinstalledCarrierApps = 17235975; // 0x1070007
-    field public static final int config_sms_enabled_locking_shift_tables = 17235977; // 0x1070009
-    field public static final int config_sms_enabled_single_shift_tables = 17235976; // 0x1070008
     field public static final int simColors = 17235974; // 0x1070006
   }
 
@@ -307,7 +305,6 @@
     field public static final int config_helpPackageNameKey = 17039387; // 0x104001b
     field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
     field public static final int config_systemGallery = 17039402; // 0x104002a
-    field public static final int low_memory = 17039403; // 0x104002b
   }
 
   public static final class R.style {
@@ -354,7 +351,6 @@
     method @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS) public static void setPersistentVrThread(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
     method public void unregisterHomeVisibilityObserver(@NonNull android.app.HomeVisibilityObserver);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
   }
 
   public static interface ActivityManager.OnUidImportanceListener {
@@ -408,7 +404,6 @@
     field public static final String OPSTR_POST_NOTIFICATION = "android:post_notification";
     field public static final String OPSTR_PROJECT_MEDIA = "android:project_media";
     field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
-    field public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
     field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
     field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio";
     field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images";
@@ -758,7 +753,6 @@
   public class StatusBarManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
-    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
   }
 
   public static final class StatusBarManager.DisableInfo {
@@ -1416,7 +1410,8 @@
   }
 
   public class NetworkStatsManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.net.netstats.provider.NetworkStatsProviderCallback registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.AbstractNetworkStatsProvider);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider);
   }
 
   public static final class UsageEvents.Event {
@@ -2108,10 +2103,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR;
   }
 
-  public static class LauncherApps.ShortcutQuery {
-    method @NonNull public android.content.pm.LauncherApps.ShortcutQuery setLocusIds(@Nullable java.util.List<android.content.LocusId>);
-  }
-
   public class PackageInstaller {
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
     field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
@@ -2282,7 +2273,6 @@
     field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
     field public static final int MATCH_ANY_USER = 4194304; // 0x400000
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
-    field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
     field public static final int MATCH_INSTANT = 8388608; // 0x800000
     field public static final int MODULE_APEX_NAME = 1; // 0x1
     field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
@@ -4896,16 +4886,10 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
-  }
-
-  public static interface Tuner.OnResourceLostListener {
-    method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
-  }
-
-  public final class TunerConstants {
     field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
     field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
     field public static final int INVALID_STREAM_ID = 65535; // 0xffff
+    field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL
     field public static final int INVALID_TS_PID = 65535; // 0xffff
     field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
     field public static final int RESULT_INVALID_STATE = 3; // 0x3
@@ -4919,6 +4903,10 @@
     field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0
   }
 
+  public static interface Tuner.OnResourceLostListener {
+    method public void onResourceLost(@NonNull android.media.tv.tuner.Tuner);
+  }
+
 }
 
 package android.media.tv.tuner.dvr {
@@ -5279,7 +5267,6 @@
     method public long getSourceTime();
     method public long getTimeStamp();
     method public int setCurrentTimestamp(long);
-    field public static final long TIMESTAMP_UNAVAILABLE = -1L; // 0xffffffffffffffffL
   }
 
   public final class TlvFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration {
@@ -5366,8 +5353,9 @@
     field public static final int SIGNAL_TYPE_UNDEFINED = 0; // 0x0
   }
 
-  public static class AnalogFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder> {
+  public static class AnalogFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build();
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int);
     method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int);
   }
@@ -5431,10 +5419,11 @@
     field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0
   }
 
-  public static class Atsc3FrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder> {
+  public static class Atsc3FrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setBandwidth(int);
     method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setDemodOutputFormat(int);
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.Atsc3FrontendSettings.Builder setPlpSettings(@NonNull android.media.tv.tuner.frontend.Atsc3PlpSettings[]);
   }
 
@@ -5475,8 +5464,9 @@
     field public static final int MODULATION_UNDEFINED = 0; // 0x0
   }
 
-  public static class AtscFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.AtscFrontendSettings.Builder> {
+  public static class AtscFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings build();
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
   }
 
@@ -5514,9 +5504,10 @@
     field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0
   }
 
-  public static class DvbcFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder> {
+  public static class DvbcFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(int);
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setInnerFec(long);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int);
@@ -5592,9 +5583,10 @@
     field public static final int VCM_MODE_UNDEFINED = 0; // 0x0
   }
 
-  public static class DvbsFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder> {
+  public static class DvbsFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate);
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int);
@@ -5691,10 +5683,11 @@
     field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0
   }
 
-  public static class DvbtFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder> {
+  public static class DvbtFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setBandwidth(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setConstellation(int);
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setGuardInterval(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHierarchy(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbtFrontendSettings.Builder setHighPriority(boolean);
@@ -5775,10 +5768,6 @@
     field public static final int TYPE_UNDEFINED = 0; // 0x0
   }
 
-  public abstract static class FrontendSettings.Builder<T extends android.media.tv.tuner.frontend.FrontendSettings.Builder<T>> {
-    method @IntRange(from=1) @NonNull public T setFrequency(int);
-  }
-
   public class FrontendStatus {
     method public int getAgc();
     method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpInfo[] getAtsc3PlpInfo();
@@ -5870,9 +5859,10 @@
     field public static final int ROLLOFF_UNDEFINED = 0; // 0x0
   }
 
-  public static class Isdbs3FrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder> {
+  public static class Isdbs3FrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setCodeRate(int);
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setModulation(int);
     method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setRolloff(int);
     method @NonNull public android.media.tv.tuner.frontend.Isdbs3FrontendSettings.Builder setStreamId(int);
@@ -5912,9 +5902,10 @@
     field public static final int STREAM_ID_TYPE_RELATIVE_NUMBER = 1; // 0x1
   }
 
-  public static class IsdbsFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder> {
+  public static class IsdbsFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setCodeRate(int);
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setModulation(int);
     method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setRolloff(int);
     method @NonNull public android.media.tv.tuner.frontend.IsdbsFrontendSettings.Builder setStreamId(int);
@@ -5957,10 +5948,11 @@
     field public static final int MODULATION_UNDEFINED = 0; // 0x0
   }
 
-  public static class IsdbtFrontendSettings.Builder extends android.media.tv.tuner.frontend.FrontendSettings.Builder<android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder> {
+  public static class IsdbtFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setBandwidth(int);
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setCodeRate(int);
+    method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setGuardInterval(int);
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setMode(int);
     method @NonNull public android.media.tv.tuner.frontend.IsdbtFrontendSettings.Builder setModulation(int);
@@ -6091,7 +6083,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
-    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
@@ -6100,7 +6092,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
     method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
@@ -6288,7 +6280,6 @@
     method @Nullable public android.net.Network getNetwork();
     method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
     method public void onAutomaticReconnectDisabled();
-    method public void onBandwidthUpdateRequested();
     method public void onNetworkUnwanted();
     method public void onRemoveKeepalivePacketFilter(int);
     method public void onSaveAcceptUnvalidated(boolean);
@@ -6302,23 +6293,17 @@
     method public void sendNetworkScore(int);
     method public void sendSocketKeepaliveEvent(int, int);
     method public void setConnected();
-    method @Deprecated public void setLegacyExtraInfo(@Nullable String);
-    method @Deprecated public void setLegacySubtype(int, @NonNull String);
     method public void unregister();
     field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
     field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
-    field public final int providerId;
   }
 
   public final class NetworkAgentConfig implements android.os.Parcelable {
     method public int describeContents();
     method public int getLegacyType();
     method @NonNull public String getLegacyTypeName();
-    method @Nullable public String getSubscriberId();
     method public boolean isExplicitlySelected();
-    method public boolean isNat64DetectionEnabled();
     method public boolean isPartialConnectivityAcceptable();
-    method public boolean isProvisioningNotificationEnabled();
     method public boolean isUnvalidatedConnectivityAcceptable();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
@@ -6327,18 +6312,14 @@
   public static class NetworkAgentConfig.Builder {
     ctor public NetworkAgentConfig.Builder();
     method @NonNull public android.net.NetworkAgentConfig build();
-    method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection();
-    method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification();
     method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
     method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
     method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
     method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
     method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean);
   }
 
   public final class NetworkCapabilities implements android.os.Parcelable {
-    method public boolean deduceRestrictedCapability();
     method @NonNull public java.util.List<java.lang.Integer> getAdministratorUids();
     method @Nullable public String getSSID();
     method @NonNull public int[] getTransportTypes();
@@ -6363,27 +6344,9 @@
     field public final android.net.WifiKey wifiKey;
   }
 
-  public class NetworkPolicyManager {
-    method @NonNull public android.telephony.SubscriptionPlan[] getSubscriptionPlans(int, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
-    method public void setSubscriptionOverride(int, int, int, long, @NonNull String);
-    method public void setSubscriptionPlans(int, @NonNull android.telephony.SubscriptionPlan[], @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback);
-    field public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 2; // 0x2
-    field public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1; // 0x1
-  }
-
-  public static class NetworkPolicyManager.SubscriptionCallback {
-    ctor public NetworkPolicyManager.SubscriptionCallback();
-    method public void onSubscriptionOverride(int, int, int);
-    method public void onSubscriptionPlansChanged(int, @NonNull android.telephony.SubscriptionPlan[]);
-  }
-
   public class NetworkProvider {
     ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
-    method @Nullable public android.os.Messenger getMessenger();
-    method @NonNull public String getName();
     method public int getProviderId();
     method public void onNetworkRequested(@NonNull android.net.NetworkRequest, int, int);
     method public void onRequestWithdrawn(@NonNull android.net.NetworkRequest);
@@ -6445,14 +6408,13 @@
   public final class NetworkStats implements android.os.Parcelable {
     ctor public NetworkStats(long, int);
     method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats);
-    method @NonNull public android.net.NetworkStats addValues(@NonNull android.net.NetworkStats.Entry);
+    method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry);
     method public int describeContents();
     method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR;
     field public static final int DEFAULT_NETWORK_NO = 0; // 0x0
     field public static final int DEFAULT_NETWORK_YES = 1; // 0x1
-    field @Nullable public static final String IFACE_ALL;
     field public static final String IFACE_VT = "vt_data0";
     field public static final int METERED_NO = 0; // 0x0
     field public static final int METERED_YES = 1; // 0x1
@@ -6852,21 +6814,17 @@
 
 package android.net.netstats.provider {
 
-  public abstract class AbstractNetworkStatsProvider {
-    ctor public AbstractNetworkStatsProvider();
-    method public abstract void requestStatsUpdate(int);
-    method public abstract void setAlert(long);
-    method public abstract void setLimit(@NonNull String, long);
+  public abstract class NetworkStatsProvider {
+    ctor public NetworkStatsProvider();
+    method public void notifyAlertReached();
+    method public void notifyLimitReached();
+    method public void notifyStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
+    method public abstract void onRequestStatsUpdate(int);
+    method public abstract void onSetAlert(long);
+    method public abstract void onSetLimit(@NonNull String, long);
     field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff
   }
 
-  public class NetworkStatsProviderCallback {
-    method public void onAlertReached();
-    method public void onLimitReached();
-    method public void onStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
-    method public void unregister();
-  }
-
 }
 
 package android.net.sip {
@@ -7490,24 +7448,7 @@
   }
 
   public final class WifiMigration {
-    method @Nullable public static android.net.wifi.WifiMigration.ConfigStoreMigrationData loadFromConfigStore();
     method @NonNull public static android.net.wifi.WifiMigration.SettingsMigrationData loadFromSettings(@NonNull android.content.Context);
-    method public static void removeConfigStore();
-  }
-
-  public static final class WifiMigration.ConfigStoreMigrationData implements android.os.Parcelable {
-    method public int describeContents();
-    method @Nullable public java.util.List<android.net.wifi.WifiConfiguration> getUserSavedNetworkConfigurations();
-    method @Nullable public android.net.wifi.SoftApConfiguration getUserSoftApConfiguration();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiMigration.ConfigStoreMigrationData> CREATOR;
-  }
-
-  public static final class WifiMigration.ConfigStoreMigrationData.Builder {
-    ctor public WifiMigration.ConfigStoreMigrationData.Builder();
-    method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData build();
-    method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
-    method @NonNull public android.net.wifi.WifiMigration.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
   }
 
   public static final class WifiMigration.SettingsMigrationData implements android.os.Parcelable {
@@ -9371,7 +9312,6 @@
     field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
     field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
     field public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled";
-    field public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
     field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
     field public static final String DOZE_ALWAYS_ON = "doze_always_on";
     field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
@@ -10767,27 +10707,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallAttributes> CREATOR;
   }
 
-  public final class CallForwardingInfo implements android.os.Parcelable {
-    ctor public CallForwardingInfo(int, int, @Nullable String, int);
-    method public int describeContents();
-    method @Nullable public String getNumber();
-    method public int getReason();
-    method public int getStatus();
-    method public int getTimeoutSeconds();
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
-    field public static final int REASON_ALL = 4; // 0x4
-    field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
-    field public static final int REASON_BUSY = 1; // 0x1
-    field public static final int REASON_NOT_REACHABLE = 3; // 0x3
-    field public static final int REASON_NO_REPLY = 2; // 0x2
-    field public static final int REASON_UNCONDITIONAL = 0; // 0x0
-    field public static final int STATUS_ACTIVE = 1; // 0x1
-    field public static final int STATUS_FDN_CHECK_FAILURE = 2; // 0x2
-    field public static final int STATUS_INACTIVE = 0; // 0x0
-    field public static final int STATUS_NOT_SUPPORTED = 4; // 0x4
-    field public static final int STATUS_UNKNOWN_ERROR = 3; // 0x3
-  }
-
   public final class CallQuality implements android.os.Parcelable {
     ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int);
     ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean);
@@ -11168,7 +11087,6 @@
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
     method public void onVoiceActivationStateChanged(int);
-    field @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 512; // 0x200
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
@@ -11199,7 +11117,6 @@
   }
 
   public final class PreciseDataConnectionState implements android.os.Parcelable {
-    ctor public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int, @Nullable android.telephony.data.ApnSetting);
     method @Deprecated @NonNull public String getDataConnectionApn();
     method @Deprecated public int getDataConnectionApnTypeBitMask();
     method @Deprecated public int getDataConnectionFailCause();
@@ -11307,7 +11224,6 @@
 
   public class ServiceState implements android.os.Parcelable {
     method public void fillInNotifierBundle(@NonNull android.os.Bundle);
-    method public int getDataNetworkType();
     method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
     method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
     method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int);
@@ -11453,7 +11369,6 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public java.util.List<android.telephony.SmsMessage> getMessagesFromIcc();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSmsCapacityOnIcc();
-    method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
     field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
@@ -11542,7 +11457,6 @@
 
   public class TelephonyManager {
     method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int changeIccLockPassword(@NonNull String, @NonNull String);
     method public int checkCarrierPrivilegesForPackage(String);
     method public int checkCarrierPrivilegesForPackageAnyPhone(String);
     method public void dial(String);
@@ -11553,8 +11467,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
-    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CallForwardingInfo getCallForwarding(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCallWaitingStatus();
+    method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -11566,18 +11479,16 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
     method public String getCdmaPrlVersion();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
     method public int getCurrentPhoneType();
     method public int getCurrentPhoneType(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
     method @Deprecated public boolean getDataEnabled();
     method @Deprecated public boolean getDataEnabled(int);
-    method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
+    method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
-    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getIsimImpu();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Map<java.lang.Integer,java.lang.Integer> getLogicalToPhysicalSlotMapping();
     method public int getMaxNumberOfSimultaneouslyActiveSims();
@@ -11604,12 +11515,10 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataAllowedInVoiceCall();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
@@ -11639,17 +11548,11 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallForwarding(@NonNull android.telephony.CallForwardingInfo);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaRoamingMode(int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaSubscriptionMode(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setIccLockEnabled(boolean, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -11688,10 +11591,6 @@
     field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
     field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
     field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
-    field public static final int CALL_WAITING_STATUS_ACTIVE = 1; // 0x1
-    field public static final int CALL_WAITING_STATUS_INACTIVE = 2; // 0x2
-    field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
-    field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
     field public static final int CARD_POWER_DOWN = 0; // 0x0
     field public static final int CARD_POWER_UP = 1; // 0x1
     field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
@@ -11699,10 +11598,6 @@
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
-    field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
-    field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
-    field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
-    field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff
     field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
     field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
     field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto";
@@ -12324,10 +12219,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
-  public class ImsManager {
-    field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
-  }
-
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
     method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -12593,82 +12484,12 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
-    field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
-    field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
-    field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
-    field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
-    field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
-    field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
-    field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
-    field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
-    field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
-    field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
-    field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
-    field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
-    field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
-    field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
-    field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
-    field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
-    field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
-    field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
-    field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
-    field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
-    field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
-    field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
-    field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
-    field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
-    field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
-    field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
-    field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
-    field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10
-    field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
-    field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
-    field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
-    field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
-    field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
-    field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
-    field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
-    field public static final int KEY_RTT_ENABLED = 66; // 0x42
-    field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
-    field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
-    field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
-    field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
-    field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
-    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
-    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
-    field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
-    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
-    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
-    field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
-    field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
-    field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
-    field public static final int KEY_SMS_FORMAT = 13; // 0xd
-    field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
-    field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
-    field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
-    field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
-    field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
-    field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
-    field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
-    field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
-    field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
     field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
     field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
-    field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
-    field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
-    field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
-    field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
-    field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
-    field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
-    field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
     field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
     field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
-    field public static final int SMS_FORMAT_3GPP = 1; // 0x1
-    field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
     field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
     field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
-    field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
-    field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
   }
 
   public static class ProvisioningManager.Callback {
@@ -12677,56 +12498,6 @@
     method public void onProvisioningStringChanged(int, @NonNull String);
   }
 
-  public final class RcsContactUceCapability implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
-    method @NonNull public android.net.Uri getContactUri();
-    method @Nullable public android.net.Uri getServiceUri(long);
-    method public boolean isCapable(long);
-    method public boolean isCapable(@NonNull String);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
-    field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000
-    field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000
-    field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
-    field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
-    field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
-    field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
-    field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
-    field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
-    field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
-    field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
-    field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
-    field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
-    field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
-    field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
-    field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
-    field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
-    field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
-    field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
-    field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
-    field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
-    field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
-    field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
-    field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
-    field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
-    field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
-    field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
-    field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
-    field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
-    field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
-    field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
-  }
-
-  public static class RcsContactUceCapability.Builder {
-    ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability build();
-  }
-
   public class RcsUceAdapter {
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
   }
@@ -12973,7 +12744,6 @@
     method public int transact(android.os.Bundle);
     method public int updateCallBarring(int, int, String[]);
     method public int updateCallBarringForServiceClass(int, int, String[], int);
-    method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
     method public int updateCallForward(int, int, String, int, int);
     method public int updateCallWaiting(boolean, int);
     method public int updateClip(boolean);
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index 55333cf..10c96a3 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -246,12 +246,6 @@
     
 
 
-ResourceValueFieldName: android.R.array#config_sms_enabled_locking_shift_tables:
-    Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_locking_shift_tables`
-ResourceValueFieldName: android.R.array#config_sms_enabled_single_shift_tables:
-    Expected resource name in `android.R.array` to be in the `fooBarBaz` style, was `config_sms_enabled_single_shift_tables`
-
-
 SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
     
 SamShouldBeLast: android.accounts.AccountManager#addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean):
diff --git a/api/test-current.txt b/api/test-current.txt
index e98a058..5bd1f89 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -83,7 +83,6 @@
     method public static void resumeAppSwitches() throws android.os.RemoteException;
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
     method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
     field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7
     field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
     field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
@@ -857,6 +856,7 @@
     method @NonNull public android.content.integrity.RuleSet getCurrentRuleSet();
     method @NonNull public String getCurrentRuleSetProvider();
     method @NonNull public String getCurrentRuleSetVersion();
+    method @NonNull public java.util.List<java.lang.String> getWhitelistedRuleProviders();
     method public void updateRuleSet(@NonNull android.content.integrity.RuleSet, @NonNull android.content.IntentSender);
     field public static final String EXTRA_STATUS = "android.content.integrity.extra.STATUS";
     field public static final int STATUS_FAILURE = 1; // 0x1
@@ -3434,13 +3434,15 @@
 
 package android.service.quickaccesswallet {
 
-  public interface QuickAccessWalletClient {
+  public interface QuickAccessWalletClient extends java.io.Closeable {
     method public void addWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
+    method public void addWalletServiceEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
     method @NonNull public static android.service.quickaccesswallet.QuickAccessWalletClient create(@NonNull android.content.Context);
     method @Nullable public android.content.Intent createWalletIntent();
     method @Nullable public android.content.Intent createWalletSettingsIntent();
     method public void disconnect();
     method public void getWalletCards(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
+    method public void getWalletCards(@NonNull java.util.concurrent.Executor, @NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
     method public boolean isWalletFeatureAvailable();
     method public boolean isWalletFeatureAvailableWhenDeviceLocked();
     method public boolean isWalletServiceAvailable();
@@ -3735,7 +3737,6 @@
 
   public final class SmsManager {
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int checkSmsShortCodeDestination(String, String);
-    method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String);
     field public static final int SMS_CATEGORY_FREE_SHORT_CODE = 1; // 0x1
     field public static final int SMS_CATEGORY_NOT_SHORT_CODE = 0; // 0x0
     field public static final int SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3; // 0x3
@@ -3756,9 +3757,10 @@
   public class TelephonyManager {
     method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
     method public int checkCarrierPrivilegesForPackage(String);
+    method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
     method public int getCarrierIdListVersion();
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
-    method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
+    method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getDefaultRespondViaMessageApplication();
     method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getEmergencyNumberDbVersion();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
@@ -4034,10 +4036,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR;
   }
 
-  public class ImsManager {
-    field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
-  }
-
   public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
     method @Deprecated @NonNull @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
@@ -4299,82 +4297,12 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
     method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
-    field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
-    field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
-    field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
-    field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
-    field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
-    field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
-    field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
-    field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
-    field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
-    field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
-    field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
-    field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
-    field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
-    field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
-    field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
-    field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
-    field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
-    field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
-    field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
-    field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
-    field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
-    field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
-    field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
-    field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
-    field public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17; // 0x11
-    field public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23; // 0x17
-    field public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22; // 0x16
-    field public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16; // 0x10
-    field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
-    field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
-    field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
-    field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
-    field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
-    field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
-    field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
-    field public static final int KEY_RTT_ENABLED = 66; // 0x42
-    field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
-    field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
-    field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
-    field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
-    field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
-    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
-    field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
-    field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
-    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
-    field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
-    field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
-    field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
-    field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
-    field public static final int KEY_SMS_FORMAT = 13; // 0xd
-    field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
-    field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
-    field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
-    field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
-    field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
-    field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
-    field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
-    field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
-    field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
     field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
     field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
-    field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
-    field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
-    field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
-    field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
-    field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
-    field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
-    field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
     field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
     field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
-    field public static final int SMS_FORMAT_3GPP = 1; // 0x1
-    field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
     field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
     field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
-    field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
-    field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
   }
 
   public static class ProvisioningManager.Callback {
@@ -4383,56 +4311,6 @@
     method public void onProvisioningStringChanged(int, @NonNull String);
   }
 
-  public final class RcsContactUceCapability implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags();
-    method @NonNull public android.net.Uri getContactUri();
-    method @Nullable public android.net.Uri getServiceUri(long);
-    method public boolean isCapable(long);
-    method public boolean isCapable(@NonNull String);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000
-    field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000
-    field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000
-    field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2
-    field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4
-    field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1
-    field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000
-    field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8
-    field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40
-    field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80
-    field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20
-    field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10
-    field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000
-    field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000
-    field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000
-    field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000
-    field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100
-    field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000
-    field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000
-    field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000
-    field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000
-    field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000
-    field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000
-    field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000
-    field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000
-    field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000
-    field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000
-    field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800
-    field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000
-    field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400
-    field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
-  }
-
-  public static class RcsContactUceCapability.Builder {
-    ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String);
-    method @NonNull public android.telephony.ims.RcsContactUceCapability build();
-  }
-
   public class RcsUceAdapter {
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
   }
@@ -4679,7 +4557,6 @@
     method public int transact(android.os.Bundle);
     method public int updateCallBarring(int, int, String[]);
     method public int updateCallBarringForServiceClass(int, int, String[], int);
-    method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
     method public int updateCallForward(int, int, String, int, int);
     method public int updateCallWaiting(boolean, int);
     method public int updateClip(boolean);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ee5a2fa..1cbe150 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -398,6 +398,8 @@
             256  [(module) = "framework"];
         DisplayJankReported display_jank_reported = 257;
         AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
+        SharesheetStarted sharesheet_started = 259 [(module) = "framework"];
+        RankingSelected ranking_selected = 260 [(module) = "framework"];
         SdkExtensionStatus sdk_extension_status = 354;
     }
 
@@ -8486,6 +8488,7 @@
         DEFAULT = 0;
         UNIFORM = 1;
         RARELY_USED = 2;
+        BOOT_TIME_SAMPLING = 3;
     }
 
     // sampling strategy used to collect this message
@@ -8913,3 +8916,64 @@
     // UsageStatsManager.java.
     optional int32 sub_reason = 5;
 }
+
+/**
+* Reports a started sharesheet transaction.
+*
+* Logged from:
+*   frameworks/base/core/java/com/android/internal/app/ChooserActivity.java
+*/
+message SharesheetStarted {
+    // The event_id (as for UiEventReported).
+    optional int32 event_id = 1;
+    // The calling app's package name.
+    optional string package_name = 2;
+    // An identifier to tie together multiple logs relating to the same share event
+    optional int32 instance_id = 3;
+    // The mime type of the share
+    optional string mime_type = 4;
+    // The number of direct targets the calling app is providing that will be shown.
+    optional int32 num_app_provided_direct_targets = 5;
+    // The number of app targets the calling app is providing that will be shown.
+    optional int32 num_app_provided_app_targets = 6;
+    // True if the share originates from the workprofile
+    optional bool is_workprofile = 7;
+
+    enum SharesheetPreviewType {  // Constants from ChooserActivity.java
+        CONTENT_PREVIEW_IMAGE = 1;  // The preview shown in the sharesheet is an image.
+        CONTENT_PREVIEW_FILE = 2;  // The preview shown in the sharesheet is a file.
+        CONTENT_PREVIEW_TEXT = 3;  // The preview shown in the sharesheet is text.
+    }
+    // How the sharesheet preview is presented.
+    optional SharesheetPreviewType previewType = 8;
+
+    enum ResolverActivityIntent { // Intents handled by ResolverActivity.java
+        INTENT_DEFAULT = 0;
+        INTENT_ACTION_VIEW = 1;
+        INTENT_ACTION_EDIT = 2;
+        INTENT_ACTION_SEND = 3;
+        INTENT_ACTION_SENDTO = 4;
+        INTENT_ACTION_SEND_MULTIPLE = 5;
+        INTENT_ACTION_IMAGE_CAPTURE = 6;
+        INTENT_ACTION_MAIN = 7;
+    }
+    // The intent being processed (only SEND and SEND_MULTIPLE are system sharesheet)
+    optional ResolverActivityIntent intentType = 9;
+}
+
+/**
+ * Reports a ranking selection event.
+ *
+ * Logged from:
+ *   frameworks/base/core/java/com/android/internal/app/ChooserActivity.java (sharesheet)
+ */
+message RankingSelected {
+    // The event_id (as for UiEventReported).
+    optional int32 event_id = 1;
+    // The relevant app's package name (can be source or picked package).
+    optional string package_name = 2;
+    // An identifier to tie together multiple logs relating to the same share event.
+    optional int32 instance_id = 3;
+    // Which of the ranked targets got picked, default starting position 0.
+    optional int32 position_picked = 4;
+}
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index e48f378..7b0467c 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -79,420 +79,363 @@
     }
 }
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//class ValueMetricProducerTestHelper {
-//
-// public:
-//    static shared_ptr<LogEvent> createEvent(int64_t eventTimeNs, int64_t value) {
-//        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventTimeNs);
-//        event->write(tagId);
-//        event->write(value);
-//        event->write(value);
-//        event->init();
-//        return event;
-//    }
-//
-//    static sp<ValueMetricProducer> createValueProducerNoConditions(
-//            sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-//        UidMap uidMap;
-//        SimpleAtomMatcher atomMatcher;
-//        atomMatcher.set_atom_id(tagId);
-//        sp<EventMatcherWizard> eventMatcherWizard =
-//                new EventMatcherWizard({new SimpleLogMatchingTracker(
-//                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//        sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//        EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//        EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-//        sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-//                kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                logEventMatcherIndex, eventMatcherWizard, tagId,
-//                bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-//        return valueProducer;
-//    }
-//
-//    static sp<ValueMetricProducer> createValueProducerWithCondition(
-//            sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-//        UidMap uidMap;
-//        SimpleAtomMatcher atomMatcher;
-//        atomMatcher.set_atom_id(tagId);
-//        sp<EventMatcherWizard> eventMatcherWizard =
-//                new EventMatcherWizard({new SimpleLogMatchingTracker(
-//                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//        sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//        EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//        EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-//        sp<ValueMetricProducer> valueProducer =
-//                new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-//                                        eventMatcherWizard, tagId, bucketStartTimeNs,
-//                                        bucketStartTimeNs, pullerManager);
-//        valueProducer->mCondition = ConditionState::kFalse;
-//        return valueProducer;
-//    }
-//
-//    static sp<ValueMetricProducer> createValueProducerWithNoInitialCondition(
-//            sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-//        UidMap uidMap;
-//        SimpleAtomMatcher atomMatcher;
-//        atomMatcher.set_atom_id(tagId);
-//        sp<EventMatcherWizard> eventMatcherWizard =
-//                new EventMatcherWizard({new SimpleLogMatchingTracker(
-//                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//        sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//        EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//        EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//
-//        sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-//                kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-//                bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-//        return valueProducer;
-//    }
-//
-//    static sp<ValueMetricProducer> createValueProducerWithState(
-//            sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
-//            vector<int32_t> slicedStateAtoms,
-//            unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
-//        UidMap uidMap;
-//        SimpleAtomMatcher atomMatcher;
-//        atomMatcher.set_atom_id(tagId);
-//        sp<EventMatcherWizard> eventMatcherWizard =
-//                new EventMatcherWizard({new SimpleLogMatchingTracker(
-//                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//        sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//        EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//        EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
-//        sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-//                kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex,
-//                eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
-//                {}, slicedStateAtoms, stateGroupMap);
-//        return valueProducer;
-//    }
-//
-//    static ValueMetric createMetric() {
-//        ValueMetric metric;
-//        metric.set_id(metricId);
-//        metric.set_bucket(ONE_MINUTE);
-//        metric.mutable_value_field()->set_field(tagId);
-//        metric.mutable_value_field()->add_child()->set_field(2);
-//        metric.set_max_pull_delay_sec(INT_MAX);
-//        return metric;
-//    }
-//
-//    static ValueMetric createMetricWithCondition() {
-//        ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//        metric.set_condition(StringToId("SCREEN_ON"));
-//        return metric;
-//    }
-//
-//    static ValueMetric createMetricWithState(string state) {
-//        ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//        metric.add_slice_by_state(StringToId(state));
-//        return metric;
-//    }
-//};
-//
-///*
-// * Tests that the first bucket works correctly
-// */
-//TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
-//    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-//    int64_t startTimeBase = 11;
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard =
-//            new EventMatcherWizard({new SimpleLogMatchingTracker(
-//                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-//    // statsd started long ago.
-//    // The metric starts in the middle of the bucket
-//    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
-//                                      22, pullerManager);
-//
-//    EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
-//    EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
-//    EXPECT_EQ(60 * NS_PER_SEC + startTimeBase,
-//              valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC));
-//    EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase,
-//              valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC));
-//}
-//
-///*
-// * Tests that the first bucket works correctly
-// */
-//TEST(ValueMetricProducerTest, TestFirstBucket) {
-//    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard =
-//            new EventMatcherWizard({new SimpleLogMatchingTracker(
-//                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-//    // statsd started long ago.
-//    // The metric starts in the middle of the bucket
-//    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard, -1, 5,
-//                                      600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
-//
-//    EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
-//    EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
-//    EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
-//}
-//
-///*
-// * Tests pulled atoms with no conditions
-// */
-//TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
-//    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-//                event->write(tagId);
-//                event->write(3);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }));
-//
-//    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-//    vector<shared_ptr<LogEvent>> allData;
-//    allData.clear();
-//    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-//    event->write(tagId);
-//    event->write(11);
-//    event->init();
-//    allData.push_back(event);
-//
-//    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//    // has one slice
-//    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-//    ValueMetricProducer::Interval curInterval =
-//            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
-//    EXPECT_EQ(11, curBaseInfo.base.long_value);
-//    EXPECT_EQ(false, curInterval.hasValue);
-//    EXPECT_EQ(8, curInterval.value.long_value);
-//    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-//    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//
-//    allData.clear();
-//    event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-//    event->write(tagId);
-//    event->write(23);
-//    event->init();
-//    allData.push_back(event);
-//    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-//    // has one slice
-//    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-//    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
-//    EXPECT_EQ(23, curBaseInfo.base.long_value);
-//    EXPECT_EQ(false, curInterval.hasValue);
-//    EXPECT_EQ(12, curInterval.value.long_value);
-//    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-//    EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
-//    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//    EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-//
-//    allData.clear();
-//    event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
-//    event->write(tagId);
-//    event->write(36);
-//    event->init();
-//    allData.push_back(event);
-//    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-//    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-//    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
-//    EXPECT_EQ(36, curBaseInfo.base.long_value);
-//    EXPECT_EQ(false, curInterval.hasValue);
-//    EXPECT_EQ(13, curInterval.value.long_value);
-//    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-//    EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
-//    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//    EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
-//    EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
-//}
-//
-//TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
-//    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            // Initialize bucket.
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
-//                event->write(tagId);
-//                event->write(1);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }))
-//            // Partial bucket.
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-//                event->write(tagId);
-//                event->write(5);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }));
-//
-//    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
-//
-//    // First bucket ends.
-//    vector<shared_ptr<LogEvent>> allData;
-//    allData.clear();
-//    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-//    event->write(tagId);
-//    event->write(2);
-//    event->init();
-//    allData.push_back(event);
-//    valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
-//
-//    // Partial buckets created in 2nd bucket.
-//    valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
-//
-//    // One full bucket and one partial bucket.
-//    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-//    vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second;
-//    EXPECT_EQ(2UL, buckets.size());
-//    // Full bucket (2 - 1)
-//    EXPECT_EQ(1, buckets[0].values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs);
-//    // Full bucket (5 - 3)
-//    EXPECT_EQ(3, buckets[1].values[0].long_value);
-//    // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2]
-//    EXPECT_EQ(2, buckets[1].mConditionTrueNs);
-//}
-//
-///*
-// * Tests pulled atoms with filtering
-// */
-//TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
-//    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    auto keyValue = atomMatcher.add_field_value_matcher();
-//    keyValue->set_field(1);
-//    keyValue->set_eq_int(3);
-//    sp<EventMatcherWizard> eventMatcherWizard =
-//            new EventMatcherWizard({new SimpleLogMatchingTracker(
-//                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-//                event->write(3);
-//                event->write(3);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }));
-//
-//    sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-//            kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
-//            eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-//
-//    vector<shared_ptr<LogEvent>> allData;
-//    allData.clear();
-//    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-//    event->write(3);
-//    event->write(11);
-//    event->init();
-//    allData.push_back(event);
-//
-//    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//    // has one slice
-//    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-//    ValueMetricProducer::Interval curInterval =
-//            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
-//    EXPECT_EQ(11, curBaseInfo.base.long_value);
-//    EXPECT_EQ(false, curInterval.hasValue);
-//    EXPECT_EQ(8, curInterval.value.long_value);
-//    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-//    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//
-//    allData.clear();
-//    event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
-//    event->write(4);
-//    event->write(23);
-//    event->init();
-//    allData.push_back(event);
-//    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-//    // No new data seen, so data has been cleared.
-//    EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
-//
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
-//    EXPECT_EQ(11, curBaseInfo.base.long_value);
-//    EXPECT_EQ(false, curInterval.hasValue);
-//    EXPECT_EQ(8, curInterval.value.long_value);
-//    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-//    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
-//
-//    allData.clear();
-//    event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
-//    event->write(3);
-//    event->write(36);
-//    event->init();
-//    allData.push_back(event);
-//    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-//    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-//    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//
-//    // the base was reset
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
-//    EXPECT_EQ(36, curBaseInfo.base.long_value);
-//    EXPECT_EQ(false, curInterval.hasValue);
-//    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
-//    EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
-//    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
-//    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
-//}
-//
+class ValueMetricProducerTestHelper {
+public:
+    static sp<ValueMetricProducer> createValueProducerNoConditions(
+            sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
+        UidMap uidMap;
+        SimpleAtomMatcher atomMatcher;
+        atomMatcher.set_atom_id(tagId);
+        sp<EventMatcherWizard> eventMatcherWizard =
+                new EventMatcherWizard({new SimpleLogMatchingTracker(
+                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+        sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+        EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+        EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+        sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+                kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
+                eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+        return valueProducer;
+    }
+
+    static sp<ValueMetricProducer> createValueProducerWithCondition(
+            sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
+        UidMap uidMap;
+        SimpleAtomMatcher atomMatcher;
+        atomMatcher.set_atom_id(tagId);
+        sp<EventMatcherWizard> eventMatcherWizard =
+                new EventMatcherWizard({new SimpleLogMatchingTracker(
+                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+        sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+        EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+        EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+        sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+                kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
+                bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+        valueProducer->mCondition = ConditionState::kFalse;
+        return valueProducer;
+    }
+
+    static sp<ValueMetricProducer> createValueProducerWithNoInitialCondition(
+            sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
+        UidMap uidMap;
+        SimpleAtomMatcher atomMatcher;
+        atomMatcher.set_atom_id(tagId);
+        sp<EventMatcherWizard> eventMatcherWizard =
+                new EventMatcherWizard({new SimpleLogMatchingTracker(
+                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+        sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+        EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+        EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+        sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+                kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
+                bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+        return valueProducer;
+    }
+
+    static sp<ValueMetricProducer> createValueProducerWithState(
+            sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
+            vector<int32_t> slicedStateAtoms,
+            unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
+        UidMap uidMap;
+        SimpleAtomMatcher atomMatcher;
+        atomMatcher.set_atom_id(tagId);
+        sp<EventMatcherWizard> eventMatcherWizard =
+                new EventMatcherWizard({new SimpleLogMatchingTracker(
+                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+        sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+        EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+        EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+        sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+                kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex,
+                eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
+                {}, slicedStateAtoms, stateGroupMap);
+        return valueProducer;
+    }
+
+    static ValueMetric createMetric() {
+        ValueMetric metric;
+        metric.set_id(metricId);
+        metric.set_bucket(ONE_MINUTE);
+        metric.mutable_value_field()->set_field(tagId);
+        metric.mutable_value_field()->add_child()->set_field(2);
+        metric.set_max_pull_delay_sec(INT_MAX);
+        return metric;
+    }
+
+    static ValueMetric createMetricWithCondition() {
+        ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+        metric.set_condition(StringToId("SCREEN_ON"));
+        return metric;
+    }
+
+    static ValueMetric createMetricWithState(string state) {
+        ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+        metric.add_slice_by_state(StringToId(state));
+        return metric;
+    }
+};
+
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+    int64_t startTimeBase = 11;
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    // statsd started long ago.
+    // The metric starts in the middle of the bucket
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
+                                      22, pullerManager);
+
+    EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
+    EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
+    EXPECT_EQ(60 * NS_PER_SEC + startTimeBase,
+              valueProducer.calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC));
+    EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase,
+              valueProducer.calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC));
+}
+
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(ValueMetricProducerTest, TestFirstBucket) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    // statsd started long ago.
+    // The metric starts in the middle of the bucket
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, -1, 5,
+                                      600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
+
+    EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
+    EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
+}
+
+/*
+ * Tests pulled atoms with no conditions
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
+
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval =
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(11, curBaseInfo.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(8, curInterval.value.long_value);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+
+    allData.clear();
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(23, curBaseInfo.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(12, curInterval.value.long_value);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+    EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
+
+    allData.clear();
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(36, curBaseInfo.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(13, curInterval.value.long_value);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+    EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
+    EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs);
+}
+
+TEST(ValueMetricProducerTest, TestPartialBucketCreated) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // Initialize bucket.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
+                return true;
+            }))
+            // Partial bucket.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
+
+    // First bucket ends.
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 2));
+    valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
+
+    // Partial buckets created in 2nd bucket.
+    valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1);
+
+    // One full bucket and one partial bucket.
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second;
+    EXPECT_EQ(2UL, buckets.size());
+    // Full bucket (2 - 1)
+    EXPECT_EQ(1, buckets[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs);
+    // Full bucket (5 - 3)
+    EXPECT_EQ(3, buckets[1].values[0].long_value);
+    // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2]
+    EXPECT_EQ(2, buckets[1].mConditionTrueNs);
+}
+
+/*
+ * Tests pulled atoms with filtering
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    auto keyValue = atomMatcher.add_field_value_matcher();
+    keyValue->set_field(1);
+    keyValue->set_eq_int(3);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+            kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
+            eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11));
+
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval =
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(11, curBaseInfo.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(8, curInterval.value.long_value);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+
+    allData.clear();
+    allData.push_back(CreateTwoValueLogEvent(tagId, bucket3StartTimeNs + 1, 4, 23));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    // No new data seen, so data has been cleared.
+    EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(11, curBaseInfo.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(8, curInterval.value.long_value);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
+
+    allData.clear();
+    allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
+    valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+
+    // the base was reset
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(36, curBaseInfo.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
+    EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value);
+    EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second.back().mConditionTrueNs);
+}
+
 ///*
 // * Tests pulled atoms with no conditions and take absolute value after reset
 // */
-//TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
+// TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_use_absolute_value_on_reset(true);
 //
@@ -512,8 +455,9 @@
 //    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 //    // has one slice
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-//    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+//    ValueMetricProducer::Interval curInterval =
+//    valueProducer->mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
+//    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 //
 //    EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -564,7 +508,7 @@
 ///*
 // * Tests pulled atoms with no conditions and take zero value after reset
 // */
-//TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
+// TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
@@ -582,8 +526,9 @@
 //    valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
 //    // has one slice
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-//    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+//    ValueMetricProducer::Interval curInterval =
+//    valueProducer->mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
+//    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 //
 //    EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -628,7 +573,7 @@
 ///*
 // * Test pulled event with non sliced condition.
 // */
-//TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
+// TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -663,14 +608,16 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
 //
 //    // has one slice
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-//    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+//    ValueMetricProducer::Interval curInterval =
+//    valueProducer->mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
+//    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 //    // startUpdated:false sum:0 start:100
 //    EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -711,7 +658,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1});
 //}
 //
-//TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
+// TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    UidMap uidMap;
@@ -723,8 +670,8 @@
 //    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-//                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
+//                                      eventMatcherWizard, -1, bucketStartTimeNs,
+//                                      bucketStartTimeNs, pullerManager);
 //
 //    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
 //    event1->write(1);
@@ -737,25 +684,21 @@
 //    EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
 //    EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
 //
-//    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
-//    event2->write(1);
-//    event2->write(10);
-//    event2->init();
+//    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 *
+//    NS_PER_SEC); event2->write(1); event2->write(10); event2->init();
 //    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
 //    EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
 //    EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
 //
 //    // Next value should create a new bucket.
-//    shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
-//    event3->write(1);
-//    event3->write(10);
-//    event3->init();
+//    shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 *
+//    NS_PER_SEC); event3->write(1); event3->write(10); event3->init();
 //    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
 //    EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
 //    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
 //}
 //
-//TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
+// TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    UidMap uidMap;
@@ -772,10 +715,8 @@
 //            .WillOnce(Return(true))
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 149);
-//                event->write(tagId);
-//                event->write(120);
-//                event->init();
+//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+//                149); event->write(tagId); event->write(120); event->init();
 //                data->push_back(event);
 //                return true;
 //            }));
@@ -814,7 +755,7 @@
 //                                    {150, bucketSizeNs - 150});
 //}
 //
-//TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
+// TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_split_bucket_for_app_upgrade(false);
 //
@@ -849,7 +790,7 @@
 //    EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
 //}
 //
-//TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
+// TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -865,15 +806,14 @@
 //            }))
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs - 100);
-//                event->write(tagId);
-//                event->write(120);
-//                event->init();
+//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs -
+//                100); event->write(tagId); event->write(120); event->init();
 //                data->push_back(event);
 //                return true;
 //            }));
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
 //
@@ -891,7 +831,7 @@
 //    EXPECT_FALSE(valueProducer->mCondition);
 //}
 //
-//TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
+// TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    UidMap uidMap;
@@ -904,8 +844,8 @@
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //
 //    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-//                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
+//                                      eventMatcherWizard, -1, bucketStartTimeNs,
+//                                      bucketStartTimeNs, pullerManager);
 //
 //    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
 //    event1->write(1);
@@ -918,10 +858,10 @@
 //    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
 //    // has one slice
 //    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-//    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(10, curInterval.value.long_value);
-//    EXPECT_EQ(true, curInterval.hasValue);
+//    ValueMetricProducer::Interval curInterval =
+//    valueProducer.mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo
+//    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(10,
+//    curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue);
 //
 //    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
 //
@@ -934,7 +874,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs});
 //}
 //
-//TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
+// TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    UidMap uidMap;
@@ -947,8 +887,8 @@
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //
 //    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
-//                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
+//                                      eventMatcherWizard, -1, bucketStartTimeNs,
+//                                      bucketStartTimeNs, pullerManager);
 //    valueProducer.mCondition = ConditionState::kFalse;
 //
 //    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -1000,7 +940,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20});
 //}
 //
-//TEST(ValueMetricProducerTest, TestAnomalyDetection) {
+// TEST(ValueMetricProducerTest, TestAnomalyDetection) {
 //    sp<AlarmMonitor> alarmMonitor;
 //    Alert alert;
 //    alert.set_id(101);
@@ -1053,7 +993,8 @@
 //    event5->write(150); // value of interest
 //    event5->init();
 //    shared_ptr<LogEvent> event6
-//            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC);
+//            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 *
+//            NS_PER_SEC);
 //    event6->write(25);
 //    event6->write(160); // value of interest
 //    event6->init();
@@ -1086,7 +1027,7 @@
 //}
 //
 //// Test value metric no condition, the pull on bucket boundary come in time and too late
-//TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
+// TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(true));
@@ -1107,7 +1048,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0];
 //
 //    // startUpdated:true sum:0 start:11
 //    EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1158,7 +1100,7 @@
 // * Test pulled event with non sliced condition. The pull on boundary come late because the alarm
 // * was delivered late.
 // */
-//TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
+// TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -1184,7 +1126,8 @@
 //                return true;
 //            }));
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
 //
@@ -1192,8 +1135,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(100, curBaseInfo.base.long_value);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
@@ -1219,10 +1162,11 @@
 //}
 //
 ///*
-// * Test pulled event with non sliced condition. The pull on boundary come late, after the condition
+// * Test pulled event with non sliced condition. The pull on boundary come late, after the
+// condition
 // * change to false, and then true again. This is due to alarm delivered late.
 // */
-//TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
+// TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -1250,16 +1194,15 @@
 //            // condition becomes true again
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 25);
-//                event->write(tagId);
-//                event->write(130);
-//                event->init();
+//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+//                25); event->write(tagId); event->write(130); event->init();
 //                data->push_back(event);
 //                return true;
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
 //
@@ -1267,7 +1210,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0];
 //    // startUpdated:false sum:0 start:100
 //    EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -1313,7 +1257,7 @@
 //                                    {bucketSizeNs - 8, bucketSizeNs - 24});
 //}
 //
-//TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_aggregation_type(ValueMetric::MIN);
 //
@@ -1327,8 +1271,8 @@
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //
 //    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-//                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
+//                                      eventMatcherWizard, -1, bucketStartTimeNs,
+//                                      bucketStartTimeNs, pullerManager);
 //
 //    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
 //    event1->write(1);
@@ -1357,7 +1301,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs});
 //}
 //
-//TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_aggregation_type(ValueMetric::MAX);
 //
@@ -1371,8 +1315,8 @@
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //
 //    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-//                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
+//                                      eventMatcherWizard, -1, bucketStartTimeNs,
+//                                      bucketStartTimeNs, pullerManager);
 //
 //    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
 //    event1->write(1);
@@ -1403,7 +1347,7 @@
 //    /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */
 //}
 //
-//TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_aggregation_type(ValueMetric::AVG);
 //
@@ -1417,8 +1361,8 @@
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //
 //    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-//                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
+//                                      eventMatcherWizard, -1, bucketStartTimeNs,
+//                                      bucketStartTimeNs, pullerManager);
 //
 //    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
 //    event1->write(1);
@@ -1449,11 +1393,12 @@
 //    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
 //    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
 //
-//    EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value -
+//    EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value
+//    -
 //                         12.5) < epsilon);
 //}
 //
-//TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
+// TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_aggregation_type(ValueMetric::SUM);
 //
@@ -1467,8 +1412,8 @@
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //
 //    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-//                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
+//                                      eventMatcherWizard, -1, bucketStartTimeNs,
+//                                      bucketStartTimeNs, pullerManager);
 //
 //    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
 //    event1->write(1);
@@ -1497,7 +1442,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs});
 //}
 //
-//TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
+// TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_aggregation_type(ValueMetric::MIN);
 //    metric.set_use_diff(true);
@@ -1512,8 +1457,8 @@
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //
 //    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-//                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
+//                                      eventMatcherWizard, -1, bucketStartTimeNs,
+//                                      bucketStartTimeNs, pullerManager);
 //
 //    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
 //    event1->write(1);
@@ -1572,7 +1517,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs});
 //}
 //
-//TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
+// TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.mutable_value_field()->add_child()->set_field(3);
 //    metric.set_aggregation_type(ValueMetric::MIN);
@@ -1588,8 +1533,8 @@
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //
 //    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
-//                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
+//                                      eventMatcherWizard, -1, bucketStartTimeNs,
+//                                      bucketStartTimeNs, pullerManager);
 //
 //    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
 //    event1->write(1);
@@ -1687,7 +1632,7 @@
 ///*
 // * Tests zero default base.
 // */
-//TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
+// TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.mutable_dimensions_in_what()->set_field(tagId);
 //    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -1774,7 +1719,7 @@
 ///*
 // * Tests using zero default base with failed pull.
 // */
-//TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
+// TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.mutable_dimensions_in_what()->set_field(tagId);
 //    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -1885,8 +1830,9 @@
 //    it2 = std::next(valueProducer->mCurrentSlicedBucket.begin());
 //    interval1 = it->second[0];
 //    interval2 = it2->second[0];
-//    baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
-//    baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+//    baseInfo1 =
+//    valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; baseInfo2
+//    = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
 //
 //    EXPECT_EQ(true, baseInfo1.hasBase);
 //    EXPECT_EQ(5, baseInfo1.base.long_value);
@@ -1905,7 +1851,7 @@
 ///*
 // * Tests trim unused dimension key if no new data is seen in an entire bucket.
 // */
-//TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
+// TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.mutable_dimensions_in_what()->set_field(tagId);
 //    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -2023,7 +1969,7 @@
 //    EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
 //}
 //
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2040,15 +1986,16 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
 //    // has one slice
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval& curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo& curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(100, curBaseInfo.base.long_value);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //
@@ -2060,7 +2007,7 @@
 //    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 //}
 //
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2077,7 +2024,8 @@
 //            .WillOnce(Return(false));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
 //
@@ -2085,8 +2033,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval& curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo& curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(100, curBaseInfo.base.long_value);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //    EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
@@ -2100,7 +2048,7 @@
 //    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 //}
 //
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2125,7 +2073,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    // Don't directly set mCondition; the real code never does that. Go through regular code path
 //    // to avoid unexpected behaviors.
@@ -2138,13 +2087,13 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval& curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(false, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 //}
 //
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_condition(StringToId("SCREEN_ON"));
 //    metric.set_max_pull_delay_sec(0);
@@ -2162,7 +2111,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->mCondition = ConditionState::kFalse;
 //
@@ -2171,7 +2121,7 @@
 //    EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
 //}
 //
-//TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
+// TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    UidMap uidMap;
@@ -2196,7 +2146,7 @@
 //    EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size());
 //}
 //
-//TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
+// TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2212,7 +2162,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->mCondition = ConditionState::kFalse;
 //    valueProducer->mHasGlobalBase = false;
@@ -2222,8 +2173,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval& curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(100, curBaseInfo.base.long_value);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2232,7 +2183,7 @@
 ///*
 // * Tests that a bucket is marked invalid when a condition change pull fails.
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2251,7 +2202,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->mCondition = ConditionState::kTrue;
 //
@@ -2286,8 +2238,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval& curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(140, curBaseInfo.base.long_value);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2317,7 +2269,7 @@
 ///*
 // * Tests that a bucket is marked invalid when the guardrail is hit.
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.mutable_dimensions_in_what()->set_field(tagId);
 //    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -2339,7 +2291,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //    valueProducer->mCondition = ConditionState::kFalse;
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 2);
@@ -2385,7 +2338,7 @@
 ///*
 // * Tests that a bucket is marked invalid when the bucket's initial pull fails.
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2412,7 +2365,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->mCondition = ConditionState::kTrue;
 //
@@ -2445,8 +2399,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval& curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(140, curBaseInfo.base.long_value);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2477,7 +2431,7 @@
 // * Tests that a bucket is marked invalid when the bucket's final pull fails
 // * (i.e. failed pull on bucket boundary).
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2504,7 +2458,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->mCondition = ConditionState::kTrue;
 //
@@ -2537,8 +2492,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval& curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(false, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 //
@@ -2564,7 +2519,7 @@
 //    EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
 //}
 //
-//TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
+// TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //    EXPECT_CALL(*pullerManager, Pull(tagId, _))
@@ -2605,7 +2560,7 @@
 //    EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
 //}
 //
-//TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
+// TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2626,14 +2581,15 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval& curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 //
@@ -2647,7 +2603,7 @@
 //    EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 //}
 //
-//TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
+// TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2682,7 +2638,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
 //    valueProducer->onConditionChanged(false, bucketStartTimeNs + 11);
@@ -2690,8 +2647,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval& curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(true, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase);
 //    EXPECT_EQ(true, curInterval.hasValue);
 //    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 //
@@ -2712,7 +2669,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1});
 //}
 //
-//TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
+// TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.mutable_dimensions_in_what()->set_field(tagId);
 //    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
@@ -2733,7 +2690,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
@@ -2764,7 +2722,7 @@
 //    EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 //}
 //
-//TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
+// TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2795,7 +2753,7 @@
 //    ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
 //}
 //
-//TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
+// TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //    EXPECT_CALL(*pullerManager, Pull(tagId, _))
@@ -2815,7 +2773,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //    valueProducer->mCondition = ConditionState::kUnknown;
 //
 //    valueProducer->onConditionChanged(false, bucketStartTimeNs);
@@ -2841,7 +2800,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10});
 //}
 //
-//TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
+// TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_use_diff(false);
 //
@@ -2861,7 +2820,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs});
 //}
 //
-//TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
+// TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2888,7 +2847,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs});
 //}
 //
-//TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
+// TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2916,7 +2875,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs});
 //}
 //
-//TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
+// TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2935,7 +2894,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
 //    valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
@@ -2955,7 +2915,7 @@
 //}
 //
 //// TODO: b/145705635 fix or delete this test
-//TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
+// TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -2969,18 +2929,19 @@
 //            // 2nd condition change.
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
-//                return true;
+//                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs,
+//                1)); return true;
 //            }))
 //            // 3rd condition change.
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1));
-//                return true;
+//                data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs,
+//                1)); return true;
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //    valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
 //
 //    vector<shared_ptr<LogEvent>> allData;
@@ -3002,7 +2963,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
 //}
 //
-//TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
+// TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    UidMap uidMap;
@@ -3043,7 +3004,7 @@
 //    EXPECT_EQ(0, report.value_metrics().data_size());
 //}
 //
-//TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
+// TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    UidMap uidMap;
@@ -3095,7 +3056,7 @@
 //    EXPECT_EQ(0, report.value_metrics().data(0).bucket_info(0).bucket_num());
 //}
 //
-//TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
+// TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //
 //    UidMap uidMap;
@@ -3147,7 +3108,7 @@
 //    EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
 //}
 //
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 //    metric.set_use_diff(false);
 //
@@ -3163,7 +3124,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs});
 //}
 //
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //    metric.set_use_diff(false);
 //
@@ -3184,7 +3145,8 @@
 //                return true;
 //            }));
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //    valueProducer->mCondition = ConditionState::kFalse;
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
@@ -3193,8 +3155,8 @@
 //    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
 //    ValueMetricProducer::Interval curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(false, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
 //    EXPECT_EQ(true, curInterval.hasValue);
 //    EXPECT_EQ(20, curInterval.value.long_value);
 //
@@ -3210,7 +3172,7 @@
 //    EXPECT_EQ(false, curInterval.hasValue);
 //}
 //
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //    metric.set_use_diff(false);
 //
@@ -3224,7 +3186,8 @@
 //                return true;
 //            }));
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //    valueProducer->mCondition = ConditionState::kFalse;
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
@@ -3237,18 +3200,19 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8});
 //    ValueMetricProducer::Interval curInterval =
 //            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-//    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
-//    EXPECT_EQ(false, curBaseInfo.hasBase);
+//    ValueMetricProducer::BaseInfo curBaseInfo =
+//    valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase);
 //    EXPECT_EQ(false, curInterval.hasValue);
 //}
 //
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //    metric.set_use_diff(false);
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //    valueProducer->mCondition = ConditionState::kFalse;
 //
 //    // Now the alarm is delivered. Condition is off though.
@@ -3260,7 +3224,7 @@
 //    assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {});
 //}
 //
-//TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
+// TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //    metric.set_use_diff(false);
 //
@@ -3275,7 +3239,8 @@
 //            }))
 //            .WillOnce(Return(false));
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //    valueProducer->mCondition = ConditionState::kFalse;
 //
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
@@ -3300,7 +3265,7 @@
 // * - the dump latency must be FAST
 // */
 //
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3317,7 +3282,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    // Condition change event.
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 20);
@@ -3348,7 +3314,7 @@
 // * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late condition
 // * change event (i.e. the condition change occurs in the wrong bucket).
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3365,7 +3331,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    // Condition change event.
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
@@ -3408,7 +3375,7 @@
 // * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late accumulate
 // * event (i.e. the accumulate events call occurs in the wrong bucket).
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3426,16 +3393,15 @@
 //            // Dump report requested.
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 100);
-//                event->write("field1");
-//                event->write(15);
-//                event->init();
+//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+//                100); event->write("field1"); event->write(15); event->init();
 //                data->push_back(event);
 //                return true;
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    // Condition change event.
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
@@ -3485,7 +3451,7 @@
 // * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
 // * when a metric is initialized.
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3503,10 +3469,8 @@
 //            // Dump report requested.
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 100);
-//                event->write("field1");
-//                event->write(15);
-//                event->init();
+//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs +
+//                100); event->write("field1"); event->write(15); event->init();
 //                data->push_back(event);
 //                return true;
 //            }));
@@ -3545,7 +3509,7 @@
 // * Test that PULL_FAILED dump reason is logged due to a pull failure in
 // * #pullAndMatchEventsLocked.
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3564,7 +3528,8 @@
 //            .WillOnce(Return(false));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    // Condition change event.
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
@@ -3596,7 +3561,7 @@
 // * Test that MULTIPLE_BUCKETS_SKIPPED dump reason is logged when a log event
 // * skips over more than one bucket.
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3624,7 +3589,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    // Condition change event.
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
@@ -3635,7 +3601,8 @@
 //    // Check dump report.
 //    ProtoOutputStream output;
 //    std::set<string> strSet;
-//    valueProducer->onDumpReport(bucket4StartTimeNs + 1000, true /* include recent buckets */, true,
+//    valueProducer->onDumpReport(bucket4StartTimeNs + 1000, true /* include recent buckets */,
+//    true,
 //                                NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
 //
 //    StatsLogReport report = outputStreamToProto(&output);
@@ -3658,7 +3625,7 @@
 // * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
 // * is smaller than the "min_bucket_size_nanos" specified in the metric config.
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //    metric.set_min_bucket_size_nanos(10000000000);  // 10 seconds
 //
@@ -3687,7 +3654,8 @@
 //            }));
 //
 //    sp<ValueMetricProducer> valueProducer =
-//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric);
+//            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager,
+//            metric);
 //
 //    // Condition change event.
 //    valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
@@ -3718,7 +3686,7 @@
 ///*
 // * Test multiple bucket drop events in the same bucket.
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3772,7 +3740,7 @@
 // * Test that the number of logged bucket drop events is capped at the maximum.
 // * The maximum is currently 10 and is set in MetricProducer::maxDropEventsReached().
 // */
-//TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
+// TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 //
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3800,10 +3768,8 @@
 //            .WillOnce(Return(false))
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 220);
-//                event->write("field1");
-//                event->write(10);
-//                event->init();
+//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs +
+//                220); event->write("field1"); event->write(10); event->init();
 //                data->push_back(event);
 //                return true;
 //            }));
@@ -3896,7 +3862,7 @@
 // * - Using diff
 // * - Second field is value field
 // */
-//TEST(ValueMetricProducerTest, TestSlicedState) {
+// TEST(ValueMetricProducerTest, TestSlicedState) {
 //    // Set up ValueMetricProducer.
 //    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
@@ -3987,7 +3953,8 @@
 //    EXPECT_EQ(2, it->second[0].value.long_value);
 //
 //    // Bucket status after screen state change ON->OFF.
-//    screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+//    screenEvent =
+//    CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
 //                                                bucketStartTimeNs + 10);
 //    StateManager::getInstance().onLogEvent(*screenEvent);
 //    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4067,9 +4034,10 @@
 // * - Using diff
 // * - Second field is value field
 // */
-//TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
+// TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
 //    // Set up ValueMetricProducer.
-//    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
+//    ValueMetric metric =
+//    ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
 //    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 //    EXPECT_CALL(*pullerManager, Pull(tagId, _))
 //            // ValueMetricProducer initialized.
@@ -4189,7 +4157,8 @@
 //    EXPECT_EQ(2, it->second[0].value.long_value);
 //
 //    // Bucket status after screen state change VR->OFF.
-//    screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+//    screenEvent =
+//    CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
 //                                                bucketStartTimeNs + 15);
 //    StateManager::getInstance().onLogEvent(*screenEvent);
 //    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4243,9 +4212,10 @@
 // * - Using diff
 // * - Second field is value field
 // */
-//TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
+// TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
 //    // Set up ValueMetricProducer.
-//    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
+//    ValueMetric metric =
+//    ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
 //    metric.mutable_dimensions_in_what()->set_field(tagId);
 //    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
 //
@@ -4311,10 +4281,8 @@
 //            // Uid 1 process state change from Foreground -> Background
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
-//                event->write(1 /* uid */);
-//                event->write(13);
-//                event->init();
+//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+//                20); event->write(1 /* uid */); event->write(13); event->init();
 //                data->push_back(event);
 //
 //                // This event should be skipped.
@@ -4328,10 +4296,8 @@
 //            // Uid 1 process state change from Background -> Foreground
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 40);
-//                event->write(1 /* uid */);
-//                event->write(17);
-//                event->init();
+//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+//                40); event->write(1 /* uid */); event->write(17); event->init();
 //                data->push_back(event);
 //
 //                // This event should be skipped.
@@ -4345,10 +4311,8 @@
 //            // Dump report pull.
 //            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
 //                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
-//                event->write(2 /* uid */);
-//                event->write(20);
-//                event->init();
+//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs +
+//                50); event->write(2 /* uid */); event->write(20); event->init();
 //                data->push_back(event);
 //
 //                event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
@@ -4393,7 +4357,8 @@
 //
 //    // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
 //    auto uidProcessEvent = CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucketStartTimeNs + 20);
+//            1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucketStartTimeNs +
+//            20);
 //    StateManager::getInstance().onLogEvent(*uidProcessEvent);
 //    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
 //    // Base for dimension key {uid 1}.
@@ -4420,7 +4385,8 @@
 //
 //    // Bucket status after uid 2 process state change kStateUnknown -> Background.
 //    uidProcessEvent = CreateUidProcessStateChangedEvent(
-//            2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucketStartTimeNs + 40);
+//            2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucketStartTimeNs +
+//            40);
 //    StateManager::getInstance().onLogEvent(*uidProcessEvent);
 //    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
 //    // Base for dimension key {uid 1}.
@@ -4499,7 +4465,8 @@
 //
 //    // Bucket status after uid 1 process state change from Foreground -> Background.
 //    uidProcessEvent = CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucket2StartTimeNs + 20);
+//            1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucket2StartTimeNs +
+//            20);
 //    StateManager::getInstance().onLogEvent(*uidProcessEvent);
 //
 //    EXPECT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4533,7 +4500,8 @@
 //
 //    // Bucket status after uid 1 process state change Background->Foreground.
 //    uidProcessEvent = CreateUidProcessStateChangedEvent(
-//            1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucket2StartTimeNs + 40);
+//            1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucket2StartTimeNs +
+//            40);
 //    StateManager::getInstance().onLogEvent(*uidProcessEvent);
 //
 //    EXPECT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index e2eee03..58eeed3 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -410,6 +410,127 @@
     return dimensions;
 }
 
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+                                            int32_t value2) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+    AStatsEvent_writeInt32(statsEvent, value1);
+    AStatsEvent_writeInt32(statsEvent, value2);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+
+    return logEvent;
+}
+//
+void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+                            int32_t value2) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+    AStatsEvent_writeInt32(statsEvent, value1);
+    AStatsEvent_writeInt32(statsEvent, value2);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+}
+
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+                                              int32_t value2, int32_t value3) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+    AStatsEvent_writeInt32(statsEvent, value1);
+    AStatsEvent_writeInt32(statsEvent, value2);
+    AStatsEvent_writeInt32(statsEvent, value3);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+
+    return logEvent;
+}
+
+void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+                              int32_t value2, int32_t value3) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+    AStatsEvent_writeInt32(statsEvent, value1);
+    AStatsEvent_writeInt32(statsEvent, value2);
+    AStatsEvent_writeInt32(statsEvent, value3);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+}
+
+shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+
+    return logEvent;
+}
+
+void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
+                                 int32_t value) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+}
+
+shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+
+    return logEvent;
+}
+
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
         uint64_t timestampNs, const android::view::DisplayStateEnum state) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 4371015..4c25ca3 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -164,6 +164,29 @@
 FieldMatcher CreateAttributionUidDimensions(const int atomId,
                                             const std::vector<Position>& positions);
 
+shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+                                            int32_t value2);
+
+void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+                            int32_t value2);
+
+shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1,
+                                              int32_t value2, int32_t value3);
+
+void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
+                              int32_t value2, int32_t value3);
+
+// The repeated value log event helpers create a log event with two int fields, both
+// set to the same value. This is useful for testing metrics that are only interested
+// in the value of the second field but still need the first field to be populated.
+std::shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs,
+                                                      int32_t value);
+
+void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs,
+                                 int32_t value);
+
+std::shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs);
+
 // Create log event for screen state changed.
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
         uint64_t timestampNs, const android::view::DisplayStateEnum state);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1a92b75..f4ee8fa 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1315,7 +1315,8 @@
 
         /**
          * @return The in-memory or loaded icon that represents the current state of this task.
-         * @deprecated This call is no longer supported.
+         * @deprecated This call is no longer supported. The caller should keep track of any icons
+         *             it sets for the task descriptions internally.
          */
         @Deprecated
         public Bitmap getIcon() {
@@ -4242,8 +4243,6 @@
      *         {@code false} otherwise.
      * @hide
      */
-    @SystemApi
-    @TestApi
     @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION)
     public boolean updateMccMncConfiguration(@NonNull String mcc, @NonNull String mnc) {
         if (mcc == null || mnc == null) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ec11043..489a0de 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -34,6 +34,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Activity manager local system service interface.
@@ -124,6 +125,12 @@
     public abstract int getUidProcessState(int uid);
 
     /**
+     * Get a map of pid and package name that process of that pid Android/data and Android/obb
+     * directory is not mounted to lowerfs.
+     */
+    public abstract Map<Integer, String> getProcessesWithPendingBindMounts(int userId);
+
+    /**
      * @return {@code true} if system is ready, {@code false} otherwise.
      */
     public abstract boolean isSystemReady();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 679c83a..fa4aa19 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -696,6 +696,10 @@
     public static final int SAMPLING_STRATEGY_RARELY_USED =
             FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
 
+    /** @hide */
+    public static final int SAMPLING_STRATEGY_BOOT_TIME_SAMPLING =
+            FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__BOOT_TIME_SAMPLING;
+
     /**
      * Strategies used for message sampling
      * @hide
@@ -704,7 +708,8 @@
     @IntDef(prefix = {"SAMPLING_STRATEGY_"}, value = {
             SAMPLING_STRATEGY_DEFAULT,
             SAMPLING_STRATEGY_UNIFORM,
-            SAMPLING_STRATEGY_RARELY_USED
+            SAMPLING_STRATEGY_RARELY_USED,
+            SAMPLING_STRATEGY_BOOT_TIME_SAMPLING
     })
     public @interface SamplingStrategy {}
 
@@ -1364,7 +1369,6 @@
     @SystemApi
     public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
     /** @hide Read device identifiers */
-    @SystemApi
     public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
     /** @hide Query all packages on device */
     public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 56e6aee..9ccfe8d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1899,8 +1899,9 @@
     public Object getSystemService(String name) {
         // Check incorrect Context usage.
         if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
-            final String errorMessage = "Tried to access visual service " + name
-                    + " from a non-visual Context.";
+            final String errorMessage = "Tried to access visual service "
+                    + SystemServiceRegistry.getSystemServiceClassName(name)
+                    + " from a non-visual Context. ";
             final String message = "Visual services, such as WindowManager, WallpaperService or "
                     + "LayoutInflater should be accessed from Activity or other visual Context. "
                     + "Use an Activity or a Context created with "
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 061e5ff..864af3d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7526,9 +7526,11 @@
                     p,
                     bindResult);
             addExtras(mBuilder.mN.extras);
-            // also update the end margin if there is an image
-            contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
-                    bindResult.getIconMarginEnd());
+            if (!CONVERSATION_LAYOUT_ENABLED) {
+                // also update the end margin if there is an image
+                contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
+                        bindResult.getIconMarginEnd());
+            }
             contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
                     mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
                             : mBuilder.resolveContrastColor(p));
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index cbbdf63..811b9c0 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1015,14 +1015,15 @@
     }
 
     /**
+     * Returns the currently applied notification policy.
+     *
      * <p>
-     *  Gets the currently applied notification policy. If {@link #getCurrentInterruptionFilter}
-     * is equal to {@link #INTERRUPTION_FILTER_ALL}, then the consolidated notification policy
-     * will match the default notification policy returned by {@link #getNotificationPolicy}.
+     * If {@link #getCurrentInterruptionFilter} is equal to {@link #INTERRUPTION_FILTER_ALL},
+     * then the consolidated notification policy will match the default notification policy
+     * returned by {@link #getNotificationPolicy}.
      * </p>
      */
-    @Nullable
-    public NotificationManager.Policy getConsolidatedNotificationPolicy() {
+    public @NonNull NotificationManager.Policy getConsolidatedNotificationPolicy() {
         INotificationManager service = getService();
         try {
             return service.getConsolidatedNotificationPolicy();
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 42563b5..1329fa4 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -399,7 +399,6 @@
      *                 {@code false}, re-enables expansion of the status bar.
      * @hide
      */
-    @SystemApi
     @TestApi
     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
     public void setDisabledForSimNetworkLock(boolean disabled) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index a3bcc9c..e8f30df 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -19,6 +19,7 @@
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.ContextImpl.ServiceInitializationState;
 import android.app.admin.DevicePolicyManager;
@@ -227,6 +228,8 @@
             new ArrayMap<Class<?>, String>();
     private static final Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
             new ArrayMap<String, ServiceFetcher<?>>();
+    private static final Map<String, String> SYSTEM_SERVICE_CLASS_NAMES = new ArrayMap<>();
+
     private static int sServiceCacheSize;
 
     private static volatile boolean sInitializing;
@@ -1389,6 +1392,19 @@
             @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
         SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
         SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
+        SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
+    }
+
+    /**
+     * Returns system service class name by system service name. This method is mostly an inverse of
+     * {@link #getSystemServiceName(Class)}
+     *
+     * @return system service class name. {@code null} if service name is invalid.
+     * @hide
+     */
+    @Nullable
+    public static String getSystemServiceClassName(@NonNull String name) {
+        return SYSTEM_SERVICE_CLASS_NAMES.get(name);
     }
 
     /**
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 1a84547..b2dd0ef 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -23,6 +23,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.os.Parcel;
 import android.os.RemoteException;
@@ -161,6 +162,13 @@
      */
     public @WindowConfiguration.ActivityType int topActivityType;
 
+    /**
+     * The {@link ActivityInfo} of the top activity in this task.
+     * @hide
+     */
+    @Nullable
+    public ActivityInfo topActivityInfo;
+
     TaskInfo() {
         // Do nothing
     }
@@ -217,8 +225,11 @@
         token = IWindowContainer.Stub.asInterface(source.readStrongBinder());
         topActivityType = source.readInt();
         pictureInPictureParams = source.readInt() != 0
-            ? PictureInPictureParams.CREATOR.createFromParcel(source)
-            : null;
+                ? PictureInPictureParams.CREATOR.createFromParcel(source)
+                : null;
+        topActivityInfo = source.readInt() != 0
+                ? ActivityInfo.CREATOR.createFromParcel(source)
+                : null;
     }
 
     /**
@@ -262,6 +273,12 @@
             dest.writeInt(1);
             pictureInPictureParams.writeToParcel(dest, flags);
         }
+        if (topActivityInfo == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            topActivityInfo.writeToParcel(dest, flags);
+        }
     }
 
     @Override
@@ -278,6 +295,7 @@
                 + " resizeMode=" + resizeMode
                 + " token=" + token
                 + " topActivityType=" + topActivityType
-                + " pictureInPictureParams=" + pictureInPictureParams;
+                + " pictureInPictureParams=" + pictureInPictureParams
+                + " topActivityInfo=" + topActivityInfo;
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 2240359..5b8ee71 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5215,6 +5215,10 @@
      * <p>Because this method might take several seconds to complete, it should only be called from
      * a worker thread. This method returns {@code null} when called from the main thread.
      *
+     * <p>This method is not thread-safe, calling it from multiple threads at the same time will
+     * result in undefined behavior. If the calling thread is interrupted while the invocation is
+     * in-flight, it will eventually terminate and return {@code null}.
+     *
      * <p>Note: If the provided {@code alias} is of an existing alias, all former grants that apps
      * have been given to access the key and certificates associated with this alias will be
      * revoked.
@@ -5732,6 +5736,10 @@
         throwIfParentInstance("isAlwaysOnVpnLockdownEnabled");
         if (mService != null) {
             try {
+                // Starting from Android R, the caller can pass the permission check in
+                // DevicePolicyManagerService if it holds android.permission.MAINLINE_NETWORK_STACK.
+                // Note that the android.permission.MAINLINE_NETWORK_STACK is a signature permission
+                // which is used by the NetworkStack mainline module.
                 return mService.isAlwaysOnVpnLockdownEnabled(admin);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -11987,7 +11995,8 @@
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with
      * @param timeoutMillis Maximum time the profile is allowed to be off in milliseconds or 0 if
-     *        not limited.
+     *        not limited. The minimum non-zero value corresponds to 72 hours. If an admin sets a
+     *        smaller non-zero vaulue, 72 hours will be set instead.
      * @throws IllegalStateException if the profile owner doesn't have an activity that handles
      *        {@link #ACTION_CHECK_POLICY_COMPLIANCE}
      * @see #setPersonalAppsSuspended
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 5b98188..d6e7762 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -29,10 +29,10 @@
 import android.net.DataUsageRequest;
 import android.net.INetworkStatsService;
 import android.net.NetworkIdentity;
+import android.net.NetworkStack;
 import android.net.NetworkTemplate;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
-import android.net.netstats.provider.NetworkStatsProviderWrapper;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
@@ -527,32 +527,53 @@
 
     /**
      * Registers a custom provider of {@link android.net.NetworkStats} to provide network statistics
-     * to the system. To unregister, invoke {@link NetworkStatsProviderCallback#unregister()}.
+     * to the system. To unregister, invoke {@link #unregisterNetworkStatsProvider}.
      * Note that no de-duplication of statistics between providers is performed, so each provider
-     * must only report network traffic that is not being reported by any other provider.
+     * must only report network traffic that is not being reported by any other provider. Also note
+     * that the provider cannot be re-registered after unregistering.
      *
      * @param tag a human readable identifier of the custom network stats provider. This is only
      *            used for debugging.
-     * @param provider the subclass of {@link AbstractNetworkStatsProvider} that needs to be
+     * @param provider the subclass of {@link NetworkStatsProvider} that needs to be
      *                 registered to the system.
-     * @return a {@link NetworkStatsProviderCallback}, which can be used to report events to the
-     *         system or unregister the provider.
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
-    @NonNull public NetworkStatsProviderCallback registerNetworkStatsProvider(
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STATS_PROVIDER,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+    @NonNull public void registerNetworkStatsProvider(
             @NonNull String tag,
-            @NonNull AbstractNetworkStatsProvider provider) {
+            @NonNull NetworkStatsProvider provider) {
         try {
-            final NetworkStatsProviderWrapper wrapper = new NetworkStatsProviderWrapper(provider);
-            return new NetworkStatsProviderCallback(
-                    mService.registerNetworkStatsProvider(tag, wrapper));
+            if (provider.getProviderCallbackBinder() != null) {
+                throw new IllegalArgumentException("provider is already registered");
+            }
+            final INetworkStatsProviderCallback cbBinder =
+                    mService.registerNetworkStatsProvider(tag, provider.getProviderBinder());
+            provider.setProviderCallbackBinder(cbBinder);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
-        // Unreachable code, but compiler doesn't know about it.
-        return null;
+    }
+
+    /**
+     * Unregisters an instance of {@link NetworkStatsProvider}.
+     *
+     * @param provider the subclass of {@link NetworkStatsProvider} that needs to be
+     *                 unregistered to the system.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_STATS_PROVIDER,
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
+    @NonNull public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) {
+        try {
+            provider.getProviderCallbackBinderOrThrow().unregister();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
     }
 
     private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2c701b4..0d66198 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -288,25 +288,25 @@
      */
     public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;
     /**
-     * The reason for restricting the app is unknown or undefined.
+     * The reason the system forced the app into the bucket is unknown or undefined.
      * @hide
      */
-    public static final int REASON_SUB_RESTRICT_UNDEFINED = 0x0000;
+    public static final int REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED = 0;
     /**
      * The app was unnecessarily using system resources (battery, memory, etc) in the background.
      * @hide
      */
-    public static final int REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE = 0x0001;
+    public static final int REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE = 1 << 0;
     /**
      * The app was deemed to be intentionally abusive.
      * @hide
      */
-    public static final int REASON_SUB_RESTRICT_ABUSE = 0x0002;
+    public static final int REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE = 1 << 1;
     /**
      * The app was displaying buggy behavior.
      * @hide
      */
-    public static final int REASON_SUB_RESTRICT_BUGGY = 0x0003;
+    public static final int REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY = 1 << 2;
 
 
     /** @hide */
@@ -322,6 +322,17 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface StandbyBuckets {}
 
+    /** @hide */
+    @IntDef(flag = true, prefix = {"REASON_SUB_FORCED_SYSTEM_FLAG_FLAG_"}, value = {
+            REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED,
+            REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
+            REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
+            REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SystemForcedReasons {
+    }
+
     /**
      * Observer id of the registered observer for the group of packages that reached the usage
      * time limit. Included as an extra in the PendingIntent that was registered.
@@ -1053,6 +1064,7 @@
 
     /** @hide */
     public static String reasonToString(int standbyReason) {
+        final int subReason = standbyReason & REASON_SUB_MASK;
         StringBuilder sb = new StringBuilder();
         switch (standbyReason & REASON_MAIN_MASK) {
             case REASON_MAIN_DEFAULT:
@@ -1060,19 +1072,8 @@
                 break;
             case REASON_MAIN_FORCED_BY_SYSTEM:
                 sb.append("s");
-                switch (standbyReason & REASON_SUB_MASK) {
-                    case REASON_SUB_RESTRICT_ABUSE:
-                        sb.append("-ra");
-                        break;
-                    case REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE:
-                        sb.append("-rbru");
-                        break;
-                    case REASON_SUB_RESTRICT_BUGGY:
-                        sb.append("-rb");
-                        break;
-                    case REASON_SUB_RESTRICT_UNDEFINED:
-                        sb.append("-r");
-                        break;
+                if (subReason > 0) {
+                    sb.append("-").append(Integer.toBinaryString(subReason));
                 }
                 break;
             case REASON_MAIN_FORCED_BY_USER:
@@ -1080,7 +1081,7 @@
                 break;
             case REASON_MAIN_PREDICTED:
                 sb.append("p");
-                switch (standbyReason & REASON_SUB_MASK) {
+                switch (subReason) {
                     case REASON_SUB_PREDICTED_RESTORED:
                         sb.append("-r");
                         break;
@@ -1091,7 +1092,7 @@
                 break;
             case REASON_MAIN_USAGE:
                 sb.append("u");
-                switch (standbyReason & REASON_SUB_MASK) {
+                switch (subReason) {
                     case REASON_SUB_USAGE_SYSTEM_INTERACTION:
                         sb.append("-si");
                         break;
diff --git a/core/java/android/content/integrity/AppIntegrityManager.java b/core/java/android/content/integrity/AppIntegrityManager.java
index 9f95d4d..2869abb 100644
--- a/core/java/android/content/integrity/AppIntegrityManager.java
+++ b/core/java/android/content/integrity/AppIntegrityManager.java
@@ -25,6 +25,8 @@
 import android.content.pm.ParceledListSlice;
 import android.os.RemoteException;
 
+import java.util.List;
+
 /**
  * Class for pushing rules used to check the integrity of app installs.
  *
@@ -121,4 +123,21 @@
             throw e.rethrowAsRuntimeException();
         }
     }
+
+    /**
+     * Get the package names of all whitelisted rule providers.
+     *
+     * <p>Warning: this method is only used for tests.
+     *
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    public List<String> getWhitelistedRuleProviders() {
+        try {
+            return mManager.getWhitelistedRuleProviders();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/content/integrity/IAppIntegrityManager.aidl b/core/java/android/content/integrity/IAppIntegrityManager.aidl
index 4714ad7..94197bb 100644
--- a/core/java/android/content/integrity/IAppIntegrityManager.aidl
+++ b/core/java/android/content/integrity/IAppIntegrityManager.aidl
@@ -19,6 +19,7 @@
 import android.content.integrity.Rule;
 import android.content.IntentSender;
 import android.content.pm.ParceledListSlice;
+import java.util.List;
 
 /** @hide */
 interface IAppIntegrityManager {
@@ -26,4 +27,5 @@
     String getCurrentRuleSetVersion();
     String getCurrentRuleSetProvider();
     ParceledListSlice<Rule> getCurrentRules();
+    List<String> getWhitelistedRuleProviders();
 }
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 4e4897f..22516f0 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -460,10 +460,7 @@
         /**
          * If non-null, return only the specified shortcuts by locus ID.  When setting this field,
          * a package name must also be set with {@link #setPackage}.
-         *
-         * @hide
          */
-        @SystemApi
         @NonNull
         public ShortcutQuery setLocusIds(@Nullable List<LocusId> locusIds) {
             mLocusIds = locusIds;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ea941cc..05ff830 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -557,7 +557,6 @@
      * Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app.
      * @hide
      */
-    @SystemApi
     public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS =  0x20000000;
 
     /**
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 5fbf0da..9906331 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -657,7 +657,9 @@
     public int accuracy;
 
     /**
-     * The time in nanosecond at which the event happened
+     * The time in nanoseconds at which the event happened. For a given sensor,
+     * each new sensor event should be monotonically increasing using the same
+     * time base as {@link android.os.SystemClock#elapsedRealtimeNanos()}.
      */
     public long timestamp;
 
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 1b6c1ee..7e4d68d 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -20,6 +20,7 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
 import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -71,6 +72,7 @@
 import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowInsets;
+import android.view.WindowInsets.Side;
 import android.view.WindowManager;
 import android.view.animation.AnimationUtils;
 import android.view.autofill.AutofillId;
@@ -1246,7 +1248,8 @@
                 Context.LAYOUT_INFLATER_SERVICE);
         mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
                 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
-        mWindow.getWindow().getAttributes().setFitInsetsTypes(WindowInsets.Type.statusBars());
+        mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
+        mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
 
         // IME layout should always be inset by navigation bar, no matter its current visibility,
         // unless automotive requests it, since automotive may hide the navigation bar.
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index d009144..6f0a4f9 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -136,7 +136,7 @@
          * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID},
          * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}.
          *
-         * @see android.net.NetworkCapabilities#CAPABILITY_VALIDATED
+         * @see android.net.NetworkCapabilities#NET_CAPABILITY_VALIDATED
          */
         @NetworkValidationResult
         public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
@@ -233,8 +233,8 @@
          * Constructor for ConnectivityReport.
          *
          * <p>Apps should obtain instances through {@link
-         * ConnectivityDiagnosticsCallback#onConnectivityReport} instead of instantiating their own
-         * instances (unless for testing purposes).
+         * ConnectivityDiagnosticsCallback#onConnectivityReportAvailable} instead of instantiating
+         * their own instances (unless for testing purposes).
          *
          * @param network The Network for which this ConnectivityReport applies
          * @param reportTimestamp The timestamp for the report
@@ -368,7 +368,14 @@
 
     /** Class that includes information for a suspected data stall on a specific Network */
     public static final class DataStallReport implements Parcelable {
+        /**
+         * Indicates that the Data Stall was detected using DNS events.
+         */
         public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+
+        /**
+         * Indicates that the Data Stall was detected using TCP metrics.
+         */
         public static final int DETECTION_METHOD_TCP_METRICS = 2;
 
         /** @hide */
@@ -615,10 +622,10 @@
 
         /** @hide */
         @VisibleForTesting
-        public void onConnectivityReport(@NonNull ConnectivityReport report) {
+        public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
             Binder.withCleanCallingIdentity(() -> {
                 mExecutor.execute(() -> {
-                    mCb.onConnectivityReport(report);
+                    mCb.onConnectivityReportAvailable(report);
                 });
             });
         }
@@ -659,7 +666,7 @@
          *
          * @param report The ConnectivityReport containing information about a connectivity check
          */
-        public void onConnectivityReport(@NonNull ConnectivityReport report) {}
+        public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {}
 
         /**
          * Called when the platform suspects a data stall on some Network.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fc6954f..81735ac 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3222,7 +3222,9 @@
 
     /** {@hide} - returns the factory serial number */
     @UnsupportedAppUsage
-    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_FACTORY})
     public int registerNetworkFactory(Messenger messenger, String name) {
         try {
             return mService.registerNetworkFactory(messenger, name);
@@ -3233,7 +3235,9 @@
 
     /** {@hide} */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_FACTORY})
     public void unregisterNetworkFactory(Messenger messenger) {
         try {
             mService.unregisterNetworkFactory(messenger);
@@ -3253,7 +3257,9 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_FACTORY})
     public int registerNetworkProvider(@NonNull NetworkProvider provider) {
         if (provider.getProviderId() != NetworkProvider.ID_NONE) {
             throw new IllegalStateException("NetworkProviders can only be registered once");
@@ -3276,7 +3282,9 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_FACTORY})
     public void unregisterNetworkProvider(@NonNull NetworkProvider provider) {
         try {
             mService.unregisterNetworkProvider(provider.getMessenger());
@@ -3288,7 +3296,9 @@
 
 
     /** @hide exposed via the NetworkProvider class. */
-    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_FACTORY})
     public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
         try {
             mService.declareNetworkRequestUnfulfillable(request);
@@ -3306,7 +3316,9 @@
      * Register a NetworkAgent with ConnectivityService.
      * @return Network corresponding to NetworkAgent.
      */
-    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_FACTORY})
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkAgentConfig config) {
         return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
@@ -3317,7 +3329,9 @@
      * Register a NetworkAgent with ConnectivityService.
      * @return Network corresponding to NetworkAgent.
      */
-    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_FACTORY})
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
         try {
diff --git a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
index 3a161bf..82b64a9 100644
--- a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
+++ b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl
@@ -22,7 +22,7 @@
 
 /** @hide */
 oneway interface IConnectivityDiagnosticsCallback {
-    void onConnectivityReport(in ConnectivityDiagnosticsManager.ConnectivityReport report);
+    void onConnectivityReportAvailable(in ConnectivityDiagnosticsManager.ConnectivityReport report);
     void onDataStallSuspected(in ConnectivityDiagnosticsManager.DataStallReport report);
     void onNetworkConnectivityReported(in Network n, boolean hasConnectivity);
 }
\ No newline at end of file
diff --git a/core/java/android/net/ITestNetworkManager.aidl b/core/java/android/net/ITestNetworkManager.aidl
index d586038..2a863ad 100644
--- a/core/java/android/net/ITestNetworkManager.aidl
+++ b/core/java/android/net/ITestNetworkManager.aidl
@@ -33,7 +33,7 @@
     TestNetworkInterface createTapInterface();
 
     void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
-            in IBinder binder);
+            in int[] administratorUids, in IBinder binder);
 
     void teardownTestNetwork(int netId);
 }
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index fef353f..5c754a1 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -78,6 +78,7 @@
     /**
      * The ID of the {@link NetworkProvider} that created this object, or
      * {@link NetworkProvider#ID_NONE} if unknown.
+     * @hide
      */
     public final int providerId;
 
@@ -584,6 +585,7 @@
      *
      * @deprecated this is for backward compatibility only.
      * @param legacySubtype the legacy subtype.
+     * @hide
      */
     @Deprecated
     public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
@@ -608,6 +610,7 @@
      *
      * @deprecated this is for backward compatibility only.
      * @param extraInfo the ExtraInfo.
+     * @hide
      */
     @Deprecated
     public void setLegacyExtraInfo(@Nullable final String extraInfo) {
@@ -711,6 +714,7 @@
     /**
      * Called when ConnectivityService request a bandwidth update. The parent factory
      * shall try to overwrite this method and produce a bandwidth update if capable.
+     * @hide
      */
     public void onBandwidthUpdateRequested() {
         pollLceData();
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
index 7e2db4a..ca9328a 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -108,6 +108,7 @@
     /**
      *
      * @return whether the sign in to network notification is enabled by this configuration.
+     * @hide
      */
     public boolean isProvisioningNotificationEnabled() {
         return !provisioningNotificationDisabled;
@@ -122,6 +123,7 @@
 
     /**
      * @return the subscriber ID, or null if none.
+     * @hide
      */
     @Nullable
     public String getSubscriberId() {
@@ -138,6 +140,7 @@
 
     /**
      * @return whether NAT64 prefix detection is enabled.
+     * @hide
      */
     public boolean isNat64DetectionEnabled() {
         return !skip464xlat;
@@ -247,6 +250,7 @@
          * Sets the subscriber ID for this network.
          *
          * @return this builder, to facilitate chaining.
+         * @hide
          */
         @NonNull
         public Builder setSubscriberId(@Nullable String subscriberId) {
@@ -259,6 +263,7 @@
          * and reduce idle traffic on networks that are known to be IPv6-only without a NAT64.
          *
          * @return this builder, to facilitate chaining.
+         * @hide
          */
         @NonNull
         public Builder disableNat64Detection() {
@@ -271,6 +276,7 @@
          * perform its own carrier-specific provisioning procedure.
          *
          * @return this builder, to facilitate chaining.
+         * @hide
          */
         @NonNull
         public Builder disableProvisioningNotification() {
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 83f9980..116e343 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -613,7 +613,6 @@
      * @return {@code true} if the network should be restricted.
      * @hide
      */
-    @SystemApi
     public boolean deduceRestrictedCapability() {
         // Check if we have any capability that forces the network to be restricted.
         final boolean forceRestrictedCapability =
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 14442a2..1922b6d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -21,7 +21,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -56,7 +55,6 @@
  * @hide
  */
 @SystemService(Context.NETWORK_POLICY_SERVICE)
-@SystemApi
 public class NetworkPolicyManager {
 
     /* POLICY_* are masks and can be ORed, although currently they are not.*/
@@ -162,11 +160,13 @@
 
     /**
      * Mask used to check if an override value is marked as unmetered.
+     * @hide
      */
     public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1 << 0;
 
     /**
      * Mask used to check if an override value is marked as congested.
+     * @hide
      */
     public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 1 << 1;
 
@@ -294,7 +294,6 @@
 
     /** @hide */
     @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
-    @SystemApi
     public void registerSubscriptionCallback(@NonNull SubscriptionCallback callback) {
         if (callback == null) {
             throw new NullPointerException("Callback cannot be null.");
@@ -309,7 +308,6 @@
 
     /** @hide */
     @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY)
-    @SystemApi
     public void unregisterSubscriptionCallback(@NonNull SubscriptionCallback callback) {
         if (callback == null) {
             throw new NullPointerException("Callback cannot be null.");
@@ -373,6 +371,7 @@
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first
      * @param callingPackage the name of the package making the call.
+     * @hide
      */
     public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
             @SubscriptionOverrideMask int overrideValue, long timeoutMillis,
@@ -391,6 +390,7 @@
      * @param subId the subscriber this relationship applies to.
      * @param plans the list of plans.
      * @param callingPackage the name of the package making the call
+     * @hide
      */
     public void setSubscriptionPlans(int subId, @NonNull SubscriptionPlan[] plans,
             @NonNull String callingPackage) {
@@ -406,6 +406,7 @@
      *
      * @param subId the subscriber to get the subscription plans for.
      * @param callingPackage the name of the package making the call.
+     * @hide
      */
     @NonNull
     public SubscriptionPlan[] getSubscriptionPlans(int subId, @NonNull String callingPackage) {
@@ -549,7 +550,6 @@
     }
 
     /** @hide */
-    @SystemApi
     public static class SubscriptionCallback {
         /**
          * Notify clients of a new override about a given subscription.
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index 2c0e4aa..418d691 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -106,10 +106,12 @@
     }
 
     // TODO: consider adding a register() method so ConnectivityManager does not need to call this.
+    /** @hide */
     public @Nullable Messenger getMessenger() {
         return mMessenger;
     }
 
+    /** @hide */
     public @NonNull String getName() {
         return mName;
     }
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 2f536ff..9c1fb41 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -21,7 +21,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
@@ -58,9 +57,12 @@
 public final class NetworkStats implements Parcelable {
     private static final String TAG = "NetworkStats";
 
-    /** {@link #iface} value when interface details unavailable. */
-    @SuppressLint("CompileTimeConstant")
+    /**
+     * {@link #iface} value when interface details unavailable.
+     * @hide
+     */
     @Nullable public static final String IFACE_ALL = null;
+
     /**
      * Virtual network interface for video telephony. This is for VT data usage counting
      * purpose.
@@ -248,7 +250,13 @@
     @UnsupportedAppUsage
     private long[] operations;
 
-    /** @hide */
+    /**
+     * Basic element of network statistics. Contains the number of packets and number of bytes
+     * transferred on both directions in a given set of conditions. See
+     * {@link Entry#Entry(String, int, int, int, int, int, int, long, long, long, long, long)}.
+     *
+     * @hide
+     */
     @SystemApi
     public static class Entry {
         /** @hide */
@@ -319,6 +327,35 @@
                     rxBytes, rxPackets, txBytes, txPackets, operations);
         }
 
+        /**
+         * Construct a {@link Entry} object by giving statistics of packet and byte transferred on
+         * both direction, and associated with a set of given conditions.
+         *
+         * @param iface interface name of this {@link Entry}. Or null if not specified.
+         * @param uid uid of this {@link Entry}. {@link #UID_TETHERING} if this {@link Entry} is
+         *            for tethering. Or {@link #UID_ALL} if this {@link NetworkStats} is only
+         *            counting iface stats.
+         * @param set usage state of this {@link Entry}. Should be one of the following
+         *            values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}.
+         * @param tag tag of this {@link Entry}.
+         * @param metered metered state of this {@link Entry}. Should be one of the following
+         *                values: {link #METERED_YES}, {link #METERED_NO}.
+         * @param roaming roaming state of this {@link Entry}. Should be one of the following
+         *                values: {link #ROAMING_YES}, {link #ROAMING_NO}.
+         * @param defaultNetwork default network status of this {@link Entry}. Should be one
+         *                       of the following values: {link #DEFAULT_NETWORK_YES},
+         *                       {link #DEFAULT_NETWORK_NO}.
+         * @param rxBytes Number of bytes received for this {@link Entry}. Statistics should
+         *                represent the contents of IP packets, including IP headers.
+         * @param rxPackets Number of packets received for this {@link Entry}. Statistics should
+         *                  represent the contents of IP packets, including IP headers.
+         * @param txBytes Number of bytes transmitted for this {@link Entry}. Statistics should
+         *                represent the contents of IP packets, including IP headers.
+         * @param txPackets Number of bytes transmitted for this {@link Entry}. Statistics should
+         *                  represent the contents of IP packets, including IP headers.
+         * @param operations count of network operations performed for this {@link Entry}. This can
+         *                   be used to derive bytes-per-operation.
+         */
         public Entry(@Nullable String iface, int uid, @State int set, int tag,
                 @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork,
                 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
@@ -466,7 +503,7 @@
         NetworkStats.Entry entry = null;
         for (int i = 0; i < size; i++) {
             entry = getValues(i, entry);
-            clone.addEntry(entry);
+            clone.insertEntry(entry);
         }
         return clone;
     }
@@ -493,26 +530,26 @@
 
     /** @hide */
     @VisibleForTesting
-    public NetworkStats addIfaceValues(
+    public NetworkStats insertEntry(
             String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) {
-        return addEntry(
+        return insertEntry(
                 iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L);
     }
 
     /** @hide */
     @VisibleForTesting
-    public NetworkStats addEntry(String iface, int uid, int set, int tag, long rxBytes,
+    public NetworkStats insertEntry(String iface, int uid, int set, int tag, long rxBytes,
             long rxPackets, long txBytes, long txPackets, long operations) {
-        return addEntry(new Entry(
+        return insertEntry(new Entry(
                 iface, uid, set, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
     }
 
     /** @hide */
     @VisibleForTesting
-    public NetworkStats addEntry(String iface, int uid, int set, int tag, int metered, int roaming,
-            int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
-            long operations) {
-        return addEntry(new Entry(
+    public NetworkStats insertEntry(String iface, int uid, int set, int tag, int metered,
+            int roaming, int defaultNetwork, long rxBytes, long rxPackets, long txBytes,
+            long txPackets, long operations) {
+        return insertEntry(new Entry(
                 iface, uid, set, tag, metered, roaming, defaultNetwork, rxBytes, rxPackets,
                 txBytes, txPackets, operations));
     }
@@ -522,7 +559,7 @@
      * object can be recycled across multiple calls.
      * @hide
      */
-    public NetworkStats addEntry(Entry entry) {
+    public NetworkStats insertEntry(Entry entry) {
         if (size >= capacity) {
             final int newLength = Math.max(size, 10) * 3 / 2;
             iface = Arrays.copyOf(iface, newLength);
@@ -665,7 +702,7 @@
                 entry.roaming, entry.defaultNetwork);
         if (i == -1) {
             // only create new entry when positive contribution
-            addEntry(entry);
+            insertEntry(entry);
         } else {
             rxBytes[i] += entry.rxBytes;
             rxPackets[i] += entry.rxPackets;
@@ -684,7 +721,7 @@
      * @param entry the {@link Entry} to add.
      * @return a new constructed {@link NetworkStats} object that contains the result.
      */
-    public @NonNull NetworkStats addValues(@NonNull Entry entry) {
+    public @NonNull NetworkStats addEntry(@NonNull Entry entry) {
         return this.clone().combineValues(entry);
     }
 
@@ -1003,7 +1040,7 @@
                 entry.operations = Math.max(entry.operations, 0);
             }
 
-            result.addEntry(entry);
+            result.insertEntry(entry);
         }
 
         return result;
diff --git a/core/java/android/net/TestNetworkManager.java b/core/java/android/net/TestNetworkManager.java
index 4ac4a69..c3284df 100644
--- a/core/java/android/net/TestNetworkManager.java
+++ b/core/java/android/net/TestNetworkManager.java
@@ -16,6 +16,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -53,6 +54,19 @@
         }
     }
 
+    private void setupTestNetwork(
+            @NonNull String iface,
+            @Nullable LinkProperties lp,
+            boolean isMetered,
+            @NonNull int[] administratorUids,
+            @NonNull IBinder binder) {
+        try {
+            mService.setupTestNetwork(iface, lp, isMetered, administratorUids, binder);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Sets up a capability-limited, testing-only network for a given interface
      *
@@ -66,11 +80,7 @@
     public void setupTestNetwork(
             @NonNull LinkProperties lp, boolean isMetered, @NonNull IBinder binder) {
         Preconditions.checkNotNull(lp, "Invalid LinkProperties");
-        try {
-            mService.setupTestNetwork(lp.getInterfaceName(), lp, isMetered, binder);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        setupTestNetwork(lp.getInterfaceName(), lp, isMetered, new int[0], binder);
     }
 
     /**
@@ -82,11 +92,21 @@
      */
     @TestApi
     public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
-        try {
-            mService.setupTestNetwork(iface, null, true, binder);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        setupTestNetwork(iface, null, true, new int[0], binder);
+    }
+
+    /**
+     * Sets up a capability-limited, testing-only network for a given interface with the given
+     * administrator UIDs.
+     *
+     * @param iface the name of the interface to be used for the Network LinkProperties.
+     * @param administratorUids The administrator UIDs to be used for the test-only network
+     * @param binder A binder object guarding the lifecycle of this test network.
+     * @hide
+     */
+    public void setupTestNetwork(
+            @NonNull String iface, @NonNull int[] administratorUids, @NonNull IBinder binder) {
+        setupTestNetwork(iface, null, true, administratorUids, binder);
     }
 
     /**
diff --git a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java b/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
deleted file mode 100644
index 740aa92..0000000
--- a/core/java/android/net/netstats/provider/AbstractNetworkStatsProvider.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.netstats.provider;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.net.NetworkStats;
-
-/**
- * A base class that allows external modules to implement a custom network statistics provider.
- * @hide
- */
-@SystemApi
-public abstract class AbstractNetworkStatsProvider {
-    /**
-     * A value used by {@link #setLimit} and {@link #setAlert} indicates there is no limit.
-     */
-    public static final int QUOTA_UNLIMITED = -1;
-
-    /**
-     * Called by {@code NetworkStatsService} when global polling is needed. Custom
-     * implementation of providers MUST respond to it by calling
-     * {@link NetworkStatsProviderCallback#onStatsUpdated} within one minute. Responding
-     * later than this may cause the stats to be dropped.
-     *
-     * @param token a positive number identifying the new state of the system under which
-     *              {@link NetworkStats} have to be gathered from now on. When this is called,
-     *              custom implementations of providers MUST report the latest stats with the
-     *              previous token, under which stats were being gathered so far.
-     */
-    public abstract void requestStatsUpdate(int token);
-
-    /**
-     * Called by {@code NetworkStatsService} when setting the interface quota for the specified
-     * upstream interface. When this is called, the custom implementation should block all egress
-     * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
-     * been reached, and MUST respond to it by calling
-     * {@link NetworkStatsProviderCallback#onLimitReached()}.
-     *
-     * @param iface the interface requiring the operation.
-     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
-     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
-     */
-    public abstract void setLimit(@NonNull String iface, long quotaBytes);
-
-    /**
-     * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
-     * MUST call {@link NetworkStatsProviderCallback#onAlertReached()} when {@code quotaBytes} bytes
-     * have been reached. Unlike {@link #setLimit(String, long)}, the custom implementation should
-     * not block all egress packets.
-     *
-     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
-     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
-     */
-    public abstract void setAlert(long quotaBytes);
-}
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
index 55b3d4e..4078b24 100644
--- a/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
+++ b/core/java/android/net/netstats/provider/INetworkStatsProvider.aidl
@@ -22,7 +22,7 @@
  * @hide
  */
 oneway interface INetworkStatsProvider {
-    void requestStatsUpdate(int token);
-    void setLimit(String iface, long quotaBytes);
-    void setAlert(long quotaBytes);
+    void onRequestStatsUpdate(int token);
+    void onSetLimit(String iface, long quotaBytes);
+    void onSetAlert(long quotaBytes);
 }
diff --git a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
index 3ea9318..bd336dd 100644
--- a/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
+++ b/core/java/android/net/netstats/provider/INetworkStatsProviderCallback.aidl
@@ -24,8 +24,8 @@
  * @hide
  */
 oneway interface INetworkStatsProviderCallback {
-    void onStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
-    void onAlertReached();
-    void onLimitReached();
+    void notifyStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
+    void notifyAlertReached();
+    void notifyLimitReached();
     void unregister();
 }
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProvider.java b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
new file mode 100644
index 0000000..7639d22
--- /dev/null
+++ b/core/java/android/net/netstats/provider/NetworkStatsProvider.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netstats.provider;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.NetworkStats;
+import android.os.RemoteException;
+
+/**
+ * A base class that allows external modules to implement a custom network statistics provider.
+ * @hide
+ */
+@SystemApi
+public abstract class NetworkStatsProvider {
+    /**
+     * A value used by {@link #onSetLimit} and {@link #onSetAlert} indicates there is no limit.
+     */
+    public static final int QUOTA_UNLIMITED = -1;
+
+    @NonNull private final INetworkStatsProvider mProviderBinder =
+            new INetworkStatsProvider.Stub() {
+
+        @Override
+        public void onRequestStatsUpdate(int token) {
+            NetworkStatsProvider.this.onRequestStatsUpdate(token);
+        }
+
+        @Override
+        public void onSetLimit(String iface, long quotaBytes) {
+            NetworkStatsProvider.this.onSetLimit(iface, quotaBytes);
+        }
+
+        @Override
+        public void onSetAlert(long quotaBytes) {
+            NetworkStatsProvider.this.onSetAlert(quotaBytes);
+        }
+    };
+
+    // The binder given by the service when successfully registering. Only null before registering,
+    // never null once non-null.
+    @Nullable
+    private INetworkStatsProviderCallback mProviderCbBinder;
+
+    /**
+     * Return the binder invoked by the service and redirect function calls to the overridden
+     * methods.
+     * @hide
+     */
+    @NonNull
+    public INetworkStatsProvider getProviderBinder() {
+        return mProviderBinder;
+    }
+
+    /**
+     * Store the binder that was returned by the service when successfully registering. Note that
+     * the provider cannot be re-registered. Hence this method can only be called once per provider.
+     *
+     * @hide
+     */
+    public void setProviderCallbackBinder(@NonNull INetworkStatsProviderCallback binder) {
+        if (mProviderCbBinder != null) {
+            throw new IllegalArgumentException("provider is already registered");
+        }
+        mProviderCbBinder = binder;
+    }
+
+    /**
+     * Get the binder that was returned by the service when successfully registering. Or null if the
+     * provider was never registered.
+     *
+     * @hide
+     */
+    @Nullable
+    public INetworkStatsProviderCallback getProviderCallbackBinder() {
+        return mProviderCbBinder;
+    }
+
+    /**
+     * Get the binder that was returned by the service when successfully registering. Throw an
+     * {@link IllegalStateException} if the provider is not registered.
+     *
+     * @hide
+     */
+    @NonNull
+    public INetworkStatsProviderCallback getProviderCallbackBinderOrThrow() {
+        if (mProviderCbBinder == null) {
+            throw new IllegalStateException("the provider is not registered");
+        }
+        return mProviderCbBinder;
+    }
+
+    /**
+     * Notify the system of new network statistics.
+     *
+     * Send the network statistics recorded since the last call to {@link #notifyStatsUpdated}. Must
+     * be called as soon as possible after {@link NetworkStatsProvider#onRequestStatsUpdate(int)}
+     * being called. Responding later increases the probability stats will be dropped. The
+     * provider can also call this whenever it wants to reports new stats for any reason.
+     * Note that the system will not necessarily immediately propagate the statistics to
+     * reflect the update.
+     *
+     * @param token the token under which these stats were gathered. Providers can call this method
+     *              with the current token as often as they want, until the token changes.
+     *              {@see NetworkStatsProvider#onRequestStatsUpdate()}
+     * @param ifaceStats the {@link NetworkStats} per interface to be reported.
+     *                   The provider should not include any traffic that is already counted by
+     *                   kernel interface counters.
+     * @param uidStats the same stats as above, but counts {@link NetworkStats}
+     *                 per uid.
+     */
+    public void notifyStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
+            @NonNull NetworkStats uidStats) {
+        try {
+            getProviderCallbackBinderOrThrow().notifyStatsUpdated(token, ifaceStats, uidStats);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Notify system that the quota set by {@code onSetAlert} has been reached.
+     */
+    public void notifyAlertReached() {
+        try {
+            getProviderCallbackBinderOrThrow().notifyAlertReached();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Notify system that the quota set by {@code onSetLimit} has been reached.
+     */
+    public void notifyLimitReached() {
+        try {
+            getProviderCallbackBinderOrThrow().notifyLimitReached();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Called by {@code NetworkStatsService} when it requires to know updated stats.
+     * The provider MUST respond by calling {@link #notifyStatsUpdated} as soon as possible.
+     * Responding later increases the probability stats will be dropped. Memory allowing, the
+     * system will try to take stats into account up to one minute after calling
+     * {@link #onRequestStatsUpdate}.
+     *
+     * @param token a positive number identifying the new state of the system under which
+     *              {@link NetworkStats} have to be gathered from now on. When this is called,
+     *              custom implementations of providers MUST tally and report the latest stats with
+     *              the previous token, under which stats were being gathered so far.
+     */
+    public abstract void onRequestStatsUpdate(int token);
+
+    /**
+     * Called by {@code NetworkStatsService} when setting the interface quota for the specified
+     * upstream interface. When this is called, the custom implementation should block all egress
+     * packets on the {@code iface} associated with the provider when {@code quotaBytes} bytes have
+     * been reached, and MUST respond to it by calling
+     * {@link NetworkStatsProvider#notifyLimitReached()}.
+     *
+     * @param iface the interface requiring the operation.
+     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
+     */
+    public abstract void onSetLimit(@NonNull String iface, long quotaBytes);
+
+    /**
+     * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
+     * MUST call {@link NetworkStatsProvider#notifyAlertReached()} when {@code quotaBytes} bytes
+     * have been reached. Unlike {@link #onSetLimit(String, long)}, the custom implementation should
+     * not block all egress packets.
+     *
+     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
+     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no alert.
+     */
+    public abstract void onSetAlert(long quotaBytes);
+}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java b/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
deleted file mode 100644
index e17a8ee..0000000
--- a/core/java/android/net/netstats/provider/NetworkStatsProviderCallback.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.netstats.provider;
-
-import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.net.NetworkStats;
-import android.os.RemoteException;
-
-/**
- * A callback class that allows callers to report events to the system.
- * @hide
- */
-@SystemApi
-@SuppressLint("CallbackMethodName")
-public class NetworkStatsProviderCallback {
-    @NonNull private final INetworkStatsProviderCallback mBinder;
-
-    /** @hide */
-    public NetworkStatsProviderCallback(@NonNull INetworkStatsProviderCallback binder) {
-        mBinder = binder;
-    }
-
-    /**
-     * Notify the system of new network statistics.
-     *
-     * Send the network statistics recorded since the last call to {@link #onStatsUpdated}. Must be
-     * called within one minute of {@link AbstractNetworkStatsProvider#requestStatsUpdate(int)}
-     * being called. The provider can also call this whenever it wants to reports new stats for any
-     * reason. Note that the system will not necessarily immediately propagate the statistics to
-     * reflect the update.
-     *
-     * @param token the token under which these stats were gathered. Providers can call this method
-     *              with the current token as often as they want, until the token changes.
-     *              {@see AbstractNetworkStatsProvider#requestStatsUpdate()}
-     * @param ifaceStats the {@link NetworkStats} per interface to be reported.
-     *                   The provider should not include any traffic that is already counted by
-     *                   kernel interface counters.
-     * @param uidStats the same stats as above, but counts {@link NetworkStats}
-     *                 per uid.
-     */
-    public void onStatsUpdated(int token, @NonNull NetworkStats ifaceStats,
-            @NonNull NetworkStats uidStats) {
-        try {
-            mBinder.onStatsUpdated(token, ifaceStats, uidStats);
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
-     * Notify system that the quota set by {@code setAlert} has been reached.
-     */
-    public void onAlertReached() {
-        try {
-            mBinder.onAlertReached();
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
-     * Notify system that the quota set by {@code setLimit} has been reached.
-     */
-    public void onLimitReached() {
-        try {
-            mBinder.onLimitReached();
-        } catch (RemoteException e) {
-            e.rethrowAsRuntimeException();
-        }
-    }
-
-    /**
-     * Unregister the provider and the referencing callback.
-     */
-    public void unregister() {
-        try {
-            mBinder.unregister();
-        } catch (RemoteException e) {
-            // Ignore error.
-        }
-    }
-}
diff --git a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java b/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
deleted file mode 100644
index 4bf7c9b..0000000
--- a/core/java/android/net/netstats/provider/NetworkStatsProviderWrapper.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.netstats.provider;
-
-import android.annotation.NonNull;
-
-/**
- * A wrapper class of {@link INetworkStatsProvider} that hides the binder interface from exposing
- * to outer world.
- *
- * @hide
- */
-public class NetworkStatsProviderWrapper extends INetworkStatsProvider.Stub {
-    @NonNull final AbstractNetworkStatsProvider mProvider;
-
-    public NetworkStatsProviderWrapper(AbstractNetworkStatsProvider provider) {
-        mProvider = provider;
-    }
-
-    @Override
-    public void requestStatsUpdate(int token) {
-        mProvider.requestStatsUpdate(token);
-    }
-
-    @Override
-    public void setLimit(@NonNull String iface, long quotaBytes) {
-        mProvider.setLimit(iface, quotaBytes);
-    }
-
-    @Override
-    public void setAlert(long quotaBytes) {
-        mProvider.setAlert(quotaBytes);
-    }
-}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index f2fb5b2..ae65f1d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -23,6 +23,9 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyManager;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
@@ -88,6 +91,46 @@
     private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT,
             "/apex");
 
+    /**
+     * Scoped Storage is on by default. However, it is not strictly enforced and there are multiple
+     * ways to opt out of scoped storage:
+     * <ul>
+     * <li>Target Sdk < Q</li>
+     * <li>Target Sdk = Q and has `requestLegacyExternalStorage` set in AndroidManifest.xml</li>
+     * <li>Target Sdk > Q: Upgrading from an app that was opted out of scoped storage and has
+     * `preserveLegacyExternalStorage` set in AndroidManifest.xml</li>
+     * </ul>
+     * This flag is enabled for all apps by default as Scoped Storage is enabled by default.
+     * Developers can disable this flag to opt out of Scoped Storage and have legacy storage
+     * workflow.
+     *
+     * Note: {@code FORCE_ENABLE_SCOPED_STORAGE} should also be disabled for apps to opt out of
+     * scoped storage.
+     * Note: This flag is also used in {@code com.android.providers.media.LocalCallingIdentity}.
+     * Any modifications to this flag should be reflected there as well.
+     * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+     */
+    @ChangeId
+    private static final long DEFAULT_SCOPED_STORAGE = 149924527L;
+
+    /**
+     * Setting this flag strictly enforces Scoped Storage regardless of:
+     * <ul>
+     * <li>The value of Target Sdk</li>
+     * <li>The value of `requestLegacyExternalStorage` in AndroidManifest.xml</li>
+     * <li>The value of `preserveLegacyExternalStorage` in AndroidManifest.xml</li>
+     * </ul>
+     *
+     * Note: {@code DEFAULT_SCOPED_STORAGE} should also be enabled for apps to be enforced into
+     * scoped storage.
+     * Note: This flag is also used in {@code com.android.providers.media.LocalCallingIdentity}.
+     * Any modifications to this flag should be reflected there as well.
+     * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+     */
+    @ChangeId
+    @Disabled
+    private static final long FORCE_ENABLE_SCOPED_STORAGE = 132649864L;
+
     @UnsupportedAppUsage
     private static UserEnvironment sCurrentUser;
     private static boolean sUserRequired;
@@ -1191,12 +1234,13 @@
     }
 
     /**
-     * Returns whether the primary shared/external storage media is a legacy
-     * view that includes files not owned by the app.
+     * Returns whether the shared/external storage media is a
+     * legacy view that includes files not owned by the app.
      * <p>
      * This value may be different from the value requested by
      * {@code requestLegacyExternalStorage} in the app's manifest, since an app
-     * may inherit its legacy state based on when it was first installed.
+     * may inherit its legacy state based on when it was first installed, target sdk and other
+     * factors.
      * <p>
      * Non-legacy apps can continue to discover and read media belonging to
      * other apps via {@link android.provider.MediaStore}.
@@ -1207,18 +1251,19 @@
     }
 
     /**
-     * Returns whether the shared/external storage media at the given path is a
+     * Returns whether the shared/external storage media is a
      * legacy view that includes files not owned by the app.
      * <p>
      * This value may be different from the value requested by
      * {@code requestLegacyExternalStorage} in the app's manifest, since an app
-     * may inherit its legacy state based on when it was first installed.
+     * may inherit its legacy state based on when it was first installed, target sdk and other
+     * factors.
      * <p>
      * Non-legacy apps can continue to discover and read media belonging to
      * other apps via {@link android.provider.MediaStore}.
      *
      * @throws IllegalArgumentException if the path is not a valid storage
-     *             device.
+     * device.
      */
     public static boolean isExternalStorageLegacy(@NonNull File path) {
         final Context context = AppGlobals.getInitialApplication();
@@ -1232,24 +1277,23 @@
             return false;
         }
 
-        if (packageManager.checkPermission(Manifest.permission.WRITE_MEDIA_STORAGE,
-                context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
+        // TODO(b/150672994): Compat flags do not override instant app and isolated process's
+        //  behavior.
+        boolean defaultScopedStorage = Compatibility.isChangeEnabled(DEFAULT_SCOPED_STORAGE);
+        boolean forceEnableScopedStorage = Compatibility.isChangeEnabled(
+                FORCE_ENABLE_SCOPED_STORAGE);
+        // if Scoped Storage is strictly enforced, the app does *not* have legacy storage access
+        // Note: does not require packagename/uid as this is directly called from an app process
+        if (isScopedStorageEnforced(defaultScopedStorage, forceEnableScopedStorage)) {
+            return false;
+        }
+        // if Scoped Storage is strictly disabled, the app has legacy storage access
+        // Note: does not require packagename/uid as this is directly called from an app process
+        if (isScopedStorageDisabled(defaultScopedStorage, forceEnableScopedStorage)) {
             return true;
         }
 
-        if (packageManager.checkPermission(Manifest.permission.INSTALL_PACKAGES,
-                context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        }
         final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
-        final String[] packagesForUid = packageManager.getPackagesForUid(uid);
-        for (String packageName : packagesForUid) {
-            if (appOps.checkOpNoThrow(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
-                    uid, packageName) == AppOpsManager.MODE_ALLOWED) {
-                return true;
-            }
-        }
-
         return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
                 uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
     }
@@ -1298,6 +1342,16 @@
         }
     }
 
+    private static boolean isScopedStorageEnforced(boolean defaultScopedStorage,
+            boolean forceEnableScopedStorage) {
+        return defaultScopedStorage && forceEnableScopedStorage;
+    }
+
+    private static boolean isScopedStorageDisabled(boolean defaultScopedStorage,
+            boolean forceEnableScopedStorage) {
+        return !defaultScopedStorage && !forceEnableScopedStorage;
+    }
+
     static File getDirectory(String variableName, String defaultPath) {
         String path = System.getenv(variableName);
         return path == null ? new File(defaultPath) : new File(path);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index d7af1b9..b7b3c4f 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -607,6 +607,7 @@
      *                             started.
      * @param pkgDataInfoMap Map from related package names to private data directory
      *                       volume UUID and inode number.
+     * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
      * @throws RuntimeException on fatal start failure
@@ -630,12 +631,13 @@
                                            @Nullable long[] disabledCompatChanges,
                                            @Nullable Map<String, Pair<String, Long>>
                                                    pkgDataInfoMap,
+                                           boolean bindMountAppStorageDirs,
                                            @Nullable String[] zygoteArgs) {
         return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
                     zygotePolicyFlags, isTopApp, disabledCompatChanges,
-                    pkgDataInfoMap, zygoteArgs);
+                    pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
     }
 
     /** @hide */
@@ -659,7 +661,7 @@
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
                     /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
-                disabledCompatChanges, /* pkgDataInfoMap */ null, zygoteArgs);
+                disabledCompatChanges, /* pkgDataInfoMap */ null, false, zygoteArgs);
     }
 
     /**
@@ -929,6 +931,19 @@
     public static final native void setProcessFrozen(int pid, int uid, boolean frozen);
 
     /**
+     * Enable or disable the freezer. When enable == false all frozen processes are unfrozen,
+     * but aren't removed from the freezer. Processes can still be added or removed
+     * by using setProcessFrozen, but they won't actually be frozen until the freezer is enabled
+     * again. If enable == true the freezer is enabled again, and all processes
+     * in the freezer (including the ones added while the freezer was disabled) are frozen.
+     *
+     * @param enable Specify whether to enable (true) or disable (false) the freezer.
+     *
+     * @hide
+     */
+    public static final native void enableFreezer(boolean enable);
+
+    /**
      * Return the scheduling group of requested process.
      *
      * @hide
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 2d218f4..1992f1d 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -963,7 +963,7 @@
                 PRIMITIVE_QUICK_RISE,
                 PRIMITIVE_SLOW_RISE,
                 PRIMITIVE_QUICK_FALL,
-                PRIMITIVE_LIGHT_TICK,
+                PRIMITIVE_TICK,
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface Primitive {}
@@ -981,10 +981,14 @@
          * A haptic effect that simulates downwards movement with gravity. Often
          * followed by extra energy of hitting and reverberation to augment
          * physicality.
+         *
+         * @hide Not confident enough to expose publicly yet
          */
         public static final int PRIMITIVE_THUD = 2;
         /**
          * A haptic effect that simulates spinning momentum.
+         *
+         * @hide Not confident enough to expose publicly yet
          */
         public static final int PRIMITIVE_SPIN = 3;
         /**
@@ -1003,7 +1007,8 @@
          * This very short effect should produce a light crisp sensation intended
          * to be used repetitively for dynamic feedback.
          */
-        public static final int PRIMITIVE_LIGHT_TICK = 7;
+        // Internally this maps to the HAL constant CompositePrimitive::LIGHT_TICK
+        public static final int PRIMITIVE_TICK = 7;
 
 
         private ArrayList<PrimitiveEffect> mEffects = new ArrayList<>();
@@ -1081,7 +1086,7 @@
          *
          */
         static int checkPrimitive(int primitiveId) {
-            Preconditions.checkArgumentInRange(primitiveId, PRIMITIVE_NOOP, PRIMITIVE_LIGHT_TICK,
+            Preconditions.checkArgumentInRange(primitiveId, PRIMITIVE_NOOP, PRIMITIVE_TICK,
                     "primitiveId");
             return primitiveId;
         }
@@ -1108,8 +1113,8 @@
                     return "PRIMITIVE_SLOW_RISE";
                 case PRIMITIVE_QUICK_FALL:
                     return "PRIMITIVE_QUICK_FALL";
-                case PRIMITIVE_LIGHT_TICK:
-                    return "PRIMITIVE_LIGHT_TICK";
+                case PRIMITIVE_TICK:
+                    return "PRIMITIVE_TICK";
 
                 default:
                     return Integer.toString(id);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 34cec06..5f3f14f 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,6 +333,7 @@
      *                             started.
      * @param pkgDataInfoMap Map from related package names to private data directory
      *                       volume UUID and inode number.
+     * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
      *
      * @param zygoteArgs Additional arguments to supply to the Zygote process.
      * @return An object that describes the result of the attempt to start the process.
@@ -354,6 +355,7 @@
                                                   @Nullable long[] disabledCompatChanges,
                                                   @Nullable Map<String, Pair<String, Long>>
                                                           pkgDataInfoMap,
+                                                  boolean bindMountAppStorageDirs,
                                                   @Nullable String[] zygoteArgs) {
         // TODO (chriswailes): Is there a better place to check this value?
         if (fetchUsapPoolEnabledPropWithMinInterval()) {
@@ -365,7 +367,7 @@
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                     packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
-                    pkgDataInfoMap, zygoteArgs);
+                    pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -606,6 +608,7 @@
      * @param disabledCompatChanges a list of disabled compat changes for the process being started.
      * @param pkgDataInfoMap Map from related package names to private data directory volume UUID
      *                       and inode number.
+     * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
      * @param extraArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
      * @throws ZygoteStartFailedEx if process start failed for any reason
@@ -628,6 +631,7 @@
                                                       @Nullable long[] disabledCompatChanges,
                                                       @Nullable Map<String, Pair<String, Long>>
                                                               pkgDataInfoMap,
+                                                      boolean bindMountAppStorageDirs,
                                                       @Nullable String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
         ArrayList<String> argsForZygote = new ArrayList<>();
@@ -725,6 +729,10 @@
             argsForZygote.add(sb.toString());
         }
 
+        if (bindMountAppStorageDirs) {
+            argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
+        }
+
         if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
             StringBuilder sb = new StringBuilder();
             sb.append("--disabled-compat-changes=");
@@ -1282,7 +1290,9 @@
                     abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
                     true /* startChildZygote */, null /* packageName */,
                     ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
-                    null /* disabledCompatChanges */, null /* pkgDataInfoMap */, extraArgs);
+                    null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
+                    /* bindMountAppStorageDirs */ false, extraArgs);
+
         } catch (ZygoteStartFailedEx ex) {
             throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
         }
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index a2def7f..f43a252 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -100,10 +100,11 @@
     }
 
     /**
-     * Check if fuse is running in target user, if it's running then setup its obb directories.
-     * TODO: System server should store a list of active pids that obb is not mounted and use it.
+     * Create storage directories if it does not exist.
+     * Return true if the directories were setup correctly, otherwise false.
      */
-    public abstract void prepareObbDirs(int userId, Set<String> packageList, String processName);
+    public abstract boolean prepareStorageDirs(int userId, Set<String> packageList,
+            String processName);
 
     /**
      * Add a listener to listen to reset event in StorageManagerService.
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index e62bd0a..e116dc6 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -49,6 +49,10 @@
 to involve the user. This can be either because the API is not sensitive, or because additional
 checks exist.
 
+Another benefit of install time permissions is that is becomes very easy to monitor which apps can
+access certain APIs. E.g. by checking which apps have the `android.permission.INTERNET` permission
+you can list all apps that are allowed to use APIs that can send data to the internet.
+
 #### Defining a permission
 
 Any package can define a permission. For that it simply adds an entry in the manifest file
@@ -591,8 +595,9 @@
 ### Permission restricted components
 
 As [publicly documented](https://developer.android.com/guide/topics/permissions/overview#permission_enforcement)
-it is possible to restrict starting an activity/binding to a service by using permission. It is
-a common pattern to
+it is possible to restrict starting an activity/binding to a service by using permission.
+
+It is a common pattern to
 
 - define a permission in the platform as `signature`
 - protect a service in an app by this permission using the `android:permission` attribute of the
@@ -605,6 +610,75 @@
 This does not work for app-op or runtime permissions as the way to check these permissions is
 more complex than install time permissions.
 
+#### End-to-end A service only the system can bind to
+
+Make sure to set the `android:permission` flag for this service. As developers can forget this it is
+a good habit to check this before binding to the service. This makes sure that the services are
+implemented correctly and no random app can bind to the service.
+
+The result is that the permission is granted only to the system. It is not granted to the service's
+package, but this has no negative side-effects.
+
+##### Permission definition
+
+frameworks/base/core/res/AndroidManifest.xml:
+
+```xml
+<manifest>
+[...]
+    <permission android:name="android.permission.BIND_MY_SERVICE"
+        android:label="@string/permlab_bindMyService"
+        android:description="@string/permdesc_bindMyService"
+        android:protectionLevel="signature" />
+[...]
+</manifest>
+```
+
+##### Service definition
+
+Manifest of the service providing the functionality:
+
+```xml
+<manifest>
+        <service android:name="com.my.ServiceImpl"
+                 android:permission="android.permission.BIND_MY_SERVICE">
+            <!-- add an intent filter so that the system can find this package -->
+            <intent-filter>
+                <action android:name="android.my.Service" />
+            </intent-filter>
+        </service>
+</manifest>
+```
+
+##### System server code binding to service
+
+```kotlin
+val serviceConnections = mutableListOf<ServiceConnection>()
+
+val potentialServices = context.packageManager.queryIntentServicesAsUser(
+        Intent("android.my.Service"), GET_SERVICES or GET_META_DATA, userId)
+
+for (val ri in potentialServices) {
+    val serviceComponent = ComponentName(ri.serviceInfo.packageName, ri.serviceInfo.name)
+
+    if (android.Manifest.permission.BIND_MY_SERVICE != ri.serviceInfo.permission) {
+        Slog.w(TAG, "$serviceComponent is not protected by " +
+                "${android.Manifest.permission.BIND_MY_SERVICE}")
+        continue
+    }
+
+    val newConnection = object : ServiceConnection {
+        ...
+    }
+
+    val wasBound = context.bindServiceAsUser(Intent().setComponent(serviceComponent),
+            serviceConnection, BIND_AUTO_CREATE, UserHandle.of(userId))
+    if (wasBound) {
+        serviceConnections.add(newConnection)
+    }
+}
+```
+
 ## Permissions for system apps
 
 System apps need to integrate deeper with the system than regular apps. Hence they need to be
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ce9b449..d8679b2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8497,7 +8497,6 @@
          *
          * @hide
          */
-        @SystemApi
         public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
 
         /**
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index f0a72c5..7fbc309 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -97,6 +97,9 @@
             host.setView(suggestionRoot, lp);
             suggestionRoot.setOnClickListener((v) -> {
                 try {
+                    if (suggestionView.hasOnClickListeners()) {
+                        suggestionView.callOnClick();
+                    }
                     callback.onClick();
                 } catch (RemoteException e) {
                     Log.w(TAG, "RemoteException calling onClick()");
@@ -105,6 +108,9 @@
 
             suggestionRoot.setOnLongClickListener((v) -> {
                 try {
+                    if (suggestionView.hasOnLongClickListeners()) {
+                        suggestionView.performLongClick();
+                    }
                     callback.onLongClick();
                 } catch (RemoteException e) {
                     Log.w(TAG, "RemoteException calling onLongClick()");
diff --git a/core/java/android/service/autofill/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
index bdcc253..6c9d36b 100644
--- a/core/java/android/service/autofill/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -52,6 +52,11 @@
     }
 
     @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return true;
+    }
+
+    @Override
     @SuppressLint("ClickableViewAccessibility")
     public boolean onTouchEvent(@NonNull MotionEvent event) {
         switch (event.getActionMasked()) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 0cd96b8..c52b02b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -55,7 +55,6 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Slog;
 import android.widget.RemoteViews;
 
 import com.android.internal.annotations.GuardedBy;
@@ -1570,6 +1569,7 @@
         private boolean mVisuallyInterruptive;
         private boolean mIsConversation;
         private ShortcutInfo mShortcutInfo;
+        private boolean mIsBubble;
 
         private static final int PARCEL_VERSION = 2;
 
@@ -1604,6 +1604,7 @@
             out.writeBoolean(mVisuallyInterruptive);
             out.writeBoolean(mIsConversation);
             out.writeParcelable(mShortcutInfo, flags);
+            out.writeBoolean(mIsBubble);
         }
 
         /** @hide */
@@ -1639,6 +1640,7 @@
             mVisuallyInterruptive = in.readBoolean();
             mIsConversation = in.readBoolean();
             mShortcutInfo = in.readParcelable(cl);
+            mIsBubble = in.readBoolean();
         }
 
 
@@ -1844,6 +1846,14 @@
         }
 
         /**
+         * Returns whether this notification is actively a bubble.
+         * @hide
+         */
+        public boolean isBubble() {
+            return mIsBubble;
+        }
+
+        /**
          * @hide
          */
         public @Nullable ShortcutInfo getShortcutInfo() {
@@ -1862,7 +1872,8 @@
                 int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
                 boolean noisy, ArrayList<Notification.Action> smartActions,
                 ArrayList<CharSequence> smartReplies, boolean canBubble,
-                boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo) {
+                boolean visuallyInterruptive, boolean isConversation, ShortcutInfo shortcutInfo,
+                boolean isBubble) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1886,6 +1897,7 @@
             mVisuallyInterruptive = visuallyInterruptive;
             mIsConversation = isConversation;
             mShortcutInfo = shortcutInfo;
+            mIsBubble = isBubble;
         }
 
         /**
@@ -1913,7 +1925,8 @@
                     other.mCanBubble,
                     other.mVisuallyInterruptive,
                     other.mIsConversation,
-                    other.mShortcutInfo);
+                    other.mShortcutInfo,
+                    other.mIsBubble);
         }
 
         /**
@@ -1970,7 +1983,8 @@
                     && Objects.equals(mIsConversation, other.mIsConversation)
                     // Shortcutinfo doesn't have equals either; use id
                     &&  Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()),
-                    (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()));
+                    (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId()))
+                    && Objects.equals(mIsBubble, other.mIsBubble);
         }
     }
 
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
index d2494a5..ae67068 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
@@ -24,8 +24,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import java.util.List;
-
 /**
  * Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
  *
@@ -56,7 +54,6 @@
      *                 presented as the selected card.
      */
     public void onSuccess(@NonNull GetWalletCardsResponse response) {
-        Log.i(TAG, "onSuccess");
         if (isValidResponse(response)) {
             mHandler.post(() -> onSuccessInternal(response));
         } else {
@@ -78,7 +75,6 @@
     }
 
     private void onSuccessInternal(GetWalletCardsResponse response) {
-        Log.i(TAG, "onSuccessInternal");
         if (mCalled) {
             Log.w(TAG, "already called");
             return;
@@ -86,7 +82,6 @@
         mCalled = true;
         try {
             mCallback.onGetWalletCardsSuccess(response);
-            Log.i(TAG, "onSuccessInternal: returned response");
         } catch (RemoteException e) {
             Log.w(TAG, "Error returning wallet cards", e);
         }
@@ -106,29 +101,53 @@
     }
 
     private boolean isValidResponse(@NonNull GetWalletCardsResponse response) {
-        return response != null
-                && response.getWalletCards() != null
-                && response.getSelectedIndex() >= 0
-                && (response.getWalletCards().isEmpty() // selectedIndex may be 0 when list is empty
-                || response.getSelectedIndex() < response.getWalletCards().size())
-                && response.getWalletCards().size() < mRequest.getMaxCards()
-                && areValidCards(response.getWalletCards());
-    }
-
-    private boolean areValidCards(List<WalletCard> walletCards) {
-        for (WalletCard walletCard : walletCards) {
-            if (walletCard == null
-                    || walletCard.getCardId() == null
-                    || walletCard.getCardImage() == null
-                    || TextUtils.isEmpty(walletCard.getContentDescription())
-                    || walletCard.getPendingIntent() == null) {
+        if (response == null) {
+            Log.w(TAG, "Invalid response: response is null");
+            return false;
+        }
+        if (response.getWalletCards() == null) {
+            Log.w(TAG, "Invalid response: walletCards is null");
+            return false;
+        }
+        if (response.getSelectedIndex() < 0) {
+            Log.w(TAG, "Invalid response: selectedIndex is negative");
+            return false;
+        }
+        if (!response.getWalletCards().isEmpty()
+                && response.getSelectedIndex() >= response.getWalletCards().size()) {
+            Log.w(TAG, "Invalid response: selectedIndex out of bounds");
+            return false;
+        }
+        if (response.getWalletCards().size() > mRequest.getMaxCards()) {
+            Log.w(TAG, "Invalid response: too many cards");
+            return false;
+        }
+        for (WalletCard walletCard : response.getWalletCards()) {
+            if (walletCard == null) {
+                Log.w(TAG, "Invalid response: card is null");
+                return false;
+            }
+            if (walletCard.getCardId() == null) {
+                Log.w(TAG, "Invalid response: cardId is null");
                 return false;
             }
             Icon cardImage = walletCard.getCardImage();
+            if (cardImage == null) {
+                Log.w(TAG, "Invalid response: cardImage is null");
+                return false;
+            }
             if (cardImage.getType() == Icon.TYPE_BITMAP
-                    && walletCard.getCardImage().getBitmap().getConfig()
-                    != Bitmap.Config.HARDWARE) {
-                Log.w(TAG, "WalletCard bitmaps should be hardware bitmaps");
+                    && cardImage.getBitmap().getConfig() != Bitmap.Config.HARDWARE) {
+                Log.w(TAG, "Invalid response: cardImage bitmaps must be hardware bitmaps");
+                return false;
+            }
+            if (TextUtils.isEmpty(walletCard.getContentDescription())) {
+                Log.w(TAG, "Invalid response: contentDescription is null");
+                return false;
+            }
+            if (walletCard.getPendingIntent() == null) {
+                Log.w(TAG, "Invalid response: pendingIntent is null");
+                return false;
             }
         }
         return true;
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index be9ab11..4f5b139 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -16,19 +16,23 @@
 
 package android.service.quickaccesswallet;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.Intent;
 
+import java.io.Closeable;
+import java.util.concurrent.Executor;
+
 /**
  * Facilitates accessing cards from the {@link QuickAccessWalletService}.
  *
  * @hide
  */
 @TestApi
-public interface QuickAccessWalletClient {
+public interface QuickAccessWalletClient extends Closeable {
 
     /**
      * Create a client for accessing wallet cards from the {@link QuickAccessWalletService}. If the
@@ -92,6 +96,14 @@
             @NonNull OnWalletCardsRetrievedCallback callback);
 
     /**
+     * Get wallet cards from the {@link QuickAccessWalletService}.
+     */
+    void getWalletCards(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull GetWalletCardsRequest request,
+            @NonNull OnWalletCardsRetrievedCallback callback);
+
+    /**
      * Callback for getWalletCards
      */
     interface OnWalletCardsRetrievedCallback {
@@ -111,12 +123,19 @@
     void notifyWalletDismissed();
 
     /**
-     * Unregister event listener.
+     * Register an event listener.
      */
     void addWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
 
     /**
-     * Unregister event listener
+     * Register an event listener.
+     */
+    void addWalletServiceEventListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull WalletServiceEventListener listener);
+
+    /**
+     * Unregister an event listener
      */
     void removeWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
 
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 37a8703..e4dd082 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -18,6 +18,7 @@
 
 import static android.service.quickaccesswallet.QuickAccessWalletService.SERVICE_INTERFACE;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -36,16 +37,19 @@
 
 import com.android.internal.widget.LockPatternUtils;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Queue;
 import java.util.UUID;
+import java.util.concurrent.Executor;
 
 /**
  * Implements {@link QuickAccessWalletClient}. The client connects, performs requests, waits for
- * responses, and disconnects automatically after a short period of time. The client may
+ * responses, and disconnects automatically one minute after the last call is performed.
+ *
  * @hide
  */
 public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, ServiceConnection {
@@ -78,15 +82,14 @@
 
     @Override
     public boolean isWalletServiceAvailable() {
-        boolean available = mServiceInfo != null;
-        Log.i(TAG, "isWalletServiceAvailable: " + available);
-        return available;
+        return mServiceInfo != null;
     }
 
     @Override
     public boolean isWalletFeatureAvailable() {
         int currentUser = ActivityManager.getCurrentUser();
-        return checkUserSetupComplete()
+        return currentUser == UserHandle.USER_SYSTEM
+                && checkUserSetupComplete()
                 && checkSecureSetting(Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED)
                 && !new LockPatternUtils(mContext).isUserInLockdown(currentUser);
     }
@@ -101,23 +104,29 @@
     public void getWalletCards(
             @NonNull GetWalletCardsRequest request,
             @NonNull OnWalletCardsRetrievedCallback callback) {
+        getWalletCards(mContext.getMainExecutor(), request, callback);
+    }
 
-        Log.i(TAG, "getWalletCards");
-
+    @Override
+    public void getWalletCards(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull GetWalletCardsRequest request,
+            @NonNull OnWalletCardsRetrievedCallback callback) {
         if (!isWalletServiceAvailable()) {
-            callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null));
+            executor.execute(
+                    () -> callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null)));
             return;
         }
 
         BaseCallbacks serviceCallback = new BaseCallbacks() {
             @Override
             public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
-                mHandler.post(() -> callback.onWalletCardsRetrieved(response));
+                executor.execute(() -> callback.onWalletCardsRetrieved(response));
             }
 
             @Override
             public void onGetWalletCardsFailure(GetWalletCardsError error) {
-                mHandler.post(() -> callback.onWalletCardRetrievalError(error));
+                executor.execute(() -> callback.onWalletCardRetrievalError(error));
             }
         };
 
@@ -132,11 +141,11 @@
                 serviceCallback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
             }
         });
+
     }
 
     @Override
     public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
-        Log.i(TAG, "selectWalletCard");
         if (!isWalletServiceAvailable()) {
             return;
         }
@@ -153,7 +162,6 @@
         if (!isWalletServiceAvailable()) {
             return;
         }
-        Log.i(TAG, "notifyWalletDismissed");
         executeApiCall(new ApiCaller("onWalletDismissed") {
             @Override
             public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
@@ -164,15 +172,20 @@
 
     @Override
     public void addWalletServiceEventListener(WalletServiceEventListener listener) {
+        addWalletServiceEventListener(mContext.getMainExecutor(), listener);
+    }
+
+    @Override
+    public void addWalletServiceEventListener(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull WalletServiceEventListener listener) {
         if (!isWalletServiceAvailable()) {
             return;
         }
-        Log.i(TAG, "registerWalletServiceEventListener");
         BaseCallbacks callback = new BaseCallbacks() {
             @Override
             public void onWalletServiceEvent(WalletServiceEvent event) {
-                Log.i(TAG, "onWalletServiceEvent");
-                mHandler.post(() -> listener.onWalletServiceEvent(event));
+                executor.execute(() -> listener.onWalletServiceEvent(event));
             }
         };
 
@@ -193,7 +206,6 @@
         if (!isWalletServiceAvailable()) {
             return;
         }
-        Log.i(TAG, "unregisterWalletServiceEventListener");
         executeApiCall(new ApiCaller("unregisterListener") {
             @Override
             public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
@@ -209,8 +221,12 @@
     }
 
     @Override
+    public void close() throws IOException {
+        disconnect();
+    }
+
+    @Override
     public void disconnect() {
-        Log.i(TAG, "disconnect");
         mHandler.post(() -> disconnectInternal(true));
     }
 
@@ -241,18 +257,15 @@
     }
 
     private void connect() {
-        Log.i(TAG, "connect");
         mHandler.post(this::connectInternal);
     }
 
     private void connectInternal() {
-        Log.i(TAG, "connectInternal");
         if (mServiceInfo == null) {
             Log.w(TAG, "Wallet service unavailable");
             return;
         }
         if (mIsConnected) {
-            Log.w(TAG, "already connected");
             return;
         }
         mIsConnected = true;
@@ -264,23 +277,14 @@
     }
 
     private void onConnectedInternal(IQuickAccessWalletService service) {
-        Log.i(TAG, "onConnectedInternal");
         if (!mIsConnected) {
             Log.w(TAG, "onConnectInternal but connection closed");
             mService = null;
             return;
         }
         mService = service;
-        Log.i(TAG, "onConnectedInternal success: request queue size " + mRequestQueue.size());
         for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) {
-            try {
-                apiCaller.performApiCall(mService);
-            } catch (RemoteException e) {
-                Log.e(TAG, "onConnectedInternal error", e);
-                apiCaller.onApiError();
-                disconnect();
-                break;
-            }
+            performApiCallInternal(apiCaller, mService);
             mRequestQueue.remove(apiCaller);
         }
     }
@@ -290,7 +294,6 @@
      * posting a new delayed message.
      */
     private void resetServiceConnectionTimeout() {
-        Log.i(TAG, "resetServiceConnectionTimeout");
         mHandler.removeMessages(MSG_TIMEOUT_SERVICE);
         mHandler.postDelayed(
                 () -> disconnectInternal(true),
@@ -299,13 +302,11 @@
     }
 
     private void disconnectInternal(boolean clearEventListeners) {
-        Log.i(TAG, "disconnectInternal: " + clearEventListeners);
         if (!mIsConnected) {
             Log.w(TAG, "already disconnected");
             return;
         }
         if (clearEventListeners && !mEventListeners.isEmpty()) {
-            Log.i(TAG, "disconnectInternal: clear event listeners");
             for (WalletServiceEventListener listener : mEventListeners.keySet()) {
                 removeWalletServiceEventListener(listener);
             }
@@ -320,29 +321,33 @@
     }
 
     private void executeApiCall(ApiCaller apiCaller) {
-        Log.i(TAG, "execute: " + apiCaller.mDesc);
         mHandler.post(() -> executeInternal(apiCaller));
     }
 
-    private void executeInternal(ApiCaller apiCall) {
-        Log.i(TAG, "executeInternal: " + apiCall.mDesc);
+    private void executeInternal(ApiCaller apiCaller) {
         if (mIsConnected && mService != null) {
-            try {
-                apiCall.performApiCall(mService);
-                Log.i(TAG, "executeInternal success: " + apiCall.mDesc);
-                resetServiceConnectionTimeout();
-            } catch (RemoteException e) {
-                Log.w(TAG, "executeInternal error: " + apiCall.mDesc, e);
-                apiCall.onApiError();
-                disconnect();
-            }
+            performApiCallInternal(apiCaller, mService);
         } else {
-            Log.i(TAG, "executeInternal: queued" + apiCall.mDesc);
-            mRequestQueue.add(apiCall);
+            mRequestQueue.add(apiCaller);
             connect();
         }
     }
 
+    private void performApiCallInternal(ApiCaller apiCaller, IQuickAccessWalletService service) {
+        if (service == null) {
+            apiCaller.onApiError();
+            return;
+        }
+        try {
+            apiCaller.performApiCall(service);
+            resetServiceConnectionTimeout();
+        } catch (RemoteException e) {
+            Log.w(TAG, "executeInternal error: " + apiCaller.mDesc, e);
+            apiCaller.onApiError();
+            disconnect();
+        }
+    }
+
     private abstract static class ApiCaller {
         private final String mDesc;
 
@@ -350,7 +355,8 @@
             this.mDesc = desc;
         }
 
-        abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException;
+        abstract void performApiCall(IQuickAccessWalletService service)
+                throws RemoteException;
 
         void onApiError() {
             Log.w(TAG, "api error: " + mDesc);
@@ -359,7 +365,6 @@
 
     @Override // ServiceConnection
     public void onServiceConnected(ComponentName name, IBinder binder) {
-        Log.i(TAG, "onServiceConnected: " + name);
         IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder);
         mHandler.post(() -> onConnectedInternal(service));
     }
@@ -367,19 +372,16 @@
     @Override // ServiceConnection
     public void onServiceDisconnected(ComponentName name) {
         // Do not disconnect, as we may later be re-connected
-        Log.w(TAG, "onServiceDisconnected");
     }
 
     @Override // ServiceConnection
     public void onBindingDied(ComponentName name) {
         // This is a recoverable error but the client will need to reconnect.
-        Log.w(TAG, "onBindingDied");
         disconnect();
     }
 
     @Override // ServiceConnection
     public void onNullBinding(ComponentName name) {
-        Log.w(TAG, "onNullBinding");
         disconnect();
     }
 
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index 23173a8..31e51bb 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -66,13 +66,11 @@
     static QuickAccessWalletServiceInfo tryCreate(@NonNull Context context) {
         ComponentName defaultPaymentApp = getDefaultPaymentApp(context);
         if (defaultPaymentApp == null) {
-            Log.d(TAG, "create: default payment app not set");
             return null;
         }
 
         ServiceInfo serviceInfo = getWalletServiceInfo(context, defaultPaymentApp.getPackageName());
         if (serviceInfo == null) {
-            Log.d(TAG, "create: unable to resolve service intent");
             return null;
         }
 
diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java
index e6ae0ab..b09d2e9 100644
--- a/core/java/android/service/quickaccesswallet/WalletCard.java
+++ b/core/java/android/service/quickaccesswallet/WalletCard.java
@@ -180,7 +180,7 @@
          *                           GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
          *                           GetWalletCardsRequest#getCardHeightPx()}. If the card image
          *                           does not have these dimensions, it may appear distorted when it
-         *                           is scaled to fit these dimensions on screen. Bitmaps should be
+         *                           is scaled to fit these dimensions on screen. Bitmaps must be
          *                           of type {@link android.graphics.Bitmap.Config#HARDWARE} for
          *                           performance reasons.
          * @param contentDescription The content description of the card image. This field is
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 3ff6f54..9dfbc28 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -41,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;
@@ -405,27 +404,19 @@
      */
     @NonNull
     public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) {
-        final TextClassificationManager tcm =
-                context.getSystemService(TextClassificationManager.class);
-        if (tcm == null) {
+        final String defaultTextClassifierPackageName =
+                context.getPackageManager().getDefaultTextClassifierPackageName();
+        if (TextUtils.isEmpty(defaultTextClassifierPackageName)) {
             return TextClassifier.NO_OP;
         }
-        TextClassificationConstants settings = new TextClassificationConstants();
-        if (settings.getUseDefaultTextClassifierAsDefaultImplementation()) {
-            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.");
-            }
-            return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE);
-        } else {
-            return tcm.getTextClassifier(TextClassifier.LOCAL);
+        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);
+        return tcm.getTextClassifier(TextClassifier.DEFAULT_SYSTEM);
     }
 
     /** @hide **/
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index d273500..28db270 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -176,7 +176,6 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
-    @SystemApi
     public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH          = 0x00000200;
 
     /**
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 6b87a10..cfbe393 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -71,7 +71,7 @@
         // introduced in R and will be removed in the next release (b/148367230).
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false");
 
-        DEFAULT_FLAGS.put("settings_tether_all_in_one", "true");
+        DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
         DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false");
         DEFAULT_FLAGS.put("settings_contextual_home2", "false");
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 79eb9f6..2437af2 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -213,15 +213,24 @@
                     verityDigest, apk.length(), signatureInfo);
         }
 
-        if (contentDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA512)) {
-            result.digest = contentDigests.get(CONTENT_DIGEST_CHUNKED_SHA512);
-        } else if (contentDigests.containsKey(CONTENT_DIGEST_CHUNKED_SHA256)) {
-            result.digest = contentDigests.get(CONTENT_DIGEST_CHUNKED_SHA256);
-        }
+        result.digest = pickBestV3DigestForV4(contentDigests);
 
         return result;
     }
 
+    // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier.
+    private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) {
+        final int[] orderedContentDigestTypes =
+                {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+                        CONTENT_DIGEST_CHUNKED_SHA256};
+        for (int contentDigestType : orderedContentDigestTypes) {
+            if (contentDigests.containsKey(contentDigestType)) {
+                return contentDigests.get(contentDigestType);
+            }
+        }
+        return null;
+    }
+
     private static VerifiedSigner verifySigner(
             ByteBuffer signerBlock,
             Map<Integer, byte[]> contentDigests,
diff --git a/core/java/android/util/apk/SourceStampVerifier.java b/core/java/android/util/apk/SourceStampVerifier.java
index c78baf5..a7ae32d 100644
--- a/core/java/android/util/apk/SourceStampVerifier.java
+++ b/core/java/android/util/apk/SourceStampVerifier.java
@@ -24,6 +24,7 @@
 import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
 
 import android.util.Pair;
+import android.util.Slog;
 import android.util.jar.StrictJarFile;
 
 import libcore.io.IoUtils;
@@ -69,6 +70,8 @@
  */
 public abstract class SourceStampVerifier {
 
+    private static final String TAG = "SourceStampVerifier";
+
     private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
     private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0;
     private static final int SOURCE_STAMP_BLOCK_ID = 0x2b09189e;
@@ -99,28 +102,31 @@
 
     /** Verifies SourceStamp present in the provided APK. */
     public static SourceStampVerificationResult verify(String apkFile) {
+        StrictJarFile apkJar = null;
         try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
-            return verify(apk);
-        } catch (IOException e) {
-            // Any exception in reading the APK returns a non-present SourceStamp outcome
-            // without affecting the outcome of any of the other signature schemes.
-            return SourceStampVerificationResult.notPresent();
-        }
-    }
-
-    private static SourceStampVerificationResult verify(RandomAccessFile apk) {
-        byte[] sourceStampCertificateDigest;
-        try {
-            sourceStampCertificateDigest = getSourceStampCertificateDigest(apk);
+            apkJar =
+                    new StrictJarFile(
+                            apkFile,
+                            /* verify= */ false,
+                            /* signatureSchemeRollbackProtectionsEnforced= */ false);
+            byte[] sourceStampCertificateDigest = getSourceStampCertificateDigest(apkJar);
             if (sourceStampCertificateDigest == null) {
                 // SourceStamp certificate hash file not found, which means that there is not
                 // SourceStamp present.
                 return SourceStampVerificationResult.notPresent();
             }
+            return verify(apk, sourceStampCertificateDigest);
         } catch (IOException e) {
+            // Any exception in reading the APK returns a non-present SourceStamp outcome
+            // without affecting the outcome of any of the other signature schemes.
             return SourceStampVerificationResult.notPresent();
+        } finally {
+            closeApkJar(apkJar);
         }
+    }
 
+    private static SourceStampVerificationResult verify(
+            RandomAccessFile apk, byte[] sourceStampCertificateDigest) {
         try {
             SignatureInfo signatureInfo =
                     ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID);
@@ -283,22 +289,17 @@
         return apkContentDigests;
     }
 
-    private static byte[] getSourceStampCertificateDigest(RandomAccessFile apk) throws IOException {
-        StrictJarFile apkJar =
-                new StrictJarFile(
-                        apk.getFD(),
-                        /* verify= */ false,
-                        /* signatureSchemeRollbackProtectionsEnforced= */ false);
-        ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME);
-        if (zipEntry == null) {
-            // SourceStamp certificate hash file not found, which means that there is not
-            // SourceStamp present.
-            return null;
-        }
+    private static byte[] getSourceStampCertificateDigest(StrictJarFile apkJar) throws IOException {
         InputStream inputStream = null;
-        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         try {
+            ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME);
+            if (zipEntry == null) {
+                // SourceStamp certificate hash file not found, which means that there is not
+                // SourceStamp present.
+                return null;
+            }
             inputStream = apkJar.getInputStream(zipEntry);
+            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
 
             // Trying to read the certificate digest, which should be less than 1024 bytes.
             byte[] buffer = new byte[1024];
@@ -327,4 +328,15 @@
         }
         return result.array();
     }
+
+    private static void closeApkJar(StrictJarFile apkJar) {
+        try {
+            if (apkJar == null) {
+                return;
+            }
+            apkJar.close();
+        } catch (IOException e) {
+            Slog.e(TAG, "Could not close APK jar", e);
+        }
+    }
 }
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 88e7f2e..123b9db 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -53,7 +53,6 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 
 import java.io.PrintWriter;
@@ -518,14 +517,13 @@
             PendingControlRequest pendingRequest = mPendingImeControlRequest;
             mPendingImeControlRequest = null;
             mHandler.removeCallbacks(mPendingControlTimeout);
-            CancellationSignal cancellationSignal = controlAnimationUnchecked(
-                    pendingRequest.types,
+            controlAnimationUnchecked(
+                    pendingRequest.types, pendingRequest.cancellationSignal,
                     pendingRequest.listener, mFrame,
                     true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
                     false /* fade */, pendingRequest.animationType,
                     pendingRequest.layoutInsetsDuringAnimation,
                     pendingRequest.useInsetsAnimationThread);
-            pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel);
             return;
         }
 
@@ -571,24 +569,26 @@
     }
 
     @Override
-    public CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
+    public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
             @Nullable Interpolator interpolator,
+            @Nullable CancellationSignal cancellationSignal,
             @NonNull WindowInsetsAnimationControlListener listener) {
-        return controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs,
-                interpolator, ANIMATION_TYPE_USER);
+        controlWindowInsetsAnimation(types, cancellationSignal, listener,
+                false /* fromIme */, durationMillis, interpolator, ANIMATION_TYPE_USER);
     }
 
-    private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types,
-            WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
-            @Nullable Interpolator interpolator, @AnimationType int animationType) {
+    private void controlWindowInsetsAnimation(@InsetsType int types,
+            @Nullable CancellationSignal cancellationSignal,
+            WindowInsetsAnimationControlListener listener,
+            boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
+            @AnimationType int animationType) {
         if (!checkDisplayFramesForControlling()) {
             listener.onCancelled();
-            CancellationSignal cancellationSignal = new CancellationSignal();
-            cancellationSignal.cancel();
-            return cancellationSignal;
+            return;
         }
-        return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
-                false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types),
+        controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
+                interpolator, false /* fade */, animationType,
+                getLayoutInsetsDuringAnimationMode(types),
                 false /* useInsetsAnimationThread */);
     }
 
@@ -599,18 +599,17 @@
         return mState.getDisplayFrame().equals(mFrame);
     }
 
-    private CancellationSignal controlAnimationUnchecked(@InsetsType int types,
+    private void controlAnimationUnchecked(@InsetsType int types,
+            @Nullable CancellationSignal cancellationSignal,
             WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
             long durationMs, Interpolator interpolator, boolean fade,
             @AnimationType int animationType,
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
             boolean useInsetsAnimationThread) {
-        CancellationSignal cancellationSignal = new CancellationSignal();
         if (types == 0) {
             // nothing to animate.
             listener.onCancelled();
-            cancellationSignal.cancel();
-            return cancellationSignal;
+            return;
         }
         cancelExistingControllers(types);
         mLastStartedAnimTypes |= types;
@@ -631,18 +630,19 @@
                     useInsetsAnimationThread);
             mPendingImeControlRequest = request;
             mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
-            cancellationSignal.setOnCancelListener(() -> {
-                if (mPendingImeControlRequest == request) {
-                    abortPendingImeControlRequest();
-                }
-            });
-            return cancellationSignal;
+            if (cancellationSignal != null) {
+                cancellationSignal.setOnCancelListener(() -> {
+                    if (mPendingImeControlRequest == request) {
+                        abortPendingImeControlRequest();
+                    }
+                });
+            }
+            return;
         }
 
         if (typesReady == 0) {
             listener.onCancelled();
-            cancellationSignal.cancel();
-            return cancellationSignal;
+            return;
         }
 
 
@@ -654,13 +654,14 @@
                         frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
                         animationType);
         mRunningAnimations.add(new RunningAnimation(runner, animationType));
-        cancellationSignal.setOnCancelListener(runner::cancel);
+        if (cancellationSignal != null) {
+            cancellationSignal.setOnCancelListener(runner::cancel);
+        }
         if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
             showDirectly(types);
         } else {
             hideDirectly(types, false /* animationFinished */, animationType);
         }
-        return cancellationSignal;
     }
 
     /**
@@ -922,7 +923,8 @@
         // Show/hide animations always need to be relative to the display frame, in order that shown
         // and hidden state insets are correct.
         controlAnimationUnchecked(
-                types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
+                types, null /* cancellationSignal */, listener, mState.getDisplayFrame(), fromIme,
+                listener.getDurationMs(),
                 INTERPOLATOR, true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
                 show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
                         : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index 7f36418..e8d9bb5 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.CancellationSignal;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.animation.Interpolator;
@@ -60,21 +62,6 @@
     }
 
     @Override
-    public CancellationSignal controlWindowInsetsAnimation(int types, long durationMillis,
-            Interpolator interpolator,
-            WindowInsetsAnimationControlListener listener) {
-        if (mReplayedInsetsController != null) {
-            return mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
-                    interpolator, listener);
-        } else {
-            listener.onCancelled();
-            CancellationSignal cancellationSignal = new CancellationSignal();
-            cancellationSignal.cancel();
-            return cancellationSignal;
-        }
-    }
-
-    @Override
     public void setSystemBarsAppearance(int appearance, int mask) {
         if (mReplayedInsetsController != null) {
             mReplayedInsetsController.setSystemBarsAppearance(appearance, mask);
@@ -176,6 +163,19 @@
         mReplayedInsetsController = null;
     }
 
+    @Override
+    public void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+            @Nullable Interpolator interpolator,
+            CancellationSignal cancellationSignal,
+            @NonNull WindowInsetsAnimationControlListener listener) {
+        if (mReplayedInsetsController != null) {
+            mReplayedInsetsController.controlWindowInsetsAnimation(types, durationMillis,
+                    interpolator, cancellationSignal, listener);
+        } else {
+            listener.onCancelled();
+        }
+    }
+
     private interface PendingRequest {
         void replay(InsetsController controller);
     }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 25f5609..c87808b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1960,7 +1960,7 @@
      * @hide
      */
     public static ScreenshotGraphicBuffer captureLayersExcluding(SurfaceControl layer,
-            Rect sourceCrop, float frameScale, SurfaceControl[] exclude) {
+          Rect sourceCrop, float frameScale, int format, SurfaceControl[] exclude) {
         final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
         long[] nativeExcludeObjects = new long[exclude.length];
         for (int i = 0; i < exclude.length; i++) {
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index 9c16e13..56b4951 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -168,6 +168,18 @@
     }
 
     /**
+     * Sets whether a container or its children should be hidden. When {@code false}, the existing
+     * visibility of the container applies, but when {@code true} the container will be forced
+     * to be hidden.
+     */
+    public WindowContainerTransaction setHidden(IWindowContainer container, boolean hidden) {
+        Change chg = getOrCreateChange(container.asBinder());
+        chg.mHidden = hidden;
+        chg.mChangeMask |= Change.CHANGE_HIDDEN;
+        return this;
+    }
+
+    /**
      * Set the smallestScreenWidth of a container.
      */
     public WindowContainerTransaction setSmallestScreenWidthDp(IWindowContainer container,
@@ -250,9 +262,11 @@
         public static final int CHANGE_FOCUSABLE = 1;
         public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
         public static final int CHANGE_PIP_CALLBACK = 1 << 2;
+        public static final int CHANGE_HIDDEN = 1 << 3;
 
         private final Configuration mConfiguration = new Configuration();
         private boolean mFocusable = true;
+        private boolean mHidden = false;
         private int mChangeMask = 0;
         private @ActivityInfo.Config int mConfigSetMask = 0;
         private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
@@ -268,6 +282,7 @@
         protected Change(Parcel in) {
             mConfiguration.readFromParcel(in);
             mFocusable = in.readBoolean();
+            mHidden = in.readBoolean();
             mChangeMask = in.readInt();
             mConfigSetMask = in.readInt();
             mWindowSetMask = in.readInt();
@@ -296,7 +311,7 @@
             return mConfiguration;
         }
 
-        /** Gets the requested focusable value */
+        /** Gets the requested focusable state */
         public boolean getFocusable() {
             if ((mChangeMask & CHANGE_FOCUSABLE) == 0) {
                 throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first");
@@ -304,6 +319,14 @@
             return mFocusable;
         }
 
+        /** Gets the requested hidden state */
+        public boolean getHidden() {
+            if ((mChangeMask & CHANGE_HIDDEN) == 0) {
+                throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first");
+            }
+            return mHidden;
+        }
+
         public int getChangeMask() {
             return mChangeMask;
         }
@@ -369,6 +392,7 @@
         public void writeToParcel(Parcel dest, int flags) {
             mConfiguration.writeToParcel(dest, flags);
             dest.writeBoolean(mFocusable);
+            dest.writeBoolean(mHidden);
             dest.writeInt(mChangeMask);
             dest.writeInt(mConfigSetMask);
             dest.writeInt(mWindowSetMask);
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index 701bd31..faaf920 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -16,7 +16,6 @@
 
 package android.view;
 
-import android.annotation.Hide;
 import android.annotation.NonNull;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.inputmethod.EditorInfo;
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 2ad557e..0282eca 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -156,16 +156,17 @@
      *                     calculate {@link WindowInsetsAnimation#getInterpolatedFraction()}.
      * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
      *                 windows are ready to be controlled, among other callbacks.
-     * @return A cancellation signal that the caller can use to cancel the request to obtain
-     *         control, or once they have control, to cancel the control.
+     * @param cancellationSignal A cancellation signal that the caller can use to cancel the
+     *                           request to obtain control, or once they have control, to cancel the
+     *                           control.
      * @see WindowInsetsAnimation#getFraction()
      * @see WindowInsetsAnimation#getInterpolatedFraction()
      * @see WindowInsetsAnimation#getInterpolator()
      * @see WindowInsetsAnimation#getDurationMillis()
      */
-    @NonNull
-    CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+    void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
             @Nullable Interpolator interpolator,
+            @Nullable CancellationSignal cancellationSignal,
             @NonNull WindowInsetsAnimationControlListener listener);
 
     /**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c5fa3c8..77ce5c1 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -89,6 +89,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -2896,6 +2897,18 @@
         private boolean mFitInsetsIgnoringVisibility = false;
 
         /**
+         * {@link InsetsState.InternalInsetsType}s to be applied to the window
+         * If {@link #type} has the predefined insets (like {@link #TYPE_STATUS_BAR} or
+         * {@link #TYPE_NAVIGATION_BAR}), this field will be ignored.
+         *
+         * <p>Note: provide only one inset corresponding to the window type (like
+         * {@link InsetsState.InternalInsetsType#ITYPE_STATUS_BAR} or
+         * {@link InsetsState.InternalInsetsType#ITYPE_NAVIGATION_BAR})</p>
+         * @hide
+         */
+        public @InsetsState.InternalInsetsType int[] providesInsetsTypes;
+
+        /**
          * Specifies types of insets that this window should avoid overlapping during layout.
          *
          * @param types which types of insets that this window should avoid. The initial value of
@@ -3116,6 +3129,12 @@
             out.writeInt(mFitInsetsSides);
             out.writeBoolean(mFitInsetsIgnoringVisibility);
             out.writeBoolean(preferMinimalPostProcessing);
+            if (providesInsetsTypes != null) {
+                out.writeInt(providesInsetsTypes.length);
+                out.writeIntArray(providesInsetsTypes);
+            } else {
+                out.writeInt(0);
+            }
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR
@@ -3177,6 +3196,11 @@
             mFitInsetsSides = in.readInt();
             mFitInsetsIgnoringVisibility = in.readBoolean();
             preferMinimalPostProcessing = in.readBoolean();
+            int insetsTypesLength = in.readInt();
+            if (insetsTypesLength > 0) {
+                providesInsetsTypes = new int[insetsTypesLength];
+                in.readIntArray(providesInsetsTypes);
+            }
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -3437,6 +3461,11 @@
                 changes |= LAYOUT_CHANGED;
             }
 
+            if (!Arrays.equals(providesInsetsTypes, o.providesInsetsTypes)) {
+                providesInsetsTypes = o.providesInsetsTypes;
+                changes |= LAYOUT_CHANGED;
+            }
+
             return changes;
         }
 
@@ -3609,6 +3638,14 @@
                 sb.append(System.lineSeparator());
                 sb.append(prefix).append("  fitIgnoreVis");
             }
+            if (providesInsetsTypes != null) {
+                sb.append(System.lineSeparator());
+                sb.append(prefix).append("  insetsTypes=");
+                for (int i = 0; i < providesInsetsTypes.length; ++i) {
+                    if (i > 0) sb.append(' ');
+                    sb.append(InsetsState.typeToString(providesInsetsTypes[i]));
+                }
+            }
 
             sb.append('}');
             return sb.toString();
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
index 3cc04b8..3788e2b 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ b/core/java/android/view/inline/InlinePresentationSpec.java
@@ -58,7 +58,7 @@
 
 
 
-    // Code below generated by codegen v1.0.14.
+    // Code below generated by codegen v1.0.15.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -104,8 +104,8 @@
     }
 
     /**
-     * The extras encoding the UI style information. Defaults to null in which case the default
-     * system UI style will be used.
+     * The extras encoding the UI style information. Defaults to {@code null} in which case the
+     * default system UI style will be used.
      */
     @DataClass.Generated.Member
     public @Nullable Bundle getStyle() {
@@ -244,11 +244,11 @@
         }
 
         /**
-         * The extras encoding the UI style information. Defaults to null in which case the default
-         * system UI style will be used.
+         * The extras encoding the UI style information. Defaults to {@code null} in which case the
+         * default system UI style will be used.
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setStyle(@Nullable Bundle value) {
+        public @NonNull Builder setStyle(@NonNull Bundle value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x4;
             mStyle = value;
@@ -279,8 +279,8 @@
     }
 
     @DataClass.Generated(
-            time = 1582078731418L,
-            codegenVersion = "1.0.14",
+            time = 1584067238741L,
+            codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
             inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable android.os.Bundle mStyle\nprivate static  android.os.Bundle defaultStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 8e8c7df..2945a86 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -72,7 +72,6 @@
     /**
      * The extras state propagated from the IME to pass extra data.
      */
-    @DataClass.MaySetToNull
     private @Nullable Bundle mExtras;
 
     /**
@@ -81,7 +80,6 @@
      *
      * @hide
      */
-    @DataClass.MaySetToNull
     private @Nullable IBinder mHostInputToken;
 
     /**
@@ -498,7 +496,7 @@
          * The extras state propagated from the IME to pass extra data.
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setExtras(@Nullable Bundle value) {
+        public @NonNull Builder setExtras(@NonNull Bundle value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x10;
             mExtras = value;
@@ -513,7 +511,7 @@
          */
         @DataClass.Generated.Member
         @Override
-        @NonNull Builder setHostInputToken(@Nullable IBinder value) {
+        @NonNull Builder setHostInputToken(@NonNull IBinder value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x20;
             mHostInputToken = value;
@@ -578,10 +576,10 @@
     }
 
     @DataClass.Generated(
-            time = 1583975428858L,
+            time = 1584067152935L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.os.Bundle mExtras\nprivate @com.android.internal.util.DataClass.MaySetToNull @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic  void setHostInputToken(android.os.IBinder)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate  int mHostDisplayId\npublic  void setHostInputToken(android.os.IBinder)\nprivate  void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic  void setHostDisplayId(int)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
deleted file mode 100644
index 3164567..0000000
--- a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.KeyValueListParser;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-import java.lang.ref.WeakReference;
-import java.util.Objects;
-import java.util.function.Supplier;
-
-/**
- * Parses the {@link Settings.Global#TEXT_CLASSIFIER_ACTION_MODEL_PARAMS} flag.
- *
- * @hide
- */
-public final class ActionsModelParamsSupplier implements
-        Supplier<ActionsModelParamsSupplier.ActionsModelParams> {
-    private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
-
-    @VisibleForTesting
-    static final String KEY_REQUIRED_MODEL_VERSION = "required_model_version";
-    @VisibleForTesting
-    static final String KEY_REQUIRED_LOCALES = "required_locales";
-    @VisibleForTesting
-    static final String KEY_SERIALIZED_PRECONDITIONS = "serialized_preconditions";
-
-    private final Context mAppContext;
-    private final SettingsObserver mSettingsObserver;
-
-    private final Object mLock = new Object();
-    private final Runnable mOnChangedListener;
-    @Nullable
-    @GuardedBy("mLock")
-    private ActionsModelParams mActionsModelParams;
-    @GuardedBy("mLock")
-    private boolean mParsed = true;
-
-    public ActionsModelParamsSupplier(Context context, @Nullable Runnable onChangedListener) {
-        final Context appContext = Preconditions.checkNotNull(context).getApplicationContext();
-        // Some contexts don't have an app context.
-        mAppContext = appContext != null ? appContext : context;
-        mOnChangedListener = onChangedListener == null ? () -> {} : onChangedListener;
-        mSettingsObserver = new SettingsObserver(mAppContext, () -> {
-            synchronized (mLock) {
-                Log.v(TAG, "Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS is updated");
-                mParsed = true;
-                mOnChangedListener.run();
-            }
-        });
-    }
-
-    /**
-     * Returns the parsed actions params or {@link ActionsModelParams#INVALID} if the value is
-     * invalid.
-     */
-    @Override
-    public ActionsModelParams get() {
-        synchronized (mLock) {
-            if (mParsed) {
-                mActionsModelParams = parse(mAppContext.getContentResolver());
-                mParsed = false;
-            }
-        }
-        return mActionsModelParams;
-    }
-
-    private ActionsModelParams parse(ContentResolver contentResolver) {
-        String settingStr = Settings.Global.getString(contentResolver,
-                Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS);
-        if (TextUtils.isEmpty(settingStr)) {
-            return ActionsModelParams.INVALID;
-        }
-        try {
-            KeyValueListParser keyValueListParser = new KeyValueListParser(',');
-            keyValueListParser.setString(settingStr);
-            int version = keyValueListParser.getInt(KEY_REQUIRED_MODEL_VERSION, -1);
-            if (version == -1) {
-                Log.w(TAG, "ActionsModelParams.Parse, invalid model version");
-                return ActionsModelParams.INVALID;
-            }
-            String locales = keyValueListParser.getString(KEY_REQUIRED_LOCALES, null);
-            if (locales == null) {
-                Log.w(TAG, "ActionsModelParams.Parse, invalid locales");
-                return ActionsModelParams.INVALID;
-            }
-            String serializedPreconditionsStr =
-                    keyValueListParser.getString(KEY_SERIALIZED_PRECONDITIONS, null);
-            if (serializedPreconditionsStr == null) {
-                Log.w(TAG, "ActionsModelParams.Parse, invalid preconditions");
-                return ActionsModelParams.INVALID;
-            }
-            byte[] serializedPreconditions =
-                    Base64.decode(serializedPreconditionsStr, Base64.NO_WRAP);
-            return new ActionsModelParams(version, locales, serializedPreconditions);
-        } catch (Throwable t) {
-            Log.e(TAG, "Invalid TEXT_CLASSIFIER_ACTION_MODEL_PARAMS, ignore", t);
-        }
-        return ActionsModelParams.INVALID;
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            mAppContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /**
-     * Represents the parsed result.
-     */
-    public static final class ActionsModelParams {
-
-        public static final ActionsModelParams INVALID =
-                new ActionsModelParams(-1, "", new byte[0]);
-
-        /**
-         * The required model version to apply {@code mSerializedPreconditions}.
-         */
-        private final int mRequiredModelVersion;
-
-        /**
-         * The required model locales to apply {@code mSerializedPreconditions}.
-         */
-        private final String mRequiredModelLocales;
-
-        /**
-         * The serialized params that will be applied to the model file, if all requirements are
-         * met. Do not modify.
-         */
-        private final byte[] mSerializedPreconditions;
-
-        public ActionsModelParams(int requiredModelVersion, String requiredModelLocales,
-                byte[] serializedPreconditions) {
-            mRequiredModelVersion = requiredModelVersion;
-            mRequiredModelLocales = Preconditions.checkNotNull(requiredModelLocales);
-            mSerializedPreconditions = Preconditions.checkNotNull(serializedPreconditions);
-        }
-
-        /**
-         * Returns the serialized preconditions. Returns {@code null} if the the model in use does
-         * not meet all the requirements listed in the {@code ActionsModelParams} or the params
-         * are invalid.
-         */
-        @Nullable
-        public byte[] getSerializedPreconditions(ModelFileManager.ModelFile modelInUse) {
-            if (this == INVALID) {
-                return null;
-            }
-            if (modelInUse.getVersion() != mRequiredModelVersion) {
-                Log.w(TAG, String.format(
-                        "Not applying mSerializedPreconditions, required version=%d, actual=%d",
-                        mRequiredModelVersion, modelInUse.getVersion()));
-                return null;
-            }
-            if (!Objects.equals(modelInUse.getSupportedLocalesStr(), mRequiredModelLocales)) {
-                Log.w(TAG, String.format(
-                        "Not applying mSerializedPreconditions, required locales=%s, actual=%s",
-                        mRequiredModelLocales, modelInUse.getSupportedLocalesStr()));
-                return null;
-            }
-            return mSerializedPreconditions;
-        }
-    }
-
-    private static final class SettingsObserver extends ContentObserver {
-
-        private final WeakReference<Runnable> mOnChangedListener;
-
-        SettingsObserver(Context appContext, Runnable listener) {
-            super(null);
-            mOnChangedListener = new WeakReference<>(listener);
-            appContext.getContentResolver().registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.TEXT_CLASSIFIER_ACTION_MODEL_PARAMS),
-                    false /* notifyForDescendants */,
-                    this);
-        }
-
-        public void onChange(boolean selfChange) {
-            if (mOnChangedListener.get() != null) {
-                mOnChangedListener.get().run();
-            }
-        }
-    }
-}
diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
deleted file mode 100644
index 3ed48f6..0000000
--- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.app.Person;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Pair;
-import android.view.textclassifier.intent.LabeledIntent;
-import android.view.textclassifier.intent.TemplateIntentFactory;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.android.textclassifier.ActionsSuggestionsModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Deque;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.StringJoiner;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * Helper class for action suggestions.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class ActionsSuggestionsHelper {
-    private static final String TAG = "ActionsSuggestions";
-    private static final int USER_LOCAL = 0;
-    private static final int FIRST_NON_LOCAL_USER = 1;
-
-    private ActionsSuggestionsHelper() {}
-
-    /**
-     * Converts the messages to a list of native messages object that the model can understand.
-     * <p>
-     * User id encoding - local user is represented as 0, Other users are numbered according to
-     * how far before they spoke last time in the conversation. For example, considering this
-     * conversation:
-     * <ul>
-     * <li> User A: xxx
-     * <li> Local user: yyy
-     * <li> User B: zzz
-     * </ul>
-     * User A will be encoded as 2, user B will be encoded as 1 and local user will be encoded as 0.
-     */
-    public static ActionsSuggestionsModel.ConversationMessage[] toNativeMessages(
-            List<ConversationActions.Message> messages,
-            Function<CharSequence, String> languageDetector) {
-        List<ConversationActions.Message> messagesWithText =
-                messages.stream()
-                        .filter(message -> !TextUtils.isEmpty(message.getText()))
-                        .collect(Collectors.toCollection(ArrayList::new));
-        if (messagesWithText.isEmpty()) {
-            return new ActionsSuggestionsModel.ConversationMessage[0];
-        }
-        Deque<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayDeque<>();
-        PersonEncoder personEncoder = new PersonEncoder();
-        int size = messagesWithText.size();
-        for (int i = size - 1; i >= 0; i--) {
-            ConversationActions.Message message = messagesWithText.get(i);
-            long referenceTime = message.getReferenceTime() == null
-                    ? 0
-                    : message.getReferenceTime().toInstant().toEpochMilli();
-            String timeZone = message.getReferenceTime() == null
-                    ? null
-                    : message.getReferenceTime().getZone().getId();
-            nativeMessages.push(new ActionsSuggestionsModel.ConversationMessage(
-                    personEncoder.encode(message.getAuthor()),
-                    message.getText().toString(), referenceTime, timeZone,
-                    languageDetector.apply(message.getText())));
-        }
-        return nativeMessages.toArray(
-                new ActionsSuggestionsModel.ConversationMessage[nativeMessages.size()]);
-    }
-
-    /**
-     * Returns the result id for logging.
-     */
-    public static String createResultId(
-            Context context,
-            List<ConversationActions.Message> messages,
-            int modelVersion,
-            List<Locale> modelLocales) {
-        final StringJoiner localesJoiner = new StringJoiner(",");
-        for (Locale locale : modelLocales) {
-            localesJoiner.add(locale.toLanguageTag());
-        }
-        final String modelName = String.format(
-                Locale.US, "%s_v%d", localesJoiner.toString(), modelVersion);
-        final int hash = Objects.hash(
-                messages.stream().mapToInt(ActionsSuggestionsHelper::hashMessage),
-                context.getPackageName(),
-                System.currentTimeMillis());
-        return SelectionSessionLogger.SignatureParser.createSignature(
-                SelectionSessionLogger.CLASSIFIER_ID, modelName, hash);
-    }
-
-    /**
-     * Generated labeled intent from an action suggestion and return the resolved result.
-     */
-    @Nullable
-    public static LabeledIntent.Result createLabeledIntentResult(
-            Context context,
-            TemplateIntentFactory templateIntentFactory,
-            ActionsSuggestionsModel.ActionSuggestion nativeSuggestion) {
-        RemoteActionTemplate[] remoteActionTemplates =
-                nativeSuggestion.getRemoteActionTemplates();
-        if (remoteActionTemplates == null) {
-            Log.w(TAG, "createRemoteAction: Missing template for type "
-                    + nativeSuggestion.getActionType());
-            return null;
-        }
-        List<LabeledIntent> labeledIntents = templateIntentFactory.create(remoteActionTemplates);
-        if (labeledIntents.isEmpty()) {
-            return null;
-        }
-        // Given that we only support implicit intent here, we should expect there is just one
-        // intent for each action type.
-        LabeledIntent.TitleChooser titleChooser =
-                ActionsSuggestionsHelper.createTitleChooser(nativeSuggestion.getActionType());
-        return labeledIntents.get(0).resolve(context, titleChooser, null);
-    }
-
-    /**
-     * Returns a {@link LabeledIntent.TitleChooser} for conversation actions use case.
-     */
-    @Nullable
-    public static LabeledIntent.TitleChooser createTitleChooser(String actionType) {
-        if (ConversationAction.TYPE_OPEN_URL.equals(actionType)) {
-            return (labeledIntent, resolveInfo) -> {
-                if (resolveInfo.handleAllWebDataURI) {
-                    return labeledIntent.titleWithEntity;
-                }
-                if ("android".equals(resolveInfo.activityInfo.packageName)) {
-                    return labeledIntent.titleWithEntity;
-                }
-                return labeledIntent.titleWithoutEntity;
-            };
-        }
-        return null;
-    }
-
-    /**
-     * Returns a list of {@link ConversationAction}s that have 0 duplicates. Two actions are
-     * duplicates if they may look the same to users. This function assumes every
-     * ConversationActions with a non-null RemoteAction also have a non-null intent in the extras.
-     */
-    public static List<ConversationAction> removeActionsWithDuplicates(
-            List<ConversationAction> conversationActions) {
-        // Ideally, we should compare title and icon here, but comparing icon is expensive and thus
-        // we use the component name of the target handler as the heuristic.
-        Map<Pair<String, String>, Integer> counter = new ArrayMap<>();
-        for (ConversationAction conversationAction : conversationActions) {
-            Pair<String, String> representation = getRepresentation(conversationAction);
-            if (representation == null) {
-                continue;
-            }
-            Integer existingCount = counter.getOrDefault(representation, 0);
-            counter.put(representation, existingCount + 1);
-        }
-        List<ConversationAction> result = new ArrayList<>();
-        for (ConversationAction conversationAction : conversationActions) {
-            Pair<String, String> representation = getRepresentation(conversationAction);
-            if (representation == null || counter.getOrDefault(representation, 0) == 1) {
-                result.add(conversationAction);
-            }
-        }
-        return result;
-    }
-
-    @Nullable
-    private static Pair<String, String> getRepresentation(
-            ConversationAction conversationAction) {
-        RemoteAction remoteAction = conversationAction.getAction();
-        if (remoteAction == null) {
-            return null;
-        }
-        Intent actionIntent = ExtrasUtils.getActionIntent(conversationAction.getExtras());
-        ComponentName componentName = actionIntent.getComponent();
-        // Action without a component name will be considered as from the same app.
-        String packageName = componentName == null ? null : componentName.getPackageName();
-        return new Pair<>(
-                conversationAction.getAction().getTitle().toString(), packageName);
-    }
-
-    private static final class PersonEncoder {
-        private final Map<Person, Integer> mMapping = new ArrayMap<>();
-        private int mNextUserId = FIRST_NON_LOCAL_USER;
-
-        private int encode(Person person) {
-            if (ConversationActions.Message.PERSON_USER_SELF.equals(person)) {
-                return USER_LOCAL;
-            }
-            Integer result = mMapping.get(person);
-            if (result == null) {
-                mMapping.put(person, mNextUserId);
-                result = mNextUserId;
-                mNextUserId++;
-            }
-            return result;
-        }
-    }
-
-    private static int hashMessage(ConversationActions.Message message) {
-        return Objects.hash(message.getAuthor(), message.getText(), message.getReferenceTime());
-    }
-}
diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java
index 11e0e2c..9e2b642 100644
--- a/core/java/android/view/textclassifier/ExtrasUtils.java
+++ b/core/java/android/view/textclassifier/ExtrasUtils.java
@@ -19,15 +19,9 @@
 import android.annotation.Nullable;
 import android.app.RemoteAction;
 import android.content.Intent;
-import android.icu.util.ULocale;
 import android.os.Bundle;
 
-import com.android.internal.util.ArrayUtils;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
 import java.util.ArrayList;
-import java.util.List;
 
 /**
  * Utility class for inserting and retrieving data in TextClassifier request/response extras.
@@ -37,52 +31,19 @@
 public final class ExtrasUtils {
 
     // Keys for response objects.
-    private static final String SERIALIZED_ENTITIES_DATA = "serialized-entities-data";
-    private static final String ENTITIES_EXTRAS = "entities-extras";
     private static final String ACTION_INTENT = "action-intent";
     private static final String ACTIONS_INTENTS = "actions-intents";
     private static final String FOREIGN_LANGUAGE = "foreign-language";
     private static final String ENTITY_TYPE = "entity-type";
     private static final String SCORE = "score";
-    private static final String MODEL_VERSION = "model-version";
     private static final String MODEL_NAME = "model-name";
-    private static final String TEXT_LANGUAGES = "text-languages";
-    private static final String ENTITIES = "entities";
 
-    // Keys for request objects.
-    private static final String IS_SERIALIZED_ENTITY_DATA_ENABLED =
-            "is-serialized-entity-data-enabled";
-
-    private ExtrasUtils() {}
-
-    /**
-     * Bundles and returns foreign language detection information for TextClassifier responses.
-     */
-    static Bundle createForeignLanguageExtra(
-            String language, float score, int modelVersion) {
-        final Bundle bundle = new Bundle();
-        bundle.putString(ENTITY_TYPE, language);
-        bundle.putFloat(SCORE, score);
-        bundle.putInt(MODEL_VERSION, modelVersion);
-        bundle.putString(MODEL_NAME, "langId_v" + modelVersion);
-        return bundle;
-    }
-
-    /**
-     * Stores {@code extra} as foreign language information in TextClassifier response object's
-     * extras {@code container}.
-     *
-     * @see #getForeignLanguageExtra(TextClassification)
-     */
-    static void putForeignLanguageExtra(Bundle container, Bundle extra) {
-        container.putParcelable(FOREIGN_LANGUAGE, extra);
+    private ExtrasUtils() {
     }
 
     /**
      * Returns foreign language detection information contained in the TextClassification object.
      * responses.
-     *
-     * @see #putForeignLanguageExtra(Bundle, Bundle)
      */
     @Nullable
     public static Bundle getForeignLanguageExtra(@Nullable TextClassification classification) {
@@ -93,72 +54,6 @@
     }
 
     /**
-     * @see #getTopLanguage(Intent)
-     */
-    static void putTopLanguageScores(Bundle container, EntityConfidence languageScores) {
-        final int maxSize = Math.min(3, languageScores.getEntities().size());
-        final String[] languages = languageScores.getEntities().subList(0, maxSize)
-                .toArray(new String[0]);
-        final float[] scores = new float[languages.length];
-        for (int i = 0; i < languages.length; i++) {
-            scores[i] = languageScores.getConfidenceScore(languages[i]);
-        }
-        container.putStringArray(ENTITY_TYPE, languages);
-        container.putFloatArray(SCORE, scores);
-    }
-
-    /**
-     * @see #putTopLanguageScores(Bundle, EntityConfidence)
-     */
-    @Nullable
-    public static ULocale getTopLanguage(@Nullable Intent intent) {
-        if (intent == null) {
-            return null;
-        }
-        final Bundle tcBundle = intent.getBundleExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER);
-        if (tcBundle == null) {
-            return null;
-        }
-        final Bundle textLanguagesExtra = tcBundle.getBundle(TEXT_LANGUAGES);
-        if (textLanguagesExtra == null) {
-            return null;
-        }
-        final String[] languages = textLanguagesExtra.getStringArray(ENTITY_TYPE);
-        final float[] scores = textLanguagesExtra.getFloatArray(SCORE);
-        if (languages == null || scores == null
-                || languages.length == 0 || languages.length != scores.length) {
-            return null;
-        }
-        int highestScoringIndex = 0;
-        for (int i = 1; i < languages.length; i++) {
-            if (scores[highestScoringIndex] < scores[i]) {
-                highestScoringIndex = i;
-            }
-        }
-        return ULocale.forLanguageTag(languages[highestScoringIndex]);
-    }
-
-    public static void putTextLanguagesExtra(Bundle container, Bundle extra) {
-        container.putBundle(TEXT_LANGUAGES, extra);
-    }
-
-    /**
-     * Stores {@code actionIntents} information in TextClassifier response object's extras
-     * {@code container}.
-     */
-    static void putActionsIntents(Bundle container, ArrayList<Intent> actionsIntents) {
-        container.putParcelableArrayList(ACTIONS_INTENTS, actionsIntents);
-    }
-
-    /**
-     * Stores {@code actionIntents} information in TextClassifier response object's extras
-     * {@code container}.
-     */
-    public static void putActionIntent(Bundle container, @Nullable Intent actionIntent) {
-        container.putParcelable(ACTION_INTENT, actionIntent);
-    }
-
-    /**
      * Returns {@code actionIntent} information contained in a TextClassifier response object.
      */
     @Nullable
@@ -167,48 +62,6 @@
     }
 
     /**
-     * Stores serialized entity data information in TextClassifier response object's extras
-     * {@code container}.
-     */
-    public static void putSerializedEntityData(
-            Bundle container, @Nullable byte[] serializedEntityData) {
-        container.putByteArray(SERIALIZED_ENTITIES_DATA, serializedEntityData);
-    }
-
-    /**
-     * Returns serialized entity data information contained in a TextClassifier response
-     * object.
-     */
-    @Nullable
-    public static byte[] getSerializedEntityData(Bundle container) {
-        return container.getByteArray(SERIALIZED_ENTITIES_DATA);
-    }
-
-    /**
-     * Stores {@code entities} information in TextClassifier response object's extras
-     * {@code container}.
-     *
-     * @see {@link #getCopyText(Bundle)}
-     */
-    public static void putEntitiesExtras(Bundle container, @Nullable Bundle entitiesExtras) {
-        container.putParcelable(ENTITIES_EXTRAS, entitiesExtras);
-    }
-
-    /**
-     * Returns {@code entities} information contained in a TextClassifier response object.
-     *
-     * @see {@link #putEntitiesExtras(Bundle, Bundle)}
-     */
-    @Nullable
-    public static String getCopyText(Bundle container) {
-        Bundle entitiesExtras = container.getParcelable(ENTITIES_EXTRAS);
-        if (entitiesExtras == null) {
-            return null;
-        }
-        return entitiesExtras.getString("text");
-    }
-
-    /**
      * Returns {@code actionIntents} information contained in the TextClassification object.
      */
     @Nullable
@@ -224,7 +77,7 @@
      * action string, {@code intentAction}.
      */
     @Nullable
-    public static RemoteAction findAction(
+    private static RemoteAction findAction(
             @Nullable TextClassification classification, @Nullable String intentAction) {
         if (classification == null || intentAction == null) {
             return null;
@@ -283,53 +136,4 @@
         }
         return extra.getString(MODEL_NAME);
     }
-
-    /**
-     * Stores the entities from {@link AnnotatorModel.ClassificationResult} in {@code container}.
-     */
-    public static void putEntities(
-            Bundle container,
-            @Nullable AnnotatorModel.ClassificationResult[] classifications) {
-        if (ArrayUtils.isEmpty(classifications)) {
-            return;
-        }
-        ArrayList<Bundle> entitiesBundle = new ArrayList<>();
-        for (AnnotatorModel.ClassificationResult classification : classifications) {
-            if (classification == null) {
-                continue;
-            }
-            Bundle entityBundle = new Bundle();
-            entityBundle.putString(ENTITY_TYPE, classification.getCollection());
-            entityBundle.putByteArray(
-                    SERIALIZED_ENTITIES_DATA,
-                    classification.getSerializedEntityData());
-            entitiesBundle.add(entityBundle);
-        }
-        if (!entitiesBundle.isEmpty()) {
-            container.putParcelableArrayList(ENTITIES, entitiesBundle);
-        }
-    }
-
-    /**
-     * Returns a list of entities contained in the {@code extra}.
-     */
-    @Nullable
-    public static List<Bundle> getEntities(Bundle container) {
-        return container.getParcelableArrayList(ENTITIES);
-    }
-
-    /**
-     * Whether the annotator should populate serialized entity data into the result object.
-     */
-    public static boolean isSerializedEntityDataEnabled(TextLinks.Request request) {
-        return request.getExtras().getBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED);
-    }
-
-    /**
-     * To indicate whether the annotator should populate serialized entity data in the result
-     * object.
-     */
-    public static void putIsSerializedEntityDataEnabled(Bundle bundle, boolean isEnabled) {
-        bundle.putBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED, isEnabled);
-    }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/GenerateLinksLogger.java b/core/java/android/view/textclassifier/GenerateLinksLogger.java
deleted file mode 100644
index 17ec73a..0000000
--- a/core/java/android/view/textclassifier/GenerateLinksLogger.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.Nullable;
-import android.metrics.LogMaker;
-import android.util.ArrayMap;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Random;
-import java.util.UUID;
-
-/**
- * A helper for logging calls to generateLinks.
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class GenerateLinksLogger {
-
-    private static final String LOG_TAG = "GenerateLinksLogger";
-    private static final String ZERO = "0";
-
-    private final MetricsLogger mMetricsLogger;
-    private final Random mRng;
-    private final int mSampleRate;
-
-    /**
-     * @param sampleRate the rate at which log events are written. (e.g. 100 means there is a 0.01
-     *                   chance that a call to logGenerateLinks results in an event being written).
-     *                   To write all events, pass 1.
-     */
-    public GenerateLinksLogger(int sampleRate) {
-        mSampleRate = sampleRate;
-        mRng = new Random(System.nanoTime());
-        mMetricsLogger = new MetricsLogger();
-    }
-
-    @VisibleForTesting
-    public GenerateLinksLogger(int sampleRate, MetricsLogger metricsLogger) {
-        mSampleRate = sampleRate;
-        mRng = new Random(System.nanoTime());
-        mMetricsLogger = metricsLogger;
-    }
-
-    /** Logs statistics about a call to generateLinks. */
-    public void logGenerateLinks(CharSequence text, TextLinks links, String callingPackageName,
-            long latencyMs) {
-        Objects.requireNonNull(text);
-        Objects.requireNonNull(links);
-        Objects.requireNonNull(callingPackageName);
-        if (!shouldLog()) {
-            return;
-        }
-
-        // Always populate the total stats, and per-entity stats for each entity type detected.
-        final LinkifyStats totalStats = new LinkifyStats();
-        final Map<String, LinkifyStats> perEntityTypeStats = new ArrayMap<>();
-        for (TextLinks.TextLink link : links.getLinks()) {
-            if (link.getEntityCount() == 0) continue;
-            final String entityType = link.getEntity(0);
-            if (entityType == null
-                    || TextClassifier.TYPE_OTHER.equals(entityType)
-                    || TextClassifier.TYPE_UNKNOWN.equals(entityType)) {
-                continue;
-            }
-            totalStats.countLink(link);
-            perEntityTypeStats.computeIfAbsent(entityType, k -> new LinkifyStats()).countLink(link);
-        }
-
-        final String callId = UUID.randomUUID().toString();
-        writeStats(callId, callingPackageName, null, totalStats, text, latencyMs);
-        for (Map.Entry<String, LinkifyStats> entry : perEntityTypeStats.entrySet()) {
-            writeStats(callId, callingPackageName, entry.getKey(), entry.getValue(), text,
-                       latencyMs);
-        }
-    }
-
-    /**
-     * Returns whether this particular event should be logged.
-     *
-     * Sampling is used to reduce the amount of logging data generated.
-     **/
-    private boolean shouldLog() {
-        if (mSampleRate <= 1) {
-            return true;
-        } else {
-            return mRng.nextInt(mSampleRate) == 0;
-        }
-    }
-
-    /** Writes a log event for the given stats. */
-    private void writeStats(String callId, String callingPackageName, @Nullable String entityType,
-                            LinkifyStats stats, CharSequence text, long latencyMs) {
-        final LogMaker log = new LogMaker(MetricsEvent.TEXT_CLASSIFIER_GENERATE_LINKS)
-                .setPackageName(callingPackageName)
-                .addTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID, callId)
-                .addTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS, stats.mNumLinks)
-                .addTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH, stats.mNumLinksTextLength)
-                .addTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH, text.length())
-                .addTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY, latencyMs);
-        if (entityType != null) {
-            log.addTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE, entityType);
-        }
-        mMetricsLogger.write(log);
-        debugLog(log);
-    }
-
-    private static void debugLog(LogMaker log) {
-        if (!Log.ENABLE_FULL_LOGGING) {
-            return;
-        }
-        final String callId = Objects.toString(
-                log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
-        final String entityType = Objects.toString(
-                log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "ANY_ENTITY");
-        final int numLinks = Integer.parseInt(
-                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS), ZERO));
-        final int linkLength = Integer.parseInt(
-                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH), ZERO));
-        final int textLength = Integer.parseInt(
-                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH), ZERO));
-        final int latencyMs = Integer.parseInt(
-                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
-
-        Log.v(LOG_TAG,
-                String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
-                        numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
-    }
-
-    /** Helper class for storing per-entity type statistics. */
-    private static final class LinkifyStats {
-        int mNumLinks;
-        int mNumLinksTextLength;
-
-        void countLink(TextLinks.TextLink link) {
-            mNumLinks += 1;
-            mNumLinksTextLength += link.getEnd() - link.getStart();
-        }
-    }
-}
diff --git a/core/java/android/view/textclassifier/Log.java b/core/java/android/view/textclassifier/Log.java
index 03ed496..98ee09c 100644
--- a/core/java/android/view/textclassifier/Log.java
+++ b/core/java/android/view/textclassifier/Log.java
@@ -32,7 +32,7 @@
      * false: Limits logging to debug level.
      */
     static final boolean ENABLE_FULL_LOGGING =
-            android.util.Log.isLoggable(TextClassifier.DEFAULT_LOG_TAG, android.util.Log.VERBOSE);
+            android.util.Log.isLoggable(TextClassifier.LOG_TAG, android.util.Log.VERBOSE);
 
     private Log() {
     }
diff --git a/core/java/android/view/textclassifier/ModelFileManager.java b/core/java/android/view/textclassifier/ModelFileManager.java
deleted file mode 100644
index 0a4ff5d..0000000
--- a/core/java/android/view/textclassifier/ModelFileManager.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static android.view.textclassifier.TextClassifier.DEFAULT_LOG_TAG;
-
-import android.annotation.Nullable;
-import android.os.LocaleList;
-import android.os.ParcelFileDescriptor;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.StringJoiner;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Manages model files that are listed by the model files supplier.
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class ModelFileManager {
-    private final Object mLock = new Object();
-    private final Supplier<List<ModelFile>> mModelFileSupplier;
-
-    private List<ModelFile> mModelFiles;
-
-    public ModelFileManager(Supplier<List<ModelFile>> modelFileSupplier) {
-        mModelFileSupplier = Objects.requireNonNull(modelFileSupplier);
-    }
-
-    /**
-     * Returns an unmodifiable list of model files listed by the given model files supplier.
-     * <p>
-     * The result is cached.
-     */
-    public List<ModelFile> listModelFiles() {
-        synchronized (mLock) {
-            if (mModelFiles == null) {
-                mModelFiles = Collections.unmodifiableList(mModelFileSupplier.get());
-            }
-            return mModelFiles;
-        }
-    }
-
-    /**
-     * Returns the best model file for the given localelist, {@code null} if nothing is found.
-     *
-     * @param localeList the required locales, use {@code null} if there is no preference.
-     */
-    public ModelFile findBestModelFile(@Nullable LocaleList localeList) {
-        final String languages = localeList == null || localeList.isEmpty()
-                ? LocaleList.getDefault().toLanguageTags()
-                : localeList.toLanguageTags();
-        final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
-
-        ModelFile bestModel = null;
-        for (ModelFile model : listModelFiles()) {
-            if (model.isAnyLanguageSupported(languageRangeList)) {
-                if (model.isPreferredTo(bestModel)) {
-                    bestModel = model;
-                }
-            }
-        }
-        return bestModel;
-    }
-
-    /**
-     * Default implementation of the model file supplier.
-     */
-    public static final class ModelFileSupplierImpl implements Supplier<List<ModelFile>> {
-        private final File mUpdatedModelFile;
-        private final File mFactoryModelDir;
-        private final Pattern mModelFilenamePattern;
-        private final Function<Integer, Integer> mVersionSupplier;
-        private final Function<Integer, String> mSupportedLocalesSupplier;
-
-        public ModelFileSupplierImpl(
-                File factoryModelDir,
-                String factoryModelFileNameRegex,
-                File updatedModelFile,
-                Function<Integer, Integer> versionSupplier,
-                Function<Integer, String> supportedLocalesSupplier) {
-            mUpdatedModelFile = Objects.requireNonNull(updatedModelFile);
-            mFactoryModelDir = Objects.requireNonNull(factoryModelDir);
-            mModelFilenamePattern = Pattern.compile(
-                    Objects.requireNonNull(factoryModelFileNameRegex));
-            mVersionSupplier = Objects.requireNonNull(versionSupplier);
-            mSupportedLocalesSupplier = Objects.requireNonNull(supportedLocalesSupplier);
-        }
-
-        @Override
-        public List<ModelFile> get() {
-            final List<ModelFile> modelFiles = new ArrayList<>();
-            // The update model has the highest precedence.
-            if (mUpdatedModelFile.exists()) {
-                final ModelFile updatedModel = createModelFile(mUpdatedModelFile);
-                if (updatedModel != null) {
-                    modelFiles.add(updatedModel);
-                }
-            }
-            // Factory models should never have overlapping locales, so the order doesn't matter.
-            if (mFactoryModelDir.exists() && mFactoryModelDir.isDirectory()) {
-                final File[] files = mFactoryModelDir.listFiles();
-                for (File file : files) {
-                    final Matcher matcher = mModelFilenamePattern.matcher(file.getName());
-                    if (matcher.matches() && file.isFile()) {
-                        final ModelFile model = createModelFile(file);
-                        if (model != null) {
-                            modelFiles.add(model);
-                        }
-                    }
-                }
-            }
-            return modelFiles;
-        }
-
-        /** Returns null if the path did not point to a compatible model. */
-        @Nullable
-        private ModelFile createModelFile(File file) {
-            if (!file.exists()) {
-                return null;
-            }
-            ParcelFileDescriptor modelFd = null;
-            try {
-                modelFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
-                if (modelFd == null) {
-                    return null;
-                }
-                final int modelFdInt = modelFd.getFd();
-                final int version = mVersionSupplier.apply(modelFdInt);
-                final String supportedLocalesStr = mSupportedLocalesSupplier.apply(modelFdInt);
-                if (supportedLocalesStr.isEmpty()) {
-                    Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
-                    return null;
-                }
-                final List<Locale> supportedLocales = new ArrayList<>();
-                for (String langTag : supportedLocalesStr.split(",")) {
-                    supportedLocales.add(Locale.forLanguageTag(langTag));
-                }
-                return new ModelFile(
-                        file,
-                        version,
-                        supportedLocales,
-                        supportedLocalesStr,
-                        ModelFile.LANGUAGE_INDEPENDENT.equals(supportedLocalesStr));
-            } catch (FileNotFoundException e) {
-                Log.e(DEFAULT_LOG_TAG, "Failed to find " + file.getAbsolutePath(), e);
-                return null;
-            } finally {
-                maybeCloseAndLogError(modelFd);
-            }
-        }
-
-        /**
-         * Closes the ParcelFileDescriptor, if non-null, and logs any errors that occur.
-         */
-        private static void maybeCloseAndLogError(@Nullable ParcelFileDescriptor fd) {
-            if (fd == null) {
-                return;
-            }
-            try {
-                fd.close();
-            } catch (IOException e) {
-                Log.e(DEFAULT_LOG_TAG, "Error closing file.", e);
-            }
-        }
-
-    }
-
-    /**
-     * Describes TextClassifier model files on disk.
-     */
-    public static final class ModelFile {
-        public static final String LANGUAGE_INDEPENDENT = "*";
-
-        private final File mFile;
-        private final int mVersion;
-        private final List<Locale> mSupportedLocales;
-        private final String mSupportedLocalesStr;
-        private final boolean mLanguageIndependent;
-
-        public ModelFile(File file, int version, List<Locale> supportedLocales,
-                String supportedLocalesStr,
-                boolean languageIndependent) {
-            mFile = Objects.requireNonNull(file);
-            mVersion = version;
-            mSupportedLocales = Objects.requireNonNull(supportedLocales);
-            mSupportedLocalesStr = Objects.requireNonNull(supportedLocalesStr);
-            mLanguageIndependent = languageIndependent;
-        }
-
-        /** Returns the absolute path to the model file. */
-        public String getPath() {
-            return mFile.getAbsolutePath();
-        }
-
-        /** Returns a name to use for id generation, effectively the name of the model file. */
-        public String getName() {
-            return mFile.getName();
-        }
-
-        /** Returns the version tag in the model's metadata. */
-        public int getVersion() {
-            return mVersion;
-        }
-
-        /** Returns whether the language supports any language in the given ranges. */
-        public boolean isAnyLanguageSupported(List<Locale.LanguageRange> languageRanges) {
-            Objects.requireNonNull(languageRanges);
-            return mLanguageIndependent || Locale.lookup(languageRanges, mSupportedLocales) != null;
-        }
-
-        /** Returns an immutable lists of supported locales. */
-        public List<Locale> getSupportedLocales() {
-            return Collections.unmodifiableList(mSupportedLocales);
-        }
-
-        /** Returns the original supported locals string read from the model file. */
-        public String getSupportedLocalesStr() {
-            return mSupportedLocalesStr;
-        }
-
-        /**
-         * Returns if this model file is preferred to the given one.
-         */
-        public boolean isPreferredTo(@Nullable ModelFile model) {
-            // A model is preferred to no model.
-            if (model == null) {
-                return true;
-            }
-
-            // A language-specific model is preferred to a language independent
-            // model.
-            if (!mLanguageIndependent && model.mLanguageIndependent) {
-                return true;
-            }
-            if (mLanguageIndependent && !model.mLanguageIndependent) {
-                return false;
-            }
-
-            // A higher-version model is preferred.
-            if (mVersion > model.getVersion()) {
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(getPath());
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (this == other) {
-                return true;
-            }
-            if (other instanceof ModelFile) {
-                final ModelFile otherModel = (ModelFile) other;
-                return TextUtils.equals(getPath(), otherModel.getPath());
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            final StringJoiner localesJoiner = new StringJoiner(",");
-            for (Locale locale : mSupportedLocales) {
-                localesJoiner.add(locale.toLanguageTag());
-            }
-            return String.format(Locale.US,
-                    "ModelFile { path=%s name=%s version=%d locales=%s }",
-                    getPath(), getName(), mVersion, localesJoiner.toString());
-        }
-    }
-}
diff --git a/core/java/android/view/textclassifier/SelectionSessionLogger.java b/core/java/android/view/textclassifier/SelectionSessionLogger.java
index ae9f65b..e7d896e 100644
--- a/core/java/android/view/textclassifier/SelectionSessionLogger.java
+++ b/core/java/android/view/textclassifier/SelectionSessionLogger.java
@@ -16,251 +16,24 @@
 
 package android.view.textclassifier;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
-import android.metrics.LogMaker;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.text.BreakIterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.StringJoiner;
 
 /**
  * A helper for logging selection session events.
+ *
  * @hide
  */
 public final class SelectionSessionLogger {
-
-    private static final String LOG_TAG = "SelectionSessionLogger";
-    static final String CLASSIFIER_ID = "androidtc";
-
-    private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
-    private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
-    private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
-    private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
-    private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
-    private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
-    private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
-    private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
-    private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
-    private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
-    private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
-    private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
-
-    private static final String ZERO = "0";
-    private static final String UNKNOWN = "unknown";
-
-    private final MetricsLogger mMetricsLogger;
-
-    public SelectionSessionLogger() {
-        mMetricsLogger = new MetricsLogger();
-    }
-
-    @VisibleForTesting
-    public SelectionSessionLogger(@NonNull MetricsLogger metricsLogger) {
-        mMetricsLogger = Objects.requireNonNull(metricsLogger);
-    }
-
-    /** Emits a selection event to the logs. */
-    public void writeEvent(@NonNull SelectionEvent event) {
-        Objects.requireNonNull(event);
-        final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
-                .setType(getLogType(event))
-                .setSubtype(getLogSubType(event))
-                .setPackageName(event.getPackageName())
-                .addTaggedData(START_EVENT_DELTA, event.getDurationSinceSessionStart())
-                .addTaggedData(PREV_EVENT_DELTA, event.getDurationSincePreviousEvent())
-                .addTaggedData(INDEX, event.getEventIndex())
-                .addTaggedData(WIDGET_TYPE, event.getWidgetType())
-                .addTaggedData(WIDGET_VERSION, event.getWidgetVersion())
-                .addTaggedData(ENTITY_TYPE, event.getEntityType())
-                .addTaggedData(EVENT_START, event.getStart())
-                .addTaggedData(EVENT_END, event.getEnd());
-        if (isPlatformLocalTextClassifierSmartSelection(event.getResultId())) {
-            // Ensure result id and smart indices are only set for events with smart selection from
-            // the platform's textclassifier.
-            log.addTaggedData(MODEL_NAME, SignatureParser.getModelName(event.getResultId()))
-                    .addTaggedData(SMART_START, event.getSmartStart())
-                    .addTaggedData(SMART_END, event.getSmartEnd());
-        }
-        if (event.getSessionId() != null) {
-            log.addTaggedData(SESSION_ID, event.getSessionId().getValue());
-        }
-        mMetricsLogger.write(log);
-        debugLog(log);
-    }
-
-    private static int getLogType(SelectionEvent event) {
-        switch (event.getEventType()) {
-            case SelectionEvent.ACTION_OVERTYPE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
-            case SelectionEvent.ACTION_COPY:
-                return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
-            case SelectionEvent.ACTION_PASTE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
-            case SelectionEvent.ACTION_CUT:
-                return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
-            case SelectionEvent.ACTION_SHARE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
-            case SelectionEvent.ACTION_SMART_SHARE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
-            case SelectionEvent.ACTION_DRAG:
-                return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
-            case SelectionEvent.ACTION_ABANDON:
-                return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
-            case SelectionEvent.ACTION_OTHER:
-                return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
-            case SelectionEvent.ACTION_SELECT_ALL:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
-            case SelectionEvent.ACTION_RESET:
-                return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
-            case SelectionEvent.EVENT_SELECTION_STARTED:
-                return MetricsEvent.ACTION_TEXT_SELECTION_START;
-            case SelectionEvent.EVENT_SELECTION_MODIFIED:
-                return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
-            case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
-            case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
-            case SelectionEvent.EVENT_AUTO_SELECTION:
-                return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
-            default:
-                return MetricsEvent.VIEW_UNKNOWN;
-        }
-    }
-
-    private static int getLogSubType(SelectionEvent event) {
-        switch (event.getInvocationMethod()) {
-            case SelectionEvent.INVOCATION_MANUAL:
-                return MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL;
-            case SelectionEvent.INVOCATION_LINK:
-                return MetricsEvent.TEXT_SELECTION_INVOCATION_LINK;
-            default:
-                return MetricsEvent.TEXT_SELECTION_INVOCATION_UNKNOWN;
-        }
-    }
-
-    private static String getLogTypeString(int logType) {
-        switch (logType) {
-            case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
-                return "OVERTYPE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
-                return "COPY";
-            case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
-                return "PASTE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
-                return "CUT";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
-                return "SHARE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
-                return "SMART_SHARE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
-                return "DRAG";
-            case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
-                return "ABANDON";
-            case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
-                return "OTHER";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
-                return "SELECT_ALL";
-            case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
-                return "RESET";
-            case MetricsEvent.ACTION_TEXT_SELECTION_START:
-                return "SELECTION_STARTED";
-            case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
-                return "SELECTION_MODIFIED";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
-                return "SMART_SELECTION_SINGLE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
-                return "SMART_SELECTION_MULTI";
-            case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
-                return "AUTO_SELECTION";
-            default:
-                return UNKNOWN;
-        }
-    }
-
-    private static String getLogSubTypeString(int logSubType) {
-        switch (logSubType) {
-            case MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL:
-                return "MANUAL";
-            case MetricsEvent.TEXT_SELECTION_INVOCATION_LINK:
-                return "LINK";
-            default:
-                return UNKNOWN;
-        }
-    }
+    // Keep this in sync with the ResultIdUtils in libtextclassifier.
+    private static final String CLASSIFIER_ID = "androidtc";
 
     static boolean isPlatformLocalTextClassifierSmartSelection(String signature) {
         return SelectionSessionLogger.CLASSIFIER_ID.equals(
                 SelectionSessionLogger.SignatureParser.getClassifierId(signature));
     }
 
-    private static void debugLog(LogMaker log) {
-        if (!Log.ENABLE_FULL_LOGGING) {
-            return;
-        }
-        final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
-        final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
-        final String widget = widgetVersion.isEmpty()
-                ? widgetType : widgetType + "-" + widgetVersion;
-        final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
-        if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
-            String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
-            sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
-            Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
-        }
-
-        final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
-        final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
-        final String type = getLogTypeString(log.getType());
-        final String subType = getLogSubTypeString(log.getSubtype());
-        final int smartStart = Integer.parseInt(
-                Objects.toString(log.getTaggedData(SMART_START), ZERO));
-        final int smartEnd = Integer.parseInt(
-                Objects.toString(log.getTaggedData(SMART_END), ZERO));
-        final int eventStart = Integer.parseInt(
-                Objects.toString(log.getTaggedData(EVENT_START), ZERO));
-        final int eventEnd = Integer.parseInt(
-                Objects.toString(log.getTaggedData(EVENT_END), ZERO));
-
-        Log.v(LOG_TAG,
-                String.format(Locale.US, "%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
-                        index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd,
-                        widget, model));
-    }
-
-    /**
-     * Returns a token iterator for tokenizing text for logging purposes.
-     */
-    public static BreakIterator getTokenIterator(@NonNull Locale locale) {
-        return BreakIterator.getWordInstance(Objects.requireNonNull(locale));
-    }
-
-    /**
-     * Creates a string id that may be used to identify a TextClassifier result.
-     */
-    public static String createId(
-            String text, int start, int end, Context context, int modelVersion,
-            List<Locale> locales) {
-        Objects.requireNonNull(text);
-        Objects.requireNonNull(context);
-        Objects.requireNonNull(locales);
-        final StringJoiner localesJoiner = new StringJoiner(",");
-        for (Locale locale : locales) {
-            localesJoiner.add(locale.toLanguageTag());
-        }
-        final String modelName = String.format(Locale.US, "%s_v%d", localesJoiner.toString(),
-                modelVersion);
-        final int hash = Objects.hash(text, start, end, context.getPackageName());
-        return SignatureParser.createSignature(CLASSIFIER_ID, modelName, hash);
-    }
-
     /**
      * Helper for creating and parsing string ids for
      * {@link android.view.textclassifier.TextClassifierImpl}.
@@ -268,10 +41,6 @@
     @VisibleForTesting
     public static final class SignatureParser {
 
-        static String createSignature(String classifierId, String modelName, int hash) {
-            return String.format(Locale.US, "%s|%s|%d", classifierId, modelName, hash);
-        }
-
         static String getClassifierId(@Nullable String signature) {
             if (signature == null) {
                 return "";
@@ -282,29 +51,5 @@
             }
             return "";
         }
-
-        static String getModelName(@Nullable String signature) {
-            if (signature == null) {
-                return "";
-            }
-            final int start = signature.indexOf("|") + 1;
-            final int end = signature.indexOf("|", start);
-            if (start >= 1 && end >= start) {
-                return signature.substring(start, end);
-            }
-            return "";
-        }
-
-        static int getHash(@Nullable String signature) {
-            if (signature == null) {
-                return 0;
-            }
-            final int index1 = signature.indexOf("|");
-            final int index2 = signature.indexOf("|", index1);
-            if (index2 > 0) {
-                return Integer.parseInt(signature.substring(index2));
-            }
-            return 0;
-        }
     }
 }
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 86ef4e1..8eac1c1 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -45,7 +45,7 @@
 @VisibleForTesting(visibility = Visibility.PACKAGE)
 public final class SystemTextClassifier implements TextClassifier {
 
-    private static final String LOG_TAG = "SystemTextClassifier";
+    private static final String LOG_TAG = TextClassifier.LOG_TAG;
 
     private final ITextClassifierService mManagerService;
     private final TextClassificationConstants mSettings;
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index 8b9d129..3aed32a 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -44,8 +44,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
-import com.google.android.textclassifier.AnnotatorModel;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.time.ZonedDateTime;
@@ -327,9 +325,6 @@
 
         @NonNull private List<RemoteAction> mActions = new ArrayList<>();
         @NonNull private final Map<String, Float> mTypeScoreMap = new ArrayMap<>();
-        @NonNull
-        private final Map<String, AnnotatorModel.ClassificationResult> mClassificationResults =
-                new ArrayMap<>();
         @Nullable private String mText;
         @Nullable private Drawable mLegacyIcon;
         @Nullable private String mLegacyLabel;
@@ -362,36 +357,7 @@
         public Builder setEntityType(
                 @NonNull @EntityType String type,
                 @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
-            setEntityType(type, confidenceScore, null);
-            return this;
-        }
-
-        /**
-         * @see #setEntityType(String, float)
-         *
-         * @hide
-         */
-        @NonNull
-        public Builder setEntityType(AnnotatorModel.ClassificationResult classificationResult) {
-            setEntityType(
-                    classificationResult.getCollection(),
-                    classificationResult.getScore(),
-                    classificationResult);
-            return this;
-        }
-
-        /**
-         * @see #setEntityType(String, float)
-         *
-         * @hide
-         */
-        @NonNull
-        private Builder setEntityType(
-                @NonNull @EntityType String type,
-                @FloatRange(from = 0.0, to = 1.0) float confidenceScore,
-                @Nullable AnnotatorModel.ClassificationResult classificationResult) {
             mTypeScoreMap.put(type, confidenceScore);
-            mClassificationResults.put(type, classificationResult);
             return this;
         }
 
@@ -517,25 +483,7 @@
             EntityConfidence entityConfidence = new EntityConfidence(mTypeScoreMap);
             return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
                     mLegacyOnClickListener, mActions, entityConfidence, mId,
-                    buildExtras(entityConfidence));
-        }
-
-        private Bundle buildExtras(EntityConfidence entityConfidence) {
-            final Bundle extras = mExtras == null ? new Bundle() : mExtras;
-            if (mActionIntents.stream().anyMatch(Objects::nonNull)) {
-                ExtrasUtils.putActionsIntents(extras, mActionIntents);
-            }
-            if (mForeignLanguageExtra != null) {
-                ExtrasUtils.putForeignLanguageExtra(extras, mForeignLanguageExtra);
-            }
-            List<String> sortedTypes = entityConfidence.getEntities();
-            ArrayList<AnnotatorModel.ClassificationResult> sortedEntities = new ArrayList<>();
-            for (String type : sortedTypes) {
-                sortedEntities.add(mClassificationResults.get(type));
-            }
-            ExtrasUtils.putEntities(
-                    extras, sortedEntities.toArray(new AnnotatorModel.ClassificationResult[0]));
-            return extras.isEmpty() ? Bundle.EMPTY : extras;
+                    mExtras == null ? Bundle.EMPTY : mExtras);
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 3d5ac58..adb6fea 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -17,16 +17,11 @@
 package android.view.textclassifier;
 
 import android.annotation.Nullable;
-import android.content.Context;
 import android.provider.DeviceConfig;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
 /**
  * TextClassifier specific settings.
  *
@@ -41,8 +36,6 @@
  */
 // TODO: Rename to TextClassifierSettings.
 public final class TextClassificationConstants {
-    private static final String DELIMITER = ":";
-
     /**
      * Whether the smart linkify feature is enabled.
      */
@@ -60,7 +53,6 @@
      * Enable smart selection without a visible UI changes.
      */
     private static final String MODEL_DARK_LAUNCH_ENABLED = "model_dark_launch_enabled";
-
     /**
      * Whether the smart selection feature is enabled.
      */
@@ -75,88 +67,10 @@
     private static final String SMART_SELECT_ANIMATION_ENABLED =
             "smart_select_animation_enabled";
     /**
-     * Max length of text that suggestSelection can accept.
-     */
-    @VisibleForTesting
-    static final String SUGGEST_SELECTION_MAX_RANGE_LENGTH =
-            "suggest_selection_max_range_length";
-    /**
-     * Max length of text that classifyText can accept.
-     */
-    private static final String CLASSIFY_TEXT_MAX_RANGE_LENGTH = "classify_text_max_range_length";
-    /**
      * Max length of text that generateLinks can accept.
      */
-    private static final String GENERATE_LINKS_MAX_TEXT_LENGTH = "generate_links_max_text_length";
-    /**
-     * Sampling rate for generateLinks logging.
-     */
-    private static final String GENERATE_LINKS_LOG_SAMPLE_RATE =
-            "generate_links_log_sample_rate";
-    /**
-     * A colon(:) separated string that specifies the default entities types for
-     * generateLinks when hint is not given.
-     */
     @VisibleForTesting
-    static final String ENTITY_LIST_DEFAULT = "entity_list_default";
-    /**
-     * A colon(:) separated string that specifies the default entities types for
-     * generateLinks when the text is in a not editable UI widget.
-     */
-    private static final String ENTITY_LIST_NOT_EDITABLE = "entity_list_not_editable";
-    /**
-     * A colon(:) separated string that specifies the default entities types for
-     * generateLinks when the text is in an editable UI widget.
-     */
-    private static final String ENTITY_LIST_EDITABLE = "entity_list_editable";
-    /**
-     * A colon(:) separated string that specifies the default action types for
-     * suggestConversationActions when the suggestions are used in an app.
-     */
-    private static final String IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT =
-            "in_app_conversation_action_types_default";
-    /**
-     * A colon(:) separated string that specifies the default action types for
-     * suggestConversationActions when the suggestions are used in a notification.
-     */
-    private static final String NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT =
-            "notification_conversation_action_types_default";
-    /**
-     * Threshold to accept a suggested language from LangID model.
-     */
-    @VisibleForTesting
-    static final String LANG_ID_THRESHOLD_OVERRIDE = "lang_id_threshold_override";
-    /**
-     * Whether to enable {@link android.view.textclassifier.TemplateIntentFactory}.
-     */
-    private static final String TEMPLATE_INTENT_FACTORY_ENABLED = "template_intent_factory_enabled";
-    /**
-     * Whether to enable "translate" action in classifyText.
-     */
-    private static final String TRANSLATE_IN_CLASSIFICATION_ENABLED =
-            "translate_in_classification_enabled";
-    /**
-     * Whether to detect the languages of the text in request by using langId for the native
-     * model.
-     */
-    private static final String DETECT_LANGUAGES_FROM_TEXT_ENABLED =
-            "detect_languages_from_text_enabled";
-    /**
-     * A colon(:) separated string that specifies the configuration to use when including
-     * surrounding context text in language detection queries.
-     * <p>
-     * Format= minimumTextSize<int>:penalizeRatio<float>:textScoreRatio<float>
-     * <p>
-     * e.g. 20:1.0:0.4
-     * <p>
-     * Accept all text lengths with minimumTextSize=0
-     * <p>
-     * Reject all text less than minimumTextSize with penalizeRatio=0
-     * @see {@code TextClassifierImpl#detectLanguages(String, int, int)} for reference.
-     */
-    @VisibleForTesting
-    static final String LANG_ID_CONTEXT_SETTINGS = "lang_id_context_settings";
-
+    static final String GENERATE_LINKS_MAX_TEXT_LENGTH = "generate_links_max_text_length";
     /**
      * The TextClassifierService which would like to use. Example of setting the package:
      * <pre>
@@ -168,16 +82,6 @@
     static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE =
             "textclassifier_service_package_override";
 
-    /**
-     * Whether to use the default system text classifier as the default text classifier
-     * implementation. The local text classifier is used if it is {@code false}.
-     *
-     * @see android.service.textclassifier.TextClassifierService#getDefaultTextClassifierImplementation(Context)
-     */
-    // TODO: Once the system health experiment is done, remove this together with local TC.
-    private static final String USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL =
-            "use_default_system_text_classifier_as_default_impl";
-
     private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null;
     private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
     private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
@@ -186,42 +90,7 @@
     private static final boolean SMART_TEXT_SHARE_ENABLED_DEFAULT = true;
     private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
     private static final boolean SMART_SELECT_ANIMATION_ENABLED_DEFAULT = true;
-    private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
-    private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
     private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
-    private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100;
-    private static final List<String> ENTITY_LIST_DEFAULT_VALUE = Arrays.asList(
-            TextClassifier.TYPE_ADDRESS,
-            TextClassifier.TYPE_EMAIL,
-            TextClassifier.TYPE_PHONE,
-            TextClassifier.TYPE_URL,
-            TextClassifier.TYPE_DATE,
-            TextClassifier.TYPE_DATE_TIME,
-            TextClassifier.TYPE_FLIGHT_NUMBER);
-    private static final List<String> CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES = Arrays.asList(
-            ConversationAction.TYPE_TEXT_REPLY,
-            ConversationAction.TYPE_CREATE_REMINDER,
-            ConversationAction.TYPE_CALL_PHONE,
-            ConversationAction.TYPE_OPEN_URL,
-            ConversationAction.TYPE_SEND_EMAIL,
-            ConversationAction.TYPE_SEND_SMS,
-            ConversationAction.TYPE_TRACK_FLIGHT,
-            ConversationAction.TYPE_VIEW_CALENDAR,
-            ConversationAction.TYPE_VIEW_MAP,
-            ConversationAction.TYPE_ADD_CONTACT,
-            ConversationAction.TYPE_COPY);
-    /**
-     * < 0  : Not set. Use value from LangId model.
-     * 0 - 1: Override value in LangId model.
-     *
-     * @see EntityConfidence
-     */
-    private static final float LANG_ID_THRESHOLD_OVERRIDE_DEFAULT = -1f;
-    private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true;
-    private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true;
-    private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true;
-    private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[]{20f, 1.0f, 0.4f};
-    private static final boolean USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT = true;
 
     @Nullable
     public String getTextClassifierServicePackageOverride() {
@@ -266,119 +135,20 @@
                 SMART_SELECT_ANIMATION_ENABLED, SMART_SELECT_ANIMATION_ENABLED_DEFAULT);
     }
 
-    public int getSuggestSelectionMaxRangeLength() {
-        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                SUGGEST_SELECTION_MAX_RANGE_LENGTH, SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT);
-    }
-
-    public int getClassifyTextMaxRangeLength() {
-        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                CLASSIFY_TEXT_MAX_RANGE_LENGTH, CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT);
-    }
-
     public int getGenerateLinksMaxTextLength() {
         return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
                 GENERATE_LINKS_MAX_TEXT_LENGTH, GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
     }
 
-    public int getGenerateLinksLogSampleRate() {
-        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                GENERATE_LINKS_LOG_SAMPLE_RATE, GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
-    }
-
-    public List<String> getEntityListDefault() {
-        return getDeviceConfigStringList(ENTITY_LIST_DEFAULT, ENTITY_LIST_DEFAULT_VALUE);
-    }
-
-    public List<String> getEntityListNotEditable() {
-        return getDeviceConfigStringList(ENTITY_LIST_NOT_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
-    }
-
-    public List<String> getEntityListEditable() {
-        return getDeviceConfigStringList(ENTITY_LIST_EDITABLE, ENTITY_LIST_DEFAULT_VALUE);
-    }
-
-    public List<String> getInAppConversationActionTypes() {
-        return getDeviceConfigStringList(
-                IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
-                CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
-    }
-
-    public List<String> getNotificationConversationActionTypes() {
-        return getDeviceConfigStringList(
-                NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
-                CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES);
-    }
-
-    public float getLangIdThresholdOverride() {
-        return DeviceConfig.getFloat(
-                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                LANG_ID_THRESHOLD_OVERRIDE,
-                LANG_ID_THRESHOLD_OVERRIDE_DEFAULT);
-    }
-
-    public boolean isTemplateIntentFactoryEnabled() {
-        return DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                TEMPLATE_INTENT_FACTORY_ENABLED,
-                TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT);
-    }
-
-    public boolean isTranslateInClassificationEnabled() {
-        return DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                TRANSLATE_IN_CLASSIFICATION_ENABLED,
-                TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT);
-    }
-
-    public boolean isDetectLanguagesFromTextEnabled() {
-        return DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                DETECT_LANGUAGES_FROM_TEXT_ENABLED,
-                DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT);
-    }
-
-    public float[] getLangIdContextSettings() {
-        return getDeviceConfigFloatArray(
-                LANG_ID_CONTEXT_SETTINGS, LANG_ID_CONTEXT_SETTINGS_DEFAULT);
-    }
-
-    public boolean getUseDefaultTextClassifierAsDefaultImplementation() {
-        return DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL,
-                USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT);
-    }
-
     void dump(IndentingPrintWriter pw) {
         pw.println("TextClassificationConstants:");
         pw.increaseIndent();
-        pw.printPair("classify_text_max_range_length", getClassifyTextMaxRangeLength())
-                .println();
-        pw.printPair("detect_languages_from_text_enabled", isDetectLanguagesFromTextEnabled())
-                .println();
-        pw.printPair("entity_list_default", getEntityListDefault())
-                .println();
-        pw.printPair("entity_list_editable", getEntityListEditable())
-                .println();
-        pw.printPair("entity_list_not_editable", getEntityListNotEditable())
-                .println();
-        pw.printPair("generate_links_log_sample_rate", getGenerateLinksLogSampleRate())
-                .println();
         pw.printPair("generate_links_max_text_length", getGenerateLinksMaxTextLength())
                 .println();
-        pw.printPair("in_app_conversation_action_types_default", getInAppConversationActionTypes())
-                .println();
-        pw.printPair("lang_id_context_settings", Arrays.toString(getLangIdContextSettings()))
-                .println();
-        pw.printPair("lang_id_threshold_override", getLangIdThresholdOverride())
-                .println();
         pw.printPair("local_textclassifier_enabled", isLocalTextClassifierEnabled())
                 .println();
         pw.printPair("model_dark_launch_enabled", isModelDarkLaunchEnabled())
                 .println();
-        pw.printPair("notification_conversation_action_types_default",
-                getNotificationConversationActionTypes()).println();
         pw.printPair("smart_linkify_enabled", isSmartLinkifyEnabled())
                 .println();
         pw.printPair("smart_select_animation_enabled", isSmartSelectionAnimationEnabled())
@@ -387,57 +157,10 @@
                 .println();
         pw.printPair("smart_text_share_enabled", isSmartTextShareEnabled())
                 .println();
-        pw.printPair("suggest_selection_max_range_length", getSuggestSelectionMaxRangeLength())
-                .println();
         pw.printPair("system_textclassifier_enabled", isSystemTextClassifierEnabled())
                 .println();
-        pw.printPair("template_intent_factory_enabled", isTemplateIntentFactoryEnabled())
-                .println();
-        pw.printPair("translate_in_classification_enabled", isTranslateInClassificationEnabled())
-                .println();
         pw.printPair("textclassifier_service_package_override",
                 getTextClassifierServicePackageOverride()).println();
-        pw.printPair("use_default_system_text_classifier_as_default_impl",
-                getUseDefaultTextClassifierAsDefaultImplementation()).println();
         pw.decreaseIndent();
     }
-
-    private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) {
-        return parse(
-                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
-                defaultValue);
-    }
-
-    private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
-        return parse(
-                DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
-                defaultValue);
-    }
-
-    private static List<String> parse(@Nullable String listStr, List<String> defaultValue) {
-        if (listStr != null) {
-            return Collections.unmodifiableList(Arrays.asList(listStr.split(DELIMITER)));
-        }
-        return defaultValue;
-    }
-
-    private static float[] parse(@Nullable String arrayStr, float[] defaultValue) {
-        if (arrayStr != null) {
-            final String[] split = arrayStr.split(DELIMITER);
-            if (split.length != defaultValue.length) {
-                return defaultValue;
-            }
-            final float[] result = new float[split.length];
-            for (int i = 0; i < split.length; i++) {
-                try {
-                    result[i] = Float.parseFloat(split[i]);
-                } catch (NumberFormatException e) {
-                    return defaultValue;
-                }
-            }
-            return result;
-        } else {
-            return defaultValue;
-        }
-    }
 }
\ No newline at end of file
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index dfbec9b..fa4f7d6 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -19,21 +19,15 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
-import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.ServiceManager;
-import android.provider.DeviceConfig;
-import android.provider.DeviceConfig.Properties;
 import android.view.textclassifier.TextClassifier.TextClassifierType;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
-import java.lang.ref.WeakReference;
 import java.util.Objects;
-import java.util.Set;
 
 /**
  * Interface to the text classification service.
@@ -41,7 +35,7 @@
 @SystemService(Context.TEXT_CLASSIFICATION_SERVICE)
 public final class TextClassificationManager {
 
-    private static final String LOG_TAG = "TextClassificationManager";
+    private static final String LOG_TAG = TextClassifier.LOG_TAG;
 
     private static final TextClassificationConstants sDefaultSettings =
             new TextClassificationConstants();
@@ -52,15 +46,11 @@
                     classificationContext, getTextClassifier());
 
     private final Context mContext;
-    private final SettingsObserver mSettingsObserver;
 
     @GuardedBy("mLock")
     @Nullable
     private TextClassifier mCustomTextClassifier;
     @GuardedBy("mLock")
-    @Nullable
-    private TextClassifier mLocalTextClassifier;
-    @GuardedBy("mLock")
     private TextClassificationSessionFactory mSessionFactory;
     @GuardedBy("mLock")
     private TextClassificationConstants mSettings;
@@ -69,7 +59,6 @@
     public TextClassificationManager(Context context) {
         mContext = Objects.requireNonNull(context);
         mSessionFactory = mDefaultSessionFactory;
-        mSettingsObserver = new SettingsObserver(this);
     }
 
     /**
@@ -112,7 +101,7 @@
      *
      * @see TextClassifier#LOCAL
      * @see TextClassifier#SYSTEM
-     * @see TextClassifier#DEFAULT_SERVICE
+     * @see TextClassifier#DEFAULT_SYSTEM
      * @hide
      */
     @UnsupportedAppUsage
@@ -189,28 +178,17 @@
         }
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            // Note that fields could be null if the constructor threw.
-            if (mSettingsObserver != null) {
-                DeviceConfig.removeOnPropertiesChangedListener(mSettingsObserver);
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
     /** @hide */
     private TextClassifier getSystemTextClassifier(@TextClassifierType int type) {
         synchronized (mLock) {
             if (getSettings().isSystemTextClassifierEnabled()) {
                 try {
-                    Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = " + type);
+                    Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = "
+                            + TextClassifier.typeToString(type));
                     return new SystemTextClassifier(
                             mContext,
                             getSettings(),
-                            /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE);
+                            /* useDefault= */ type == TextClassifier.DEFAULT_SYSTEM);
                 } catch (ServiceManager.ServiceNotFoundException e) {
                     Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
                 }
@@ -224,49 +202,13 @@
      */
     @NonNull
     private TextClassifier getLocalTextClassifier() {
-        synchronized (mLock) {
-            if (mLocalTextClassifier == null) {
-                if (getSettings().isLocalTextClassifierEnabled()) {
-                    mLocalTextClassifier =
-                            new TextClassifierImpl(mContext, getSettings(), TextClassifier.NO_OP);
-                } else {
-                    Log.d(LOG_TAG, "Local TextClassifier disabled");
-                    mLocalTextClassifier = TextClassifier.NO_OP;
-                }
-            }
-            return mLocalTextClassifier;
-        }
-    }
-
-    /** @hide */
-    @VisibleForTesting
-    public void invalidateForTesting() {
-        invalidate();
-    }
-
-    private void invalidate() {
-        synchronized (mLock) {
-            mSettings = null;
-            invalidateTextClassifiers();
-        }
-    }
-
-    private void invalidateTextClassifiers() {
-        synchronized (mLock) {
-            mLocalTextClassifier = null;
-        }
-    }
-
-    Context getApplicationContext() {
-        return mContext.getApplicationContext() != null
-                ? mContext.getApplicationContext()
-                : mContext;
+        Log.d(LOG_TAG, "Local text-classifier not supported. Returning a no-op text-classifier.");
+        return TextClassifier.NO_OP;
     }
 
     /** @hide **/
     public void dump(IndentingPrintWriter pw) {
-        getLocalTextClassifier().dump(pw);
-        getSystemTextClassifier(TextClassifier.DEFAULT_SERVICE).dump(pw);
+        getSystemTextClassifier(TextClassifier.DEFAULT_SYSTEM).dump(pw);
         getSystemTextClassifier(TextClassifier.SYSTEM).dump(pw);
         getSettings().dump(pw);
     }
@@ -283,31 +225,4 @@
             return sDefaultSettings;
         }
     }
-
-    private static final class SettingsObserver implements
-            DeviceConfig.OnPropertiesChangedListener {
-
-        private final WeakReference<TextClassificationManager> mTcm;
-
-        SettingsObserver(TextClassificationManager tcm) {
-            mTcm = new WeakReference<>(tcm);
-            DeviceConfig.addOnPropertiesChangedListener(
-                    DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                    ActivityThread.currentApplication().getMainExecutor(),
-                    this);
-        }
-
-        @Override
-        public void onPropertiesChanged(Properties properties) {
-            final TextClassificationManager tcm = mTcm.get();
-            if (tcm != null) {
-                final Set<String> keys = properties.getKeyset();
-                if (keys.contains(TextClassificationConstants.SYSTEM_TEXT_CLASSIFIER_ENABLED)
-                        || keys.contains(
-                        TextClassificationConstants.LOCAL_TEXT_CLASSIFIER_ENABLED)) {
-                    tcm.invalidateTextClassifiers();
-                }
-            }
-        }
-    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 2cc226d..6d5077a 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -61,19 +61,32 @@
 public interface TextClassifier {
 
     /** @hide */
-    String DEFAULT_LOG_TAG = "androidtc";
+    String LOG_TAG = "androidtc";
 
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SERVICE})
+    @IntDef(value = {LOCAL, SYSTEM, DEFAULT_SYSTEM})
     @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;
+    int DEFAULT_SYSTEM = 2;
+
+    /** @hide */
+    static String typeToString(@TextClassifierType int type) {
+        switch (type) {
+            case LOCAL:
+                return "Local";
+            case SYSTEM:
+                return "System";
+            case DEFAULT_SYSTEM:
+                return "Default system";
+        }
+        return "Unknown";
+    }
 
     /** The TextClassifier failed to run. */
     String TYPE_UNKNOWN = "";
@@ -776,7 +789,7 @@
 
         static void checkMainThread() {
             if (Looper.myLooper() == Looper.getMainLooper()) {
-                Log.w(DEFAULT_LOG_TAG, "TextClassifier called on main thread");
+                Log.w(LOG_TAG, "TextClassifier called on main thread");
             }
         }
     }
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
deleted file mode 100644
index 8162699..0000000
--- a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SESSION_ID;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_VERSION;
-
-import android.metrics.LogMaker;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import java.util.Objects;
-
-
-/**
- * Log {@link TextClassifierEvent} by using Tron, only support language detection and
- * conversation actions.
- *
- * @hide
- */
-public final class TextClassifierEventTronLogger {
-
-    private static final String TAG = "TCEventTronLogger";
-
-    private final MetricsLogger mMetricsLogger;
-
-    public TextClassifierEventTronLogger() {
-        this(new MetricsLogger());
-    }
-
-    @VisibleForTesting
-    public TextClassifierEventTronLogger(MetricsLogger metricsLogger) {
-        mMetricsLogger = Objects.requireNonNull(metricsLogger);
-    }
-
-    /** Emits a text classifier event to the logs. */
-    public void writeEvent(TextClassifierEvent event) {
-        Objects.requireNonNull(event);
-
-        int category = getCategory(event);
-        if (category == -1) {
-            Log.w(TAG, "Unknown category: " + event.getEventCategory());
-            return;
-        }
-        final LogMaker log = new LogMaker(category)
-                .setSubtype(getLogType(event))
-                .addTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID, event.getResultId())
-                .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL, getModelName(event));
-        if (event.getScores().length >= 1) {
-            log.addTaggedData(FIELD_TEXT_CLASSIFIER_SCORE, event.getScores()[0]);
-        }
-        String[] entityTypes = event.getEntityTypes();
-        // The old logger does not support a field of list type, and thus workaround by store them
-        // in three separate fields. This is not an issue with the new logger.
-        if (entityTypes.length >= 1) {
-            log.addTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE, entityTypes[0]);
-        }
-        if (entityTypes.length >= 2) {
-            log.addTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE, entityTypes[1]);
-        }
-        if (entityTypes.length >= 3) {
-            log.addTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE, entityTypes[2]);
-        }
-        TextClassificationContext eventContext = event.getEventContext();
-        if (eventContext != null) {
-            log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE, eventContext.getWidgetType());
-            log.addTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION,
-                    eventContext.getWidgetVersion());
-            log.setPackageName(eventContext.getPackageName());
-        }
-        mMetricsLogger.write(log);
-        debugLog(log);
-    }
-
-    private static String getModelName(TextClassifierEvent event) {
-        if (event.getModelName() != null) {
-            return event.getModelName();
-        }
-        return SelectionSessionLogger.SignatureParser.getModelName(event.getResultId());
-    }
-
-    private static int getCategory(TextClassifierEvent event) {
-        switch (event.getEventCategory()) {
-            case TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS:
-                return MetricsEvent.CONVERSATION_ACTIONS;
-            case TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION:
-                return MetricsEvent.LANGUAGE_DETECTION;
-        }
-        return -1;
-    }
-
-    private static int getLogType(TextClassifierEvent event) {
-        switch (event.getEventType()) {
-            case TextClassifierEvent.TYPE_SMART_ACTION:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
-            case TextClassifierEvent.TYPE_ACTIONS_SHOWN:
-                return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN;
-            case TextClassifierEvent.TYPE_MANUAL_REPLY:
-                return MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY;
-            case TextClassifierEvent.TYPE_ACTIONS_GENERATED:
-                return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED;
-            default:
-                return MetricsEvent.VIEW_UNKNOWN;
-        }
-    }
-
-    private String toCategoryName(int category) {
-        switch (category) {
-            case MetricsEvent.CONVERSATION_ACTIONS:
-                return "conversation_actions";
-            case MetricsEvent.LANGUAGE_DETECTION:
-                return "language_detection";
-        }
-        return "unknown";
-    }
-
-    private String toEventName(int logType) {
-        switch (logType) {
-            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
-                return "smart_share";
-            case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN:
-                return "actions_shown";
-            case MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY:
-                return "manual_reply";
-            case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED:
-                return "actions_generated";
-        }
-        return "unknown";
-    }
-
-    private void debugLog(LogMaker log) {
-        if (!Log.ENABLE_FULL_LOGGING) {
-            return;
-        }
-        final String id = String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SESSION_ID));
-        final String categoryName = toCategoryName(log.getCategory());
-        final String eventName = toEventName(log.getSubtype());
-        final String widgetType =
-                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE));
-        final String widgetVersion =
-                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_VERSION));
-        final String model = String.valueOf(log.getTaggedData(FIELD_TEXTCLASSIFIER_MODEL));
-        final String firstEntityType =
-                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE));
-        final String secondEntityType =
-                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SECOND_ENTITY_TYPE));
-        final String thirdEntityType =
-                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_THIRD_ENTITY_TYPE));
-        final String score =
-                String.valueOf(log.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE));
-
-        StringBuilder builder = new StringBuilder();
-        builder.append("writeEvent: ");
-        builder.append("id=").append(id);
-        builder.append(", category=").append(categoryName);
-        builder.append(", eventName=").append(eventName);
-        builder.append(", widgetType=").append(widgetType);
-        builder.append(", widgetVersion=").append(widgetVersion);
-        builder.append(", model=").append(model);
-        builder.append(", firstEntityType=").append(firstEntityType);
-        builder.append(", secondEntityType=").append(secondEntityType);
-        builder.append(", thirdEntityType=").append(thirdEntityType);
-        builder.append(", score=").append(score);
-
-        Log.v(TAG, builder.toString());
-    }
-}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
deleted file mode 100644
index d7149ee..0000000
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ /dev/null
@@ -1,911 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.app.RemoteAction;
-import android.content.Context;
-import android.content.Intent;
-import android.icu.util.ULocale;
-import android.os.Bundle;
-import android.os.LocaleList;
-import android.os.ParcelFileDescriptor;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Pair;
-import android.view.textclassifier.ActionsModelParamsSupplier.ActionsModelParams;
-import android.view.textclassifier.intent.ClassificationIntentFactory;
-import android.view.textclassifier.intent.LabeledIntent;
-import android.view.textclassifier.intent.LegacyClassificationIntentFactory;
-import android.view.textclassifier.intent.TemplateClassificationIntentFactory;
-import android.view.textclassifier.intent.TemplateIntentFactory;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
-
-import com.google.android.textclassifier.ActionsSuggestionsModel;
-import com.google.android.textclassifier.AnnotatorModel;
-import com.google.android.textclassifier.LangIdModel;
-import com.google.android.textclassifier.LangIdModel.LanguageResult;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.time.Instant;
-import java.time.ZonedDateTime;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Supplier;
-
-/**
- * Default implementation of the {@link TextClassifier} interface.
- *
- * <p>This class uses machine learning to recognize entities in text.
- * Unless otherwise stated, methods of this class are blocking operations and should most
- * likely not be called on the UI thread.
- *
- * @hide
- */
-public final class TextClassifierImpl implements TextClassifier {
-
-    private static final String LOG_TAG = DEFAULT_LOG_TAG;
-
-    private static final boolean DEBUG = false;
-
-    private static final File FACTORY_MODEL_DIR = new File("/etc/textclassifier/");
-    // Annotator
-    private static final String ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX =
-            "textclassifier\\.(.*)\\.model";
-    private static final File ANNOTATOR_UPDATED_MODEL_FILE =
-            new File("/data/misc/textclassifier/textclassifier.model");
-
-    // LangID
-    private static final String LANG_ID_FACTORY_MODEL_FILENAME_REGEX = "lang_id.model";
-    private static final File UPDATED_LANG_ID_MODEL_FILE =
-            new File("/data/misc/textclassifier/lang_id.model");
-
-    // Actions
-    private static final String ACTIONS_FACTORY_MODEL_FILENAME_REGEX =
-            "actions_suggestions\\.(.*)\\.model";
-    private static final File UPDATED_ACTIONS_MODEL =
-            new File("/data/misc/textclassifier/actions_suggestions.model");
-
-    private final Context mContext;
-    private final TextClassifier mFallback;
-    private final GenerateLinksLogger mGenerateLinksLogger;
-
-    private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
-    private ModelFileManager.ModelFile mAnnotatorModelInUse;
-    @GuardedBy("mLock")
-    private AnnotatorModel mAnnotatorImpl;
-
-    @GuardedBy("mLock")
-    private ModelFileManager.ModelFile mLangIdModelInUse;
-    @GuardedBy("mLock")
-    private LangIdModel mLangIdImpl;
-
-    @GuardedBy("mLock")
-    private ModelFileManager.ModelFile mActionModelInUse;
-    @GuardedBy("mLock")
-    private ActionsSuggestionsModel mActionsImpl;
-
-    private final SelectionSessionLogger mSessionLogger = new SelectionSessionLogger();
-    private final TextClassifierEventTronLogger mTextClassifierEventTronLogger =
-            new TextClassifierEventTronLogger();
-
-    private final TextClassificationConstants mSettings;
-
-    private final ModelFileManager mAnnotatorModelFileManager;
-    private final ModelFileManager mLangIdModelFileManager;
-    private final ModelFileManager mActionsModelFileManager;
-
-    private final ClassificationIntentFactory mClassificationIntentFactory;
-    private final TemplateIntentFactory mTemplateIntentFactory;
-    private final Supplier<ActionsModelParams> mActionsModelParamsSupplier;
-
-    public TextClassifierImpl(
-            Context context, TextClassificationConstants settings, TextClassifier fallback) {
-        mContext = Objects.requireNonNull(context);
-        mFallback = Objects.requireNonNull(fallback);
-        mSettings = Objects.requireNonNull(settings);
-        mGenerateLinksLogger = new GenerateLinksLogger(mSettings.getGenerateLinksLogSampleRate());
-        mAnnotatorModelFileManager = new ModelFileManager(
-                new ModelFileManager.ModelFileSupplierImpl(
-                        FACTORY_MODEL_DIR,
-                        ANNOTATOR_FACTORY_MODEL_FILENAME_REGEX,
-                        ANNOTATOR_UPDATED_MODEL_FILE,
-                        AnnotatorModel::getVersion,
-                        AnnotatorModel::getLocales));
-        mLangIdModelFileManager = new ModelFileManager(
-                new ModelFileManager.ModelFileSupplierImpl(
-                        FACTORY_MODEL_DIR,
-                        LANG_ID_FACTORY_MODEL_FILENAME_REGEX,
-                        UPDATED_LANG_ID_MODEL_FILE,
-                        LangIdModel::getVersion,
-                        fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT));
-        mActionsModelFileManager = new ModelFileManager(
-                new ModelFileManager.ModelFileSupplierImpl(
-                        FACTORY_MODEL_DIR,
-                        ACTIONS_FACTORY_MODEL_FILENAME_REGEX,
-                        UPDATED_ACTIONS_MODEL,
-                        ActionsSuggestionsModel::getVersion,
-                        ActionsSuggestionsModel::getLocales));
-
-        mTemplateIntentFactory = new TemplateIntentFactory();
-        mClassificationIntentFactory = mSettings.isTemplateIntentFactoryEnabled()
-                ? new TemplateClassificationIntentFactory(
-                mTemplateIntentFactory, new LegacyClassificationIntentFactory())
-                : new LegacyClassificationIntentFactory();
-        mActionsModelParamsSupplier = new ActionsModelParamsSupplier(mContext,
-                () -> {
-                    synchronized (mLock) {
-                        // Clear mActionsImpl here, so that we will create a new
-                        // ActionsSuggestionsModel object with the new flag in the next request.
-                        mActionsImpl = null;
-                        mActionModelInUse = null;
-                    }
-                });
-    }
-
-    public TextClassifierImpl(Context context, TextClassificationConstants settings) {
-        this(context, settings, TextClassifier.NO_OP);
-    }
-
-    /** @inheritDoc */
-    @Override
-    @WorkerThread
-    public TextSelection suggestSelection(TextSelection.Request request) {
-        Objects.requireNonNull(request);
-        Utils.checkMainThread();
-        try {
-            final int rangeLength = request.getEndIndex() - request.getStartIndex();
-            final String string = request.getText().toString();
-            if (string.length() > 0
-                    && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
-                final String localesString = concatenateLocales(request.getDefaultLocales());
-                final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
-                final ZonedDateTime refTime = ZonedDateTime.now();
-                final AnnotatorModel annotatorImpl = getAnnotatorImpl(request.getDefaultLocales());
-                final int start;
-                final int end;
-                if (mSettings.isModelDarkLaunchEnabled() && !request.isDarkLaunchAllowed()) {
-                    start = request.getStartIndex();
-                    end = request.getEndIndex();
-                } else {
-                    final int[] startEnd = annotatorImpl.suggestSelection(
-                            string, request.getStartIndex(), request.getEndIndex(),
-                            new AnnotatorModel.SelectionOptions(localesString, detectLanguageTags));
-                    start = startEnd[0];
-                    end = startEnd[1];
-                }
-                if (start < end
-                        && start >= 0 && end <= string.length()
-                        && start <= request.getStartIndex() && end >= request.getEndIndex()) {
-                    final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
-                    final AnnotatorModel.ClassificationResult[] results =
-                            annotatorImpl.classifyText(
-                                    string, start, end,
-                                    new AnnotatorModel.ClassificationOptions(
-                                            refTime.toInstant().toEpochMilli(),
-                                            refTime.getZone().getId(),
-                                            localesString,
-                                            detectLanguageTags),
-                                    // Passing null here to suppress intent generation
-                                    // TODO: Use an explicit flag to suppress it.
-                                    /* appContext */ null,
-                                    /* deviceLocales */null);
-                    final int size = results.length;
-                    for (int i = 0; i < size; i++) {
-                        tsBuilder.setEntityType(results[i].getCollection(), results[i].getScore());
-                    }
-                    return tsBuilder.setId(createId(
-                            string, request.getStartIndex(), request.getEndIndex()))
-                            .build();
-                } else {
-                    // We can not trust the result. Log the issue and ignore the result.
-                    Log.d(LOG_TAG, "Got bad indices for input text. Ignoring result.");
-                }
-            }
-        } catch (Throwable t) {
-            // Avoid throwing from this method. Log the error.
-            Log.e(LOG_TAG,
-                    "Error suggesting selection for text. No changes to selection suggested.",
-                    t);
-        }
-        // Getting here means something went wrong, return a NO_OP result.
-        return mFallback.suggestSelection(request);
-    }
-
-    /** @inheritDoc */
-    @Override
-    @WorkerThread
-    public TextClassification classifyText(TextClassification.Request request) {
-        Objects.requireNonNull(request);
-        Utils.checkMainThread();
-        try {
-            final int rangeLength = request.getEndIndex() - request.getStartIndex();
-            final String string = request.getText().toString();
-            if (string.length() > 0 && rangeLength <= mSettings.getClassifyTextMaxRangeLength()) {
-                final String localesString = concatenateLocales(request.getDefaultLocales());
-                final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
-                final ZonedDateTime refTime = request.getReferenceTime() != null
-                        ? request.getReferenceTime() : ZonedDateTime.now();
-                final AnnotatorModel.ClassificationResult[] results =
-                        getAnnotatorImpl(request.getDefaultLocales())
-                                .classifyText(
-                                        string, request.getStartIndex(), request.getEndIndex(),
-                                        new AnnotatorModel.ClassificationOptions(
-                                                refTime.toInstant().toEpochMilli(),
-                                                refTime.getZone().getId(),
-                                                localesString,
-                                                detectLanguageTags),
-                                        mContext,
-                                        getResourceLocalesString()
-                                );
-                if (results.length > 0) {
-                    return createClassificationResult(
-                            results, string,
-                            request.getStartIndex(), request.getEndIndex(), refTime.toInstant());
-                }
-            }
-        } catch (Throwable t) {
-            // Avoid throwing from this method. Log the error.
-            Log.e(LOG_TAG, "Error getting text classification info.", t);
-        }
-        // Getting here means something went wrong, return a NO_OP result.
-        return mFallback.classifyText(request);
-    }
-
-    /** @inheritDoc */
-    @Override
-    @WorkerThread
-    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);
-        }
-
-        final String textString = request.getText().toString();
-        final TextLinks.Builder builder = new TextLinks.Builder(textString);
-
-        try {
-            final long startTimeMs = System.currentTimeMillis();
-            final ZonedDateTime refTime = ZonedDateTime.now();
-            final Collection<String> entitiesToIdentify = request.getEntityConfig() != null
-                    ? request.getEntityConfig().resolveEntityListModifications(
-                    getEntitiesForHints(request.getEntityConfig().getHints()))
-                    : mSettings.getEntityListDefault();
-            final String localesString = concatenateLocales(request.getDefaultLocales());
-            final String detectLanguageTags = detectLanguageTagsFromText(request.getText());
-            final AnnotatorModel annotatorImpl =
-                    getAnnotatorImpl(request.getDefaultLocales());
-            final boolean isSerializedEntityDataEnabled =
-                    ExtrasUtils.isSerializedEntityDataEnabled(request);
-            final AnnotatorModel.AnnotatedSpan[] annotations =
-                    annotatorImpl.annotate(
-                            textString,
-                            new AnnotatorModel.AnnotationOptions(
-                                    refTime.toInstant().toEpochMilli(),
-                                    refTime.getZone().getId(),
-                                    localesString,
-                                    detectLanguageTags,
-                                    entitiesToIdentify,
-                                    AnnotatorModel.AnnotationUsecase.SMART.getValue(),
-                                    isSerializedEntityDataEnabled));
-            for (AnnotatorModel.AnnotatedSpan span : annotations) {
-                final AnnotatorModel.ClassificationResult[] results =
-                        span.getClassification();
-                if (results.length == 0
-                        || !entitiesToIdentify.contains(results[0].getCollection())) {
-                    continue;
-                }
-                final Map<String, Float> entityScores = new ArrayMap<>();
-                for (int i = 0; i < results.length; i++) {
-                    entityScores.put(results[i].getCollection(), results[i].getScore());
-                }
-                Bundle extras = new Bundle();
-                if (isSerializedEntityDataEnabled) {
-                    ExtrasUtils.putEntities(extras, results);
-                }
-                builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores, extras);
-            }
-            final TextLinks links = builder.build();
-            final long endTimeMs = System.currentTimeMillis();
-            final String callingPackageName = request.getCallingPackageName() == null
-                    ? mContext.getPackageName()  // local (in process) TC.
-                    : request.getCallingPackageName();
-            mGenerateLinksLogger.logGenerateLinks(
-                    request.getText(), links, callingPackageName, endTimeMs - startTimeMs);
-            return links;
-        } catch (Throwable t) {
-            // Avoid throwing from this method. Log the error.
-            Log.e(LOG_TAG, "Error getting links info.", t);
-        }
-        return mFallback.generateLinks(request);
-    }
-
-    /** @inheritDoc */
-    @Override
-    public int getMaxGenerateLinksTextLength() {
-        return mSettings.getGenerateLinksMaxTextLength();
-    }
-
-    private Collection<String> getEntitiesForHints(Collection<String> hints) {
-        final boolean editable = hints.contains(HINT_TEXT_IS_EDITABLE);
-        final boolean notEditable = hints.contains(HINT_TEXT_IS_NOT_EDITABLE);
-
-        // Use the default if there is no hint, or conflicting ones.
-        final boolean useDefault = editable == notEditable;
-        if (useDefault) {
-            return mSettings.getEntityListDefault();
-        } else if (editable) {
-            return mSettings.getEntityListEditable();
-        } else {  // notEditable
-            return mSettings.getEntityListNotEditable();
-        }
-    }
-
-    /** @inheritDoc */
-    @Override
-    public void onSelectionEvent(SelectionEvent event) {
-        mSessionLogger.writeEvent(event);
-    }
-
-    @Override
-    public void onTextClassifierEvent(TextClassifierEvent event) {
-        if (DEBUG) {
-            Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]");
-        }
-        try {
-            final SelectionEvent selEvent = event.toSelectionEvent();
-            if (selEvent != null) {
-                mSessionLogger.writeEvent(selEvent);
-            } else {
-                mTextClassifierEventTronLogger.writeEvent(event);
-            }
-        } catch (Exception e) {
-            Log.e(LOG_TAG, "Error writing event", e);
-        }
-    }
-
-    /** @inheritDoc */
-    @Override
-    public TextLanguage detectLanguage(@NonNull TextLanguage.Request request) {
-        Objects.requireNonNull(request);
-        Utils.checkMainThread();
-        try {
-            final TextLanguage.Builder builder = new TextLanguage.Builder();
-            final LangIdModel.LanguageResult[] langResults =
-                    getLangIdImpl().detectLanguages(request.getText().toString());
-            for (int i = 0; i < langResults.length; i++) {
-                builder.putLocale(
-                        ULocale.forLanguageTag(langResults[i].getLanguage()),
-                        langResults[i].getScore());
-            }
-            return builder.build();
-        } catch (Throwable t) {
-            // Avoid throwing from this method. Log the error.
-            Log.e(LOG_TAG, "Error detecting text language.", t);
-        }
-        return mFallback.detectLanguage(request);
-    }
-
-    @Override
-    public ConversationActions suggestConversationActions(ConversationActions.Request request) {
-        Objects.requireNonNull(request);
-        Utils.checkMainThread();
-        try {
-            ActionsSuggestionsModel actionsImpl = getActionsImpl();
-            if (actionsImpl == null) {
-                // Actions model is optional, fallback if it is not available.
-                return mFallback.suggestConversationActions(request);
-            }
-            ActionsSuggestionsModel.ConversationMessage[] nativeMessages =
-                    ActionsSuggestionsHelper.toNativeMessages(
-                            request.getConversation(), this::detectLanguageTagsFromText);
-            if (nativeMessages.length == 0) {
-                return mFallback.suggestConversationActions(request);
-            }
-            ActionsSuggestionsModel.Conversation nativeConversation =
-                    new ActionsSuggestionsModel.Conversation(nativeMessages);
-
-            ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions =
-                    actionsImpl.suggestActionsWithIntents(
-                            nativeConversation,
-                            null,
-                            mContext,
-                            getResourceLocalesString(),
-                            getAnnotatorImpl(LocaleList.getDefault()));
-            return createConversationActionResult(request, nativeSuggestions);
-        } catch (Throwable t) {
-            // Avoid throwing from this method. Log the error.
-            Log.e(LOG_TAG, "Error suggesting conversation actions.", t);
-        }
-        return mFallback.suggestConversationActions(request);
-    }
-
-    /**
-     * Returns the {@link ConversationAction} result, with a non-null extras.
-     * <p>
-     * Whenever the RemoteAction is non-null, you can expect its corresponding intent
-     * with a non-null component name is in the extras.
-     */
-    private ConversationActions createConversationActionResult(
-            ConversationActions.Request request,
-            ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions) {
-        Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
-        List<ConversationAction> conversationActions = new ArrayList<>();
-        for (ActionsSuggestionsModel.ActionSuggestion nativeSuggestion : nativeSuggestions) {
-            String actionType = nativeSuggestion.getActionType();
-            if (!expectedTypes.contains(actionType)) {
-                continue;
-            }
-            LabeledIntent.Result labeledIntentResult =
-                    ActionsSuggestionsHelper.createLabeledIntentResult(
-                            mContext,
-                            mTemplateIntentFactory,
-                            nativeSuggestion);
-            RemoteAction remoteAction = null;
-            Bundle extras = new Bundle();
-            if (labeledIntentResult != null) {
-                remoteAction = labeledIntentResult.remoteAction;
-                ExtrasUtils.putActionIntent(extras, labeledIntentResult.resolvedIntent);
-            }
-            ExtrasUtils.putSerializedEntityData(extras, nativeSuggestion.getSerializedEntityData());
-            ExtrasUtils.putEntitiesExtras(
-                    extras,
-                    TemplateIntentFactory.nameVariantsToBundle(nativeSuggestion.getEntityData()));
-            conversationActions.add(
-                    new ConversationAction.Builder(actionType)
-                            .setConfidenceScore(nativeSuggestion.getScore())
-                            .setTextReply(nativeSuggestion.getResponseText())
-                            .setAction(remoteAction)
-                            .setExtras(extras)
-                            .build());
-        }
-        conversationActions =
-                ActionsSuggestionsHelper.removeActionsWithDuplicates(conversationActions);
-        if (request.getMaxSuggestions() >= 0
-                && conversationActions.size() > request.getMaxSuggestions()) {
-            conversationActions = conversationActions.subList(0, request.getMaxSuggestions());
-        }
-        String resultId = ActionsSuggestionsHelper.createResultId(
-                mContext,
-                request.getConversation(),
-                mActionModelInUse.getVersion(),
-                mActionModelInUse.getSupportedLocales());
-        return new ConversationActions(conversationActions, resultId);
-    }
-
-    @Nullable
-    private String detectLanguageTagsFromText(CharSequence text) {
-        if (!mSettings.isDetectLanguagesFromTextEnabled()) {
-            return null;
-        }
-        final float threshold = getLangIdThreshold();
-        if (threshold < 0 || threshold > 1) {
-            Log.w(LOG_TAG,
-                    "[detectLanguageTagsFromText] unexpected threshold is found: " + threshold);
-            return null;
-        }
-        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
-        TextLanguage textLanguage = detectLanguage(request);
-        int localeHypothesisCount = textLanguage.getLocaleHypothesisCount();
-        List<String> languageTags = new ArrayList<>();
-        for (int i = 0; i < localeHypothesisCount; i++) {
-            ULocale locale = textLanguage.getLocale(i);
-            if (textLanguage.getConfidenceScore(locale) < threshold) {
-                break;
-            }
-            languageTags.add(locale.toLanguageTag());
-        }
-        if (languageTags.isEmpty()) {
-            return null;
-        }
-        return String.join(",", languageTags);
-    }
-
-    private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
-        List<String> defaultActionTypes =
-                request.getHints().contains(ConversationActions.Request.HINT_FOR_NOTIFICATION)
-                        ? mSettings.getNotificationConversationActionTypes()
-                        : mSettings.getInAppConversationActionTypes();
-        return request.getTypeConfig().resolveEntityListModifications(defaultActionTypes);
-    }
-
-    private AnnotatorModel getAnnotatorImpl(LocaleList localeList)
-            throws FileNotFoundException {
-        synchronized (mLock) {
-            localeList = localeList == null ? LocaleList.getDefault() : localeList;
-            final ModelFileManager.ModelFile bestModel =
-                    mAnnotatorModelFileManager.findBestModelFile(localeList);
-            if (bestModel == null) {
-                throw new FileNotFoundException(
-                        "No annotator model for " + localeList.toLanguageTags());
-            }
-            if (mAnnotatorImpl == null || !Objects.equals(mAnnotatorModelInUse, bestModel)) {
-                Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
-                final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
-                        new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
-                try {
-                    if (pfd != null) {
-                        // The current annotator model may be still used by another thread / model.
-                        // Do not call close() here, and let the GC to clean it up when no one else
-                        // is using it.
-                        mAnnotatorImpl = new AnnotatorModel(pfd.getFd());
-                        mAnnotatorModelInUse = bestModel;
-                    }
-                } finally {
-                    maybeCloseAndLogError(pfd);
-                }
-            }
-            return mAnnotatorImpl;
-        }
-    }
-
-    private LangIdModel getLangIdImpl() throws FileNotFoundException {
-        synchronized (mLock) {
-            final ModelFileManager.ModelFile bestModel =
-                    mLangIdModelFileManager.findBestModelFile(null);
-            if (bestModel == null) {
-                throw new FileNotFoundException("No LangID model is found");
-            }
-            if (mLangIdImpl == null || !Objects.equals(mLangIdModelInUse, bestModel)) {
-                Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
-                final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
-                        new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
-                try {
-                    if (pfd != null) {
-                        mLangIdImpl = new LangIdModel(pfd.getFd());
-                        mLangIdModelInUse = bestModel;
-                    }
-                } finally {
-                    maybeCloseAndLogError(pfd);
-                }
-            }
-            return mLangIdImpl;
-        }
-    }
-
-    @Nullable
-    private ActionsSuggestionsModel getActionsImpl() throws FileNotFoundException {
-        synchronized (mLock) {
-            // TODO: Use LangID to determine the locale we should use here?
-            final ModelFileManager.ModelFile bestModel =
-                    mActionsModelFileManager.findBestModelFile(LocaleList.getDefault());
-            if (bestModel == null) {
-                return null;
-            }
-            if (mActionsImpl == null || !Objects.equals(mActionModelInUse, bestModel)) {
-                Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
-                final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
-                        new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
-                try {
-                    if (pfd == null) {
-                        Log.d(LOG_TAG, "Failed to read the model file: " + bestModel.getPath());
-                        return null;
-                    }
-                    ActionsModelParams params = mActionsModelParamsSupplier.get();
-                    mActionsImpl = new ActionsSuggestionsModel(
-                            pfd.getFd(), params.getSerializedPreconditions(bestModel));
-                    mActionModelInUse = bestModel;
-                } finally {
-                    maybeCloseAndLogError(pfd);
-                }
-            }
-            return mActionsImpl;
-        }
-    }
-
-    private String createId(String text, int start, int end) {
-        synchronized (mLock) {
-            return SelectionSessionLogger.createId(text, start, end, mContext,
-                    mAnnotatorModelInUse.getVersion(),
-                    mAnnotatorModelInUse.getSupportedLocales());
-        }
-    }
-
-    private static String concatenateLocales(@Nullable LocaleList locales) {
-        return (locales == null) ? "" : locales.toLanguageTags();
-    }
-
-    private TextClassification createClassificationResult(
-            AnnotatorModel.ClassificationResult[] classifications,
-            String text, int start, int end, @Nullable Instant referenceTime) {
-        final String classifiedText = text.substring(start, end);
-        final TextClassification.Builder builder = new TextClassification.Builder()
-                .setText(classifiedText);
-
-        final int typeCount = classifications.length;
-        AnnotatorModel.ClassificationResult highestScoringResult =
-                typeCount > 0 ? classifications[0] : null;
-        for (int i = 0; i < typeCount; i++) {
-            builder.setEntityType(classifications[i]);
-            if (classifications[i].getScore() > highestScoringResult.getScore()) {
-                highestScoringResult = classifications[i];
-            }
-        }
-
-        final Pair<Bundle, Bundle> languagesBundles = generateLanguageBundles(text, start, end);
-        final Bundle textLanguagesBundle = languagesBundles.first;
-        final Bundle foreignLanguageBundle = languagesBundles.second;
-        builder.setForeignLanguageExtra(foreignLanguageBundle);
-
-        boolean isPrimaryAction = true;
-        final List<LabeledIntent> labeledIntents = mClassificationIntentFactory.create(
-                mContext,
-                classifiedText,
-                foreignLanguageBundle != null,
-                referenceTime,
-                highestScoringResult);
-        final LabeledIntent.TitleChooser titleChooser =
-                (labeledIntent, resolveInfo) -> labeledIntent.titleWithoutEntity;
-
-        for (LabeledIntent labeledIntent : labeledIntents) {
-            final LabeledIntent.Result result =
-                    labeledIntent.resolve(mContext, titleChooser, textLanguagesBundle);
-            if (result == null) {
-                continue;
-            }
-
-            final Intent intent = result.resolvedIntent;
-            final RemoteAction action = result.remoteAction;
-            if (isPrimaryAction) {
-                // For O backwards compatibility, the first RemoteAction is also written to the
-                // legacy API fields.
-                builder.setIcon(action.getIcon().loadDrawable(mContext));
-                builder.setLabel(action.getTitle().toString());
-                builder.setIntent(intent);
-                builder.setOnClickListener(TextClassification.createIntentOnClickListener(
-                        TextClassification.createPendingIntent(
-                                mContext, intent, labeledIntent.requestCode)));
-                isPrimaryAction = false;
-            }
-            builder.addAction(action, intent);
-        }
-        return builder.setId(createId(text, start, end)).build();
-    }
-
-    /**
-     * Returns a bundle pair with language detection information for extras.
-     * <p>
-     * Pair.first = textLanguagesBundle - A bundle containing information about all detected
-     * languages in the text. May be null if language detection fails or is disabled. This is
-     * typically expected to be added to a textClassifier generated remote action intent.
-     * See {@link ExtrasUtils#putTextLanguagesExtra(Bundle, Bundle)}.
-     * See {@link ExtrasUtils#getTopLanguage(Intent)}.
-     * <p>
-     * Pair.second = foreignLanguageBundle - A bundle with the language and confidence score if the
-     * system finds the text to be in a foreign language. Otherwise is null.
-     * See {@link TextClassification.Builder#setForeignLanguageExtra(Bundle)}.
-     *
-     * @param context the context of the text to detect languages for
-     * @param start the start index of the text
-     * @param end the end index of the text
-     */
-    // TODO: Revisit this algorithm.
-    // TODO: Consider making this public API.
-    private Pair<Bundle, Bundle> generateLanguageBundles(String context, int start, int end) {
-        if (!mSettings.isTranslateInClassificationEnabled()) {
-            return null;
-        }
-        try {
-            final float threshold = getLangIdThreshold();
-            if (threshold < 0 || threshold > 1) {
-                Log.w(LOG_TAG,
-                        "[detectForeignLanguage] unexpected threshold is found: " + threshold);
-                return Pair.create(null, null);
-            }
-
-            final EntityConfidence languageScores = detectLanguages(context, start, end);
-            if (languageScores.getEntities().isEmpty()) {
-                return Pair.create(null, null);
-            }
-
-            final Bundle textLanguagesBundle = new Bundle();
-            ExtrasUtils.putTopLanguageScores(textLanguagesBundle, languageScores);
-
-            final String language = languageScores.getEntities().get(0);
-            final float score = languageScores.getConfidenceScore(language);
-            if (score < threshold) {
-                return Pair.create(textLanguagesBundle, null);
-            }
-
-            Log.v(LOG_TAG, String.format(
-                    Locale.US, "Language detected: <%s:%.2f>", language, score));
-
-            final Locale detected = new Locale(language);
-            final LocaleList deviceLocales = LocaleList.getDefault();
-            final int size = deviceLocales.size();
-            for (int i = 0; i < size; i++) {
-                if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) {
-                    return Pair.create(textLanguagesBundle, null);
-                }
-            }
-            final Bundle foreignLanguageBundle = ExtrasUtils.createForeignLanguageExtra(
-                    detected.getLanguage(), score, getLangIdImpl().getVersion());
-            return Pair.create(textLanguagesBundle, foreignLanguageBundle);
-        } catch (Throwable t) {
-            Log.e(LOG_TAG, "Error generating language bundles.", t);
-        }
-        return Pair.create(null, null);
-    }
-
-    /**
-     * Detect the language of a piece of text by taking surrounding text into consideration.
-     *
-     * @param text text providing context for the text for which its language is to be detected
-     * @param start the start index of the text to detect its language
-     * @param end the end index of the text to detect its language
-     */
-    // TODO: Revisit this algorithm.
-    private EntityConfidence detectLanguages(String text, int start, int end)
-            throws FileNotFoundException {
-        Preconditions.checkArgument(start >= 0);
-        Preconditions.checkArgument(end <= text.length());
-        Preconditions.checkArgument(start <= end);
-
-        final float[] langIdContextSettings = mSettings.getLangIdContextSettings();
-        // The minimum size of text to prefer for detection.
-        final int minimumTextSize = (int) langIdContextSettings[0];
-        // For reducing the score when text is less than the preferred size.
-        final float penalizeRatio = langIdContextSettings[1];
-        // Original detection score to surrounding text detection score ratios.
-        final float subjectTextScoreRatio = langIdContextSettings[2];
-        final float moreTextScoreRatio = 1f - subjectTextScoreRatio;
-        Log.v(LOG_TAG,
-                String.format(Locale.US, "LangIdContextSettings: "
-                                + "minimumTextSize=%d, penalizeRatio=%.2f, "
-                                + "subjectTextScoreRatio=%.2f, moreTextScoreRatio=%.2f",
-                        minimumTextSize, penalizeRatio, subjectTextScoreRatio, moreTextScoreRatio));
-
-        if (end - start < minimumTextSize && penalizeRatio <= 0) {
-            return new EntityConfidence(Collections.emptyMap());
-        }
-
-        final String subject = text.substring(start, end);
-        final EntityConfidence scores = detectLanguages(subject);
-
-        if (subject.length() >= minimumTextSize
-                || subject.length() == text.length()
-                || subjectTextScoreRatio * penalizeRatio >= 1) {
-            return scores;
-        }
-
-        final EntityConfidence moreTextScores;
-        if (moreTextScoreRatio >= 0) {
-            // Attempt to grow the detection text to be at least minimumTextSize long.
-            final String moreText = Utils.getSubString(text, start, end, minimumTextSize);
-            moreTextScores = detectLanguages(moreText);
-        } else {
-            moreTextScores = new EntityConfidence(Collections.emptyMap());
-        }
-
-        // Combine the original detection scores with the those returned after including more text.
-        final Map<String, Float> newScores = new ArrayMap<>();
-        final Set<String> languages = new ArraySet<>();
-        languages.addAll(scores.getEntities());
-        languages.addAll(moreTextScores.getEntities());
-        for (String language : languages) {
-            final float score = (subjectTextScoreRatio * scores.getConfidenceScore(language)
-                    + moreTextScoreRatio * moreTextScores.getConfidenceScore(language))
-                    * penalizeRatio;
-            newScores.put(language, score);
-        }
-        return new EntityConfidence(newScores);
-    }
-
-    /**
-     * Detect languages for the specified text.
-     */
-    private EntityConfidence detectLanguages(String text) throws FileNotFoundException {
-        final LangIdModel langId = getLangIdImpl();
-        final LangIdModel.LanguageResult[] langResults = langId.detectLanguages(text);
-        final Map<String, Float> languagesMap = new ArrayMap<>();
-        for (LanguageResult langResult : langResults) {
-            languagesMap.put(langResult.getLanguage(), langResult.getScore());
-        }
-        return new EntityConfidence(languagesMap);
-    }
-
-    private float getLangIdThreshold() {
-        try {
-            return mSettings.getLangIdThresholdOverride() >= 0
-                    ? mSettings.getLangIdThresholdOverride()
-                    : getLangIdImpl().getLangIdThreshold();
-        } catch (FileNotFoundException e) {
-            final float defaultThreshold = 0.5f;
-            Log.v(LOG_TAG, "Using default foreign language threshold: " + defaultThreshold);
-            return defaultThreshold;
-        }
-    }
-
-    @Override
-    public void dump(@NonNull IndentingPrintWriter printWriter) {
-        synchronized (mLock) {
-            printWriter.println("TextClassifierImpl:");
-            printWriter.increaseIndent();
-            printWriter.println("Annotator model file(s):");
-            printWriter.increaseIndent();
-            for (ModelFileManager.ModelFile modelFile :
-                    mAnnotatorModelFileManager.listModelFiles()) {
-                printWriter.println(modelFile.toString());
-            }
-            printWriter.decreaseIndent();
-            printWriter.println("LangID model file(s):");
-            printWriter.increaseIndent();
-            for (ModelFileManager.ModelFile modelFile :
-                    mLangIdModelFileManager.listModelFiles()) {
-                printWriter.println(modelFile.toString());
-            }
-            printWriter.decreaseIndent();
-            printWriter.println("Actions model file(s):");
-            printWriter.increaseIndent();
-            for (ModelFileManager.ModelFile modelFile :
-                    mActionsModelFileManager.listModelFiles()) {
-                printWriter.println(modelFile.toString());
-            }
-            printWriter.decreaseIndent();
-            printWriter.printPair("mFallback", mFallback);
-            printWriter.decreaseIndent();
-            printWriter.println();
-        }
-    }
-
-    /**
-     * Closes the ParcelFileDescriptor, if non-null, and logs any errors that occur.
-     */
-    private static void maybeCloseAndLogError(@Nullable ParcelFileDescriptor fd) {
-        if (fd == null) {
-            return;
-        }
-
-        try {
-            fd.close();
-        } catch (IOException e) {
-            Log.e(LOG_TAG, "Error closing file.", e);
-        }
-    }
-
-    /**
-     * Returns the locales string for the current resources configuration.
-     */
-    private String getResourceLocalesString() {
-        try {
-            return mContext.getResources().getConfiguration().getLocales().toLanguageTags();
-        } catch (NullPointerException e) {
-            // NPE is unexpected. Erring on the side of caution.
-            return LocaleList.getDefault().toLanguageTags();
-        }
-    }
-}
diff --git a/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java
deleted file mode 100644
index 22e374f2..0000000
--- a/core/java/android/view/textclassifier/intent/ClassificationIntentFactory.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
-import java.time.Instant;
-import java.util.List;
-
-/**
- * @hide
- */
-public interface ClassificationIntentFactory {
-
-    /**
-     * Return a list of LabeledIntent from the classification result.
-     */
-    List<LabeledIntent> create(
-            Context context,
-            String text,
-            boolean foreignText,
-            @Nullable Instant referenceTime,
-            @Nullable AnnotatorModel.ClassificationResult classification);
-
-    /**
-     * Inserts translate action to the list if it is a foreign text.
-     */
-    static void insertTranslateAction(
-            List<LabeledIntent> actions, Context context, String text) {
-        actions.add(new LabeledIntent(
-                context.getString(com.android.internal.R.string.translate),
-                /* titleWithEntity */ null,
-                context.getString(com.android.internal.R.string.translate_desc),
-                /* descriptionWithAppName */ null,
-                new Intent(Intent.ACTION_TRANSLATE)
-                        // TODO: Probably better to introduce a "translate" scheme instead of
-                        // using EXTRA_TEXT.
-                        .putExtra(Intent.EXTRA_TEXT, text),
-                text.hashCode()));
-    }
-}
diff --git a/core/java/android/view/textclassifier/intent/LabeledIntent.java b/core/java/android/view/textclassifier/intent/LabeledIntent.java
deleted file mode 100644
index cbd9d1a..0000000
--- a/core/java/android/view/textclassifier/intent/LabeledIntent.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.textclassifier.ExtrasUtils;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassifier;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.Objects;
-
-/**
- * Helper class to store the information from which RemoteActions are built.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class LabeledIntent {
-    private static final String TAG = "LabeledIntent";
-    public static final int DEFAULT_REQUEST_CODE = 0;
-    private static final TitleChooser DEFAULT_TITLE_CHOOSER =
-            (labeledIntent, resolveInfo) -> {
-                if (!TextUtils.isEmpty(labeledIntent.titleWithEntity)) {
-                    return labeledIntent.titleWithEntity;
-                }
-                return labeledIntent.titleWithoutEntity;
-            };
-
-    @Nullable
-    public final String titleWithoutEntity;
-    @Nullable
-    public final String titleWithEntity;
-    public final String description;
-    @Nullable
-    public final String descriptionWithAppName;
-    // Do not update this intent.
-    public final Intent intent;
-    public final int requestCode;
-
-    /**
-     * Initializes a LabeledIntent.
-     *
-     * <p>NOTE: {@code requestCode} is required to not be {@link #DEFAULT_REQUEST_CODE}
-     * if distinguishing info (e.g. the classified text) is represented in intent extras only.
-     * In such circumstances, the request code should represent the distinguishing info
-     * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat)
-     * unique. To be correct, the PendingIntent should be definitely unique but we try a
-     * best effort approach that avoids spamming the system with PendingIntents.
-     */
-    // TODO: Fix the issue mentioned above so the behaviour is correct.
-    public LabeledIntent(
-            @Nullable String titleWithoutEntity,
-            @Nullable String titleWithEntity,
-            String description,
-            @Nullable String descriptionWithAppName,
-            Intent intent,
-            int requestCode) {
-        if (TextUtils.isEmpty(titleWithEntity) && TextUtils.isEmpty(titleWithoutEntity)) {
-            throw new IllegalArgumentException(
-                    "titleWithEntity and titleWithoutEntity should not be both null");
-        }
-        this.titleWithoutEntity = titleWithoutEntity;
-        this.titleWithEntity = titleWithEntity;
-        this.description = Objects.requireNonNull(description);
-        this.descriptionWithAppName = descriptionWithAppName;
-        this.intent = Objects.requireNonNull(intent);
-        this.requestCode = requestCode;
-    }
-
-    /**
-     * Return the resolved result.
-     *
-     * @param context the context to resolve the result's intent and action
-     * @param titleChooser for choosing an action title
-     * @param textLanguagesBundle containing language detection information
-     */
-    @Nullable
-    public Result resolve(
-            Context context,
-            @Nullable TitleChooser titleChooser,
-            @Nullable Bundle textLanguagesBundle) {
-        final PackageManager pm = context.getPackageManager();
-        final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
-
-        if (resolveInfo == null || resolveInfo.activityInfo == null) {
-            Log.w(TAG, "resolveInfo or activityInfo is null");
-            return null;
-        }
-        final String packageName = resolveInfo.activityInfo.packageName;
-        final String className = resolveInfo.activityInfo.name;
-        if (packageName == null || className == null) {
-            Log.w(TAG, "packageName or className is null");
-            return null;
-        }
-        Intent resolvedIntent = new Intent(intent);
-        resolvedIntent.putExtra(
-                TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER,
-                getFromTextClassifierExtra(textLanguagesBundle));
-        boolean shouldShowIcon = false;
-        Icon icon = null;
-        if (!"android".equals(packageName)) {
-            // We only set the component name when the package name is not resolved to "android"
-            // to workaround a bug that explicit intent with component name == ResolverActivity
-            // can't be launched on keyguard.
-            resolvedIntent.setComponent(new ComponentName(packageName, className));
-            if (resolveInfo.activityInfo.getIconResource() != 0) {
-                icon = Icon.createWithResource(
-                        packageName, resolveInfo.activityInfo.getIconResource());
-                shouldShowIcon = true;
-            }
-        }
-        if (icon == null) {
-            // RemoteAction requires that there be an icon.
-            icon = Icon.createWithResource(
-                    "android", com.android.internal.R.drawable.ic_more_items);
-        }
-        final PendingIntent pendingIntent =
-                TextClassification.createPendingIntent(context, resolvedIntent, requestCode);
-        titleChooser = titleChooser == null ? DEFAULT_TITLE_CHOOSER : titleChooser;
-        CharSequence title = titleChooser.chooseTitle(this, resolveInfo);
-        if (TextUtils.isEmpty(title)) {
-            Log.w(TAG, "Custom titleChooser return null, fallback to the default titleChooser");
-            title = DEFAULT_TITLE_CHOOSER.chooseTitle(this, resolveInfo);
-        }
-        final RemoteAction action =
-                new RemoteAction(icon, title, resolveDescription(resolveInfo, pm), pendingIntent);
-        action.setShouldShowIcon(shouldShowIcon);
-        return new Result(resolvedIntent, action);
-    }
-
-    private String resolveDescription(ResolveInfo resolveInfo, PackageManager packageManager) {
-        if (!TextUtils.isEmpty(descriptionWithAppName)) {
-            // Example string format of descriptionWithAppName: "Use %1$s to open map".
-            String applicationName = getApplicationName(resolveInfo, packageManager);
-            if (!TextUtils.isEmpty(applicationName)) {
-                return String.format(descriptionWithAppName, applicationName);
-            }
-        }
-        return description;
-    }
-
-    @Nullable
-    private String getApplicationName(
-            ResolveInfo resolveInfo, PackageManager packageManager) {
-        if (resolveInfo.activityInfo == null) {
-            return null;
-        }
-        if ("android".equals(resolveInfo.activityInfo.packageName)) {
-            return null;
-        }
-        if (resolveInfo.activityInfo.applicationInfo == null) {
-            return null;
-        }
-        return (String) packageManager.getApplicationLabel(
-                resolveInfo.activityInfo.applicationInfo);
-    }
-
-    private Bundle getFromTextClassifierExtra(@Nullable Bundle textLanguagesBundle) {
-        if (textLanguagesBundle != null) {
-            final Bundle bundle = new Bundle();
-            ExtrasUtils.putTextLanguagesExtra(bundle, textLanguagesBundle);
-            return bundle;
-        } else {
-            return Bundle.EMPTY;
-        }
-    }
-
-    /**
-     * Data class that holds the result.
-     */
-    public static final class Result {
-        public final Intent resolvedIntent;
-        public final RemoteAction remoteAction;
-
-        public Result(Intent resolvedIntent, RemoteAction remoteAction) {
-            this.resolvedIntent = Objects.requireNonNull(resolvedIntent);
-            this.remoteAction = Objects.requireNonNull(remoteAction);
-        }
-    }
-
-    /**
-     * An object to choose a title from resolved info.  If {@code null} is returned,
-     * {@link #titleWithEntity} will be used if it exists, {@link #titleWithoutEntity} otherwise.
-     */
-    public interface TitleChooser {
-        /**
-         * Picks a title from a {@link LabeledIntent} by looking into resolved info.
-         * {@code resolveInfo} is guaranteed to have a non-null {@code activityInfo}.
-         */
-        @Nullable
-        CharSequence chooseTitle(LabeledIntent labeledIntent, ResolveInfo resolveInfo);
-    }
-}
diff --git a/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java
deleted file mode 100644
index 8d60ad8..0000000
--- a/core/java/android/view/textclassifier/intent/LegacyClassificationIntentFactory.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import static java.time.temporal.ChronoUnit.MILLIS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.SearchManager;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.provider.Browser;
-import android.provider.CalendarContract;
-import android.provider.ContactsContract;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassifier;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Creates intents based on the classification type.
- * @hide
- */
-// TODO: Consider to support {@code descriptionWithAppName}.
-public final class LegacyClassificationIntentFactory implements ClassificationIntentFactory {
-
-    private static final String TAG = "LegacyClassificationIntentFactory";
-    private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
-    private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
-
-    @NonNull
-    @Override
-    public List<LabeledIntent> create(Context context, String text, boolean foreignText,
-            @Nullable Instant referenceTime,
-            AnnotatorModel.ClassificationResult classification) {
-        final String type = classification != null
-                ? classification.getCollection().trim().toLowerCase(Locale.ENGLISH)
-                : "";
-        text = text.trim();
-        final List<LabeledIntent> actions;
-        switch (type) {
-            case TextClassifier.TYPE_EMAIL:
-                actions = createForEmail(context, text);
-                break;
-            case TextClassifier.TYPE_PHONE:
-                actions = createForPhone(context, text);
-                break;
-            case TextClassifier.TYPE_ADDRESS:
-                actions = createForAddress(context, text);
-                break;
-            case TextClassifier.TYPE_URL:
-                actions = createForUrl(context, text);
-                break;
-            case TextClassifier.TYPE_DATE:  // fall through
-            case TextClassifier.TYPE_DATE_TIME:
-                if (classification.getDatetimeResult() != null) {
-                    final Instant parsedTime = Instant.ofEpochMilli(
-                            classification.getDatetimeResult().getTimeMsUtc());
-                    actions = createForDatetime(context, type, referenceTime, parsedTime);
-                } else {
-                    actions = new ArrayList<>();
-                }
-                break;
-            case TextClassifier.TYPE_FLIGHT_NUMBER:
-                actions = createForFlight(context, text);
-                break;
-            case TextClassifier.TYPE_DICTIONARY:
-                actions = createForDictionary(context, text);
-                break;
-            default:
-                actions = new ArrayList<>();
-                break;
-        }
-        if (foreignText) {
-            ClassificationIntentFactory.insertTranslateAction(actions, context, text);
-        }
-        return actions;
-    }
-
-    @NonNull
-    private static List<LabeledIntent> createForEmail(Context context, String text) {
-        final List<LabeledIntent> actions = new ArrayList<>();
-        actions.add(new LabeledIntent(
-                context.getString(com.android.internal.R.string.email),
-                /* titleWithEntity */ null,
-                context.getString(com.android.internal.R.string.email_desc),
-                /* descriptionWithAppName */ null,
-                new Intent(Intent.ACTION_SENDTO)
-                        .setData(Uri.parse(String.format("mailto:%s", text))),
-                LabeledIntent.DEFAULT_REQUEST_CODE));
-        actions.add(new LabeledIntent(
-                context.getString(com.android.internal.R.string.add_contact),
-                /* titleWithEntity */ null,
-                context.getString(com.android.internal.R.string.add_contact_desc),
-                /* descriptionWithAppName */ null,
-                new Intent(Intent.ACTION_INSERT_OR_EDIT)
-                        .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                        .putExtra(ContactsContract.Intents.Insert.EMAIL, text),
-                text.hashCode()));
-        return actions;
-    }
-
-    @NonNull
-    private static List<LabeledIntent> createForPhone(Context context, String text) {
-        final List<LabeledIntent> actions = new ArrayList<>();
-        final UserManager userManager = context.getSystemService(UserManager.class);
-        final Bundle userRestrictions = userManager != null
-                ? userManager.getUserRestrictions() : new Bundle();
-        if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) {
-            actions.add(new LabeledIntent(
-                    context.getString(com.android.internal.R.string.dial),
-                    /* titleWithEntity */ null,
-                    context.getString(com.android.internal.R.string.dial_desc),
-                    /* descriptionWithAppName */ null,
-                    new Intent(Intent.ACTION_DIAL).setData(
-                            Uri.parse(String.format("tel:%s", text))),
-                    LabeledIntent.DEFAULT_REQUEST_CODE));
-        }
-        actions.add(new LabeledIntent(
-                context.getString(com.android.internal.R.string.add_contact),
-                /* titleWithEntity */ null,
-                context.getString(com.android.internal.R.string.add_contact_desc),
-                /* descriptionWithAppName */ null,
-                new Intent(Intent.ACTION_INSERT_OR_EDIT)
-                        .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                        .putExtra(ContactsContract.Intents.Insert.PHONE, text),
-                text.hashCode()));
-        if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
-            actions.add(new LabeledIntent(
-                    context.getString(com.android.internal.R.string.sms),
-                    /* titleWithEntity */ null,
-                    context.getString(com.android.internal.R.string.sms_desc),
-                    /* descriptionWithAppName */ null,
-                    new Intent(Intent.ACTION_SENDTO)
-                            .setData(Uri.parse(String.format("smsto:%s", text))),
-                    LabeledIntent.DEFAULT_REQUEST_CODE));
-        }
-        return actions;
-    }
-
-    @NonNull
-    private static List<LabeledIntent> createForAddress(Context context, String text) {
-        final List<LabeledIntent> actions = new ArrayList<>();
-        try {
-            final String encText = URLEncoder.encode(text, "UTF-8");
-            actions.add(new LabeledIntent(
-                    context.getString(com.android.internal.R.string.map),
-                    /* titleWithEntity */ null,
-                    context.getString(com.android.internal.R.string.map_desc),
-                    /* descriptionWithAppName */ null,
-                    new Intent(Intent.ACTION_VIEW)
-                            .setData(Uri.parse(String.format("geo:0,0?q=%s", encText))),
-                    LabeledIntent.DEFAULT_REQUEST_CODE));
-        } catch (UnsupportedEncodingException e) {
-            Log.e(TAG, "Could not encode address", e);
-        }
-        return actions;
-    }
-
-    @NonNull
-    private static List<LabeledIntent> createForUrl(Context context, String text) {
-        if (Uri.parse(text).getScheme() == null) {
-            text = "http://" + text;
-        }
-        final List<LabeledIntent> actions = new ArrayList<>();
-        actions.add(new LabeledIntent(
-                context.getString(com.android.internal.R.string.browse),
-                /* titleWithEntity */ null,
-                context.getString(com.android.internal.R.string.browse_desc),
-                /* descriptionWithAppName */ null,
-                new Intent(Intent.ACTION_VIEW)
-                        .setDataAndNormalize(Uri.parse(text))
-                        .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()),
-                LabeledIntent.DEFAULT_REQUEST_CODE));
-        return actions;
-    }
-
-    @NonNull
-    private static List<LabeledIntent> createForDatetime(
-            Context context, String type, @Nullable Instant referenceTime,
-            Instant parsedTime) {
-        if (referenceTime == null) {
-            // If no reference time was given, use now.
-            referenceTime = Instant.now();
-        }
-        List<LabeledIntent> actions = new ArrayList<>();
-        actions.add(createCalendarViewIntent(context, parsedTime));
-        final long millisUntilEvent = referenceTime.until(parsedTime, MILLIS);
-        if (millisUntilEvent > MIN_EVENT_FUTURE_MILLIS) {
-            actions.add(createCalendarCreateEventIntent(context, parsedTime, type));
-        }
-        return actions;
-    }
-
-    @NonNull
-    private static List<LabeledIntent> createForFlight(Context context, String text) {
-        final List<LabeledIntent> actions = new ArrayList<>();
-        actions.add(new LabeledIntent(
-                context.getString(com.android.internal.R.string.view_flight),
-                /* titleWithEntity */ null,
-                context.getString(com.android.internal.R.string.view_flight_desc),
-                /* descriptionWithAppName */ null,
-                new Intent(Intent.ACTION_WEB_SEARCH)
-                        .putExtra(SearchManager.QUERY, text),
-                text.hashCode()));
-        return actions;
-    }
-
-    @NonNull
-    private static LabeledIntent createCalendarViewIntent(Context context, Instant parsedTime) {
-        Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
-        builder.appendPath("time");
-        ContentUris.appendId(builder, parsedTime.toEpochMilli());
-        return new LabeledIntent(
-                context.getString(com.android.internal.R.string.view_calendar),
-                /* titleWithEntity */ null,
-                context.getString(com.android.internal.R.string.view_calendar_desc),
-                /* descriptionWithAppName */ null,
-                new Intent(Intent.ACTION_VIEW).setData(builder.build()),
-                LabeledIntent.DEFAULT_REQUEST_CODE);
-    }
-
-    @NonNull
-    private static LabeledIntent createCalendarCreateEventIntent(
-            Context context, Instant parsedTime, @TextClassifier.EntityType String type) {
-        final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type);
-        return new LabeledIntent(
-                context.getString(com.android.internal.R.string.add_calendar_event),
-                /* titleWithEntity */ null,
-                context.getString(com.android.internal.R.string.add_calendar_event_desc),
-                /* descriptionWithAppName */ null,
-                new Intent(Intent.ACTION_INSERT)
-                        .setData(CalendarContract.Events.CONTENT_URI)
-                        .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
-                        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
-                                parsedTime.toEpochMilli())
-                        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
-                                parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION),
-                parsedTime.hashCode());
-    }
-
-    @NonNull
-    private static List<LabeledIntent> createForDictionary(Context context, String text) {
-        final List<LabeledIntent> actions = new ArrayList<>();
-        actions.add(new LabeledIntent(
-                context.getString(com.android.internal.R.string.define),
-                /* titleWithEntity */ null,
-                context.getString(com.android.internal.R.string.define_desc),
-                /* descriptionWithAppName */ null,
-                new Intent(Intent.ACTION_DEFINE)
-                        .putExtra(Intent.EXTRA_TEXT, text),
-                text.hashCode()));
-        return actions;
-    }
-}
diff --git a/core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java b/core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java
deleted file mode 100644
index aef4bd6..0000000
--- a/core/java/android/view/textclassifier/intent/TemplateClassificationIntentFactory.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassifier;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-
-import com.google.android.textclassifier.AnnotatorModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import java.time.Instant;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Creates intents based on {@link RemoteActionTemplate} objects for a ClassificationResult.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class TemplateClassificationIntentFactory implements ClassificationIntentFactory {
-    private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
-    private final TemplateIntentFactory mTemplateIntentFactory;
-    private final ClassificationIntentFactory mFallback;
-
-    public TemplateClassificationIntentFactory(TemplateIntentFactory templateIntentFactory,
-            ClassificationIntentFactory fallback) {
-        mTemplateIntentFactory = Objects.requireNonNull(templateIntentFactory);
-        mFallback = Objects.requireNonNull(fallback);
-    }
-
-    /**
-     * Returns a list of {@link LabeledIntent}
-     * that are constructed from the classification result.
-     */
-    @NonNull
-    @Override
-    public List<LabeledIntent> create(
-            Context context,
-            String text,
-            boolean foreignText,
-            @Nullable Instant referenceTime,
-            @Nullable AnnotatorModel.ClassificationResult classification) {
-        if (classification == null) {
-            return Collections.emptyList();
-        }
-        RemoteActionTemplate[] remoteActionTemplates = classification.getRemoteActionTemplates();
-        if (remoteActionTemplates == null) {
-            // RemoteActionTemplate is missing, fallback.
-            Log.w(TAG, "RemoteActionTemplate is missing, fallback to"
-                    + " LegacyClassificationIntentFactory.");
-            return mFallback.create(context, text, foreignText, referenceTime, classification);
-        }
-        final List<LabeledIntent> labeledIntents =
-                mTemplateIntentFactory.create(remoteActionTemplates);
-        if (foreignText) {
-            ClassificationIntentFactory.insertTranslateAction(labeledIntents, context, text.trim());
-        }
-        return labeledIntents;
-    }
-}
diff --git a/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java b/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java
deleted file mode 100644
index 7a39569..0000000
--- a/core/java/android/view/textclassifier/intent/TemplateIntentFactory.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.textclassifier.Log;
-import android.view.textclassifier.TextClassifier;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.android.textclassifier.NamedVariant;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Creates intents based on {@link RemoteActionTemplate} objects.
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class TemplateIntentFactory {
-    private static final String TAG = TextClassifier.DEFAULT_LOG_TAG;
-
-    /**
-     * Constructs and returns a list of {@link LabeledIntent} based on the given templates.
-     */
-    @Nullable
-    public List<LabeledIntent> create(
-            @NonNull RemoteActionTemplate[] remoteActionTemplates) {
-        if (remoteActionTemplates.length == 0) {
-            return new ArrayList<>();
-        }
-        final List<LabeledIntent> labeledIntents = new ArrayList<>();
-        for (RemoteActionTemplate remoteActionTemplate : remoteActionTemplates) {
-            if (!isValidTemplate(remoteActionTemplate)) {
-                Log.w(TAG, "Invalid RemoteActionTemplate skipped.");
-                continue;
-            }
-            labeledIntents.add(
-                    new LabeledIntent(
-                            remoteActionTemplate.titleWithoutEntity,
-                            remoteActionTemplate.titleWithEntity,
-                            remoteActionTemplate.description,
-                            remoteActionTemplate.descriptionWithAppName,
-                            createIntent(remoteActionTemplate),
-                            remoteActionTemplate.requestCode == null
-                                    ? LabeledIntent.DEFAULT_REQUEST_CODE
-                                    : remoteActionTemplate.requestCode));
-        }
-        return labeledIntents;
-    }
-
-    private static boolean isValidTemplate(@Nullable RemoteActionTemplate remoteActionTemplate) {
-        if (remoteActionTemplate == null) {
-            Log.w(TAG, "Invalid RemoteActionTemplate: is null");
-            return false;
-        }
-        if (TextUtils.isEmpty(remoteActionTemplate.titleWithEntity)
-                && TextUtils.isEmpty(remoteActionTemplate.titleWithoutEntity)) {
-            Log.w(TAG, "Invalid RemoteActionTemplate: title is null");
-            return false;
-        }
-        if (TextUtils.isEmpty(remoteActionTemplate.description)) {
-            Log.w(TAG, "Invalid RemoteActionTemplate: description is null");
-            return false;
-        }
-        if (!TextUtils.isEmpty(remoteActionTemplate.packageName)) {
-            Log.w(TAG, "Invalid RemoteActionTemplate: package name is set");
-            return false;
-        }
-        if (TextUtils.isEmpty(remoteActionTemplate.action)) {
-            Log.w(TAG, "Invalid RemoteActionTemplate: intent action not set");
-            return false;
-        }
-        return true;
-    }
-
-    private static Intent createIntent(RemoteActionTemplate remoteActionTemplate) {
-        final Intent intent = new Intent(remoteActionTemplate.action);
-        final Uri uri = TextUtils.isEmpty(remoteActionTemplate.data)
-                ? null : Uri.parse(remoteActionTemplate.data).normalizeScheme();
-        final String type = TextUtils.isEmpty(remoteActionTemplate.type)
-                ? null : Intent.normalizeMimeType(remoteActionTemplate.type);
-        intent.setDataAndType(uri, type);
-        intent.setFlags(remoteActionTemplate.flags == null ? 0 : remoteActionTemplate.flags);
-        if (remoteActionTemplate.category != null) {
-            for (String category : remoteActionTemplate.category) {
-                if (category != null) {
-                    intent.addCategory(category);
-                }
-            }
-        }
-        intent.putExtras(nameVariantsToBundle(remoteActionTemplate.extras));
-        return intent;
-    }
-
-    /**
-     * Converts an array of {@link NamedVariant} to a Bundle and returns it.
-     */
-    public static Bundle nameVariantsToBundle(@Nullable NamedVariant[] namedVariants) {
-        if (namedVariants == null) {
-            return Bundle.EMPTY;
-        }
-        Bundle bundle = new Bundle();
-        for (NamedVariant namedVariant : namedVariants) {
-            if (namedVariant == null) {
-                continue;
-            }
-            switch (namedVariant.getType()) {
-                case NamedVariant.TYPE_INT:
-                    bundle.putInt(namedVariant.getName(), namedVariant.getInt());
-                    break;
-                case NamedVariant.TYPE_LONG:
-                    bundle.putLong(namedVariant.getName(), namedVariant.getLong());
-                    break;
-                case NamedVariant.TYPE_FLOAT:
-                    bundle.putFloat(namedVariant.getName(), namedVariant.getFloat());
-                    break;
-                case NamedVariant.TYPE_DOUBLE:
-                    bundle.putDouble(namedVariant.getName(), namedVariant.getDouble());
-                    break;
-                case NamedVariant.TYPE_BOOL:
-                    bundle.putBoolean(namedVariant.getName(), namedVariant.getBool());
-                    break;
-                case NamedVariant.TYPE_STRING:
-                    bundle.putString(namedVariant.getName(), namedVariant.getString());
-                    break;
-                default:
-                    Log.w(TAG,
-                            "Unsupported type found in nameVariantsToBundle : "
-                                    + namedVariant.getType());
-            }
-        }
-        return bundle;
-    }
-}
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
deleted file mode 100644
index 28cb80d..0000000
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.logging;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
-import android.metrics.LogMaker;
-import android.os.Build;
-import android.util.Log;
-import android.view.textclassifier.TextClassification;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextSelection;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-import java.util.UUID;
-
-/**
- * A selection event tracker.
- * @hide
- */
-//TODO: Do not allow any crashes from this class.
-public final class SmartSelectionEventTracker {
-
-    private static final String LOG_TAG = "SmartSelectEventTracker";
-    private static final boolean DEBUG_LOG_ENABLED = true;
-
-    private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
-    private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
-    private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
-    private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
-    private static final int WIDGET_VERSION = MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
-    private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
-    private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
-    private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
-    private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
-    private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
-    private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
-    private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
-
-    private static final String ZERO = "0";
-    private static final String TEXTVIEW = "textview";
-    private static final String EDITTEXT = "edittext";
-    private static final String UNSELECTABLE_TEXTVIEW = "nosel-textview";
-    private static final String WEBVIEW = "webview";
-    private static final String EDIT_WEBVIEW = "edit-webview";
-    private static final String CUSTOM_TEXTVIEW = "customview";
-    private static final String CUSTOM_EDITTEXT = "customedit";
-    private static final String CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
-    private static final String UNKNOWN = "unknown";
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({WidgetType.UNSPECIFIED, WidgetType.TEXTVIEW, WidgetType.WEBVIEW,
-            WidgetType.EDITTEXT, WidgetType.EDIT_WEBVIEW})
-    public @interface WidgetType {
-        int UNSPECIFIED = 0;
-        int TEXTVIEW = 1;
-        int WEBVIEW = 2;
-        int EDITTEXT = 3;
-        int EDIT_WEBVIEW = 4;
-        int UNSELECTABLE_TEXTVIEW = 5;
-        int CUSTOM_TEXTVIEW = 6;
-        int CUSTOM_EDITTEXT = 7;
-        int CUSTOM_UNSELECTABLE_TEXTVIEW = 8;
-    }
-
-    private final MetricsLogger mMetricsLogger = new MetricsLogger();
-    private final int mWidgetType;
-    @Nullable private final String mWidgetVersion;
-    private final Context mContext;
-
-    @Nullable private String mSessionId;
-    private final int[] mSmartIndices = new int[2];
-    private final int[] mPrevIndices = new int[2];
-    private int mOrigStart;
-    private int mIndex;
-    private long mSessionStartTime;
-    private long mLastEventTime;
-    private boolean mSmartSelectionTriggered;
-    private String mModelName;
-
-    @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
-            publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
-    public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
-        mWidgetType = widgetType;
-        mWidgetVersion = null;
-        mContext = Objects.requireNonNull(context);
-    }
-
-    public SmartSelectionEventTracker(
-            @NonNull Context context, @WidgetType int widgetType, @Nullable String widgetVersion) {
-        mWidgetType = widgetType;
-        mWidgetVersion = widgetVersion;
-        mContext = Objects.requireNonNull(context);
-    }
-
-    /**
-     * Logs a selection event.
-     *
-     * @param event the selection event
-     */
-    @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
-            publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
-    public void logEvent(@NonNull SelectionEvent event) {
-        Objects.requireNonNull(event);
-
-        if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null
-                && DEBUG_LOG_ENABLED) {
-            Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
-            return;
-        }
-
-        final long now = System.currentTimeMillis();
-        switch (event.mEventType) {
-            case SelectionEvent.EventType.SELECTION_STARTED:
-                mSessionId = startNewSession();
-                Preconditions.checkArgument(event.mEnd == event.mStart + 1);
-                mOrigStart = event.mStart;
-                mSessionStartTime = now;
-                break;
-            case SelectionEvent.EventType.SMART_SELECTION_SINGLE:  // fall through
-            case SelectionEvent.EventType.SMART_SELECTION_MULTI:
-                mSmartSelectionTriggered = true;
-                mModelName = getModelName(event);
-                mSmartIndices[0] = event.mStart;
-                mSmartIndices[1] = event.mEnd;
-                break;
-            case SelectionEvent.EventType.SELECTION_MODIFIED:  // fall through
-            case SelectionEvent.EventType.AUTO_SELECTION:
-                if (mPrevIndices[0] == event.mStart && mPrevIndices[1] == event.mEnd) {
-                    // Selection did not change. Ignore event.
-                    return;
-                }
-        }
-        writeEvent(event, now);
-
-        if (event.isTerminal()) {
-            endSession();
-        }
-    }
-
-    private void writeEvent(SelectionEvent event, long now) {
-        final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime;
-        final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
-                .setType(getLogType(event))
-                .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
-                .setPackageName(mContext.getPackageName())
-                .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime)
-                .addTaggedData(PREV_EVENT_DELTA, prevEventDelta)
-                .addTaggedData(INDEX, mIndex)
-                .addTaggedData(WIDGET_TYPE, getWidgetTypeName())
-                .addTaggedData(WIDGET_VERSION, mWidgetVersion)
-                .addTaggedData(MODEL_NAME, mModelName)
-                .addTaggedData(ENTITY_TYPE, event.mEntityType)
-                .addTaggedData(SMART_START, getSmartRangeDelta(mSmartIndices[0]))
-                .addTaggedData(SMART_END, getSmartRangeDelta(mSmartIndices[1]))
-                .addTaggedData(EVENT_START, getRangeDelta(event.mStart))
-                .addTaggedData(EVENT_END, getRangeDelta(event.mEnd))
-                .addTaggedData(SESSION_ID, mSessionId);
-        mMetricsLogger.write(log);
-        debugLog(log);
-        mLastEventTime = now;
-        mPrevIndices[0] = event.mStart;
-        mPrevIndices[1] = event.mEnd;
-        mIndex++;
-    }
-
-    private String startNewSession() {
-        endSession();
-        mSessionId = createSessionId();
-        return mSessionId;
-    }
-
-    private void endSession() {
-        // Reset fields.
-        mOrigStart = 0;
-        mSmartIndices[0] = mSmartIndices[1] = 0;
-        mPrevIndices[0] = mPrevIndices[1] = 0;
-        mIndex = 0;
-        mSessionStartTime = 0;
-        mLastEventTime = 0;
-        mSmartSelectionTriggered = false;
-        mModelName = getModelName(null);
-        mSessionId = null;
-    }
-
-    private static int getLogType(SelectionEvent event) {
-        switch (event.mEventType) {
-            case SelectionEvent.ActionType.OVERTYPE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
-            case SelectionEvent.ActionType.COPY:
-                return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
-            case SelectionEvent.ActionType.PASTE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
-            case SelectionEvent.ActionType.CUT:
-                return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
-            case SelectionEvent.ActionType.SHARE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
-            case SelectionEvent.ActionType.SMART_SHARE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
-            case SelectionEvent.ActionType.DRAG:
-                return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
-            case SelectionEvent.ActionType.ABANDON:
-                return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
-            case SelectionEvent.ActionType.OTHER:
-                return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
-            case SelectionEvent.ActionType.SELECT_ALL:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
-            case SelectionEvent.ActionType.RESET:
-                return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
-            case SelectionEvent.EventType.SELECTION_STARTED:
-                return MetricsEvent.ACTION_TEXT_SELECTION_START;
-            case SelectionEvent.EventType.SELECTION_MODIFIED:
-                return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
-            case SelectionEvent.EventType.SMART_SELECTION_SINGLE:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
-            case SelectionEvent.EventType.SMART_SELECTION_MULTI:
-                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
-            case SelectionEvent.EventType.AUTO_SELECTION:
-                return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
-            default:
-                return MetricsEvent.VIEW_UNKNOWN;
-        }
-    }
-
-    private static String getLogTypeString(int logType) {
-        switch (logType) {
-            case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
-                return "OVERTYPE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
-                return "COPY";
-            case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
-                return "PASTE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
-                return "CUT";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
-                return "SHARE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
-                return "SMART_SHARE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
-                return "DRAG";
-            case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
-                return "ABANDON";
-            case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
-                return "OTHER";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
-                return "SELECT_ALL";
-            case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
-                return "RESET";
-            case MetricsEvent.ACTION_TEXT_SELECTION_START:
-                return "SELECTION_STARTED";
-            case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
-                return "SELECTION_MODIFIED";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
-                return "SMART_SELECTION_SINGLE";
-            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
-                return "SMART_SELECTION_MULTI";
-            case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
-                return "AUTO_SELECTION";
-            default:
-                return UNKNOWN;
-        }
-    }
-
-    private int getRangeDelta(int offset) {
-        return offset - mOrigStart;
-    }
-
-    private int getSmartRangeDelta(int offset) {
-        return mSmartSelectionTriggered ? getRangeDelta(offset) : 0;
-    }
-
-    private String getWidgetTypeName() {
-        switch (mWidgetType) {
-            case WidgetType.TEXTVIEW:
-                return TEXTVIEW;
-            case WidgetType.WEBVIEW:
-                return WEBVIEW;
-            case WidgetType.EDITTEXT:
-                return EDITTEXT;
-            case WidgetType.EDIT_WEBVIEW:
-                return EDIT_WEBVIEW;
-            case WidgetType.UNSELECTABLE_TEXTVIEW:
-                return UNSELECTABLE_TEXTVIEW;
-            case WidgetType.CUSTOM_TEXTVIEW:
-                return CUSTOM_TEXTVIEW;
-            case WidgetType.CUSTOM_EDITTEXT:
-                return CUSTOM_EDITTEXT;
-            case WidgetType.CUSTOM_UNSELECTABLE_TEXTVIEW:
-                return CUSTOM_UNSELECTABLE_TEXTVIEW;
-            default:
-                return UNKNOWN;
-        }
-    }
-
-    private String getModelName(@Nullable SelectionEvent event) {
-        return event == null
-                ? SelectionEvent.NO_VERSION_TAG
-                : Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
-    }
-
-    private static String createSessionId() {
-        return UUID.randomUUID().toString();
-    }
-
-    private static void debugLog(LogMaker log) {
-        if (!DEBUG_LOG_ENABLED) return;
-
-        final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
-        final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
-        final String widget = widgetVersion.isEmpty()
-                ? widgetType : widgetType + "-" + widgetVersion;
-        final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
-        if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
-            String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
-            sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
-            Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
-        }
-
-        final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
-        final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
-        final String type = getLogTypeString(log.getType());
-        final int smartStart = Integer.parseInt(
-                Objects.toString(log.getTaggedData(SMART_START), ZERO));
-        final int smartEnd = Integer.parseInt(
-                Objects.toString(log.getTaggedData(SMART_END), ZERO));
-        final int eventStart = Integer.parseInt(
-                Objects.toString(log.getTaggedData(EVENT_START), ZERO));
-        final int eventEnd = Integer.parseInt(
-                Objects.toString(log.getTaggedData(EVENT_END), ZERO));
-
-        Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
-                index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
-    }
-
-    /**
-     * A selection event.
-     * Specify index parameters as word token indices.
-     */
-    public static final class SelectionEvent {
-
-        /**
-         * Use this to specify an indeterminate positive index.
-         */
-        public static final int OUT_OF_BOUNDS = Integer.MAX_VALUE;
-
-        /**
-         * Use this to specify an indeterminate negative index.
-         */
-        public static final int OUT_OF_BOUNDS_NEGATIVE = Integer.MIN_VALUE;
-
-        private static final String NO_VERSION_TAG = "";
-
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({ActionType.OVERTYPE, ActionType.COPY, ActionType.PASTE, ActionType.CUT,
-                ActionType.SHARE, ActionType.SMART_SHARE, ActionType.DRAG, ActionType.ABANDON,
-                ActionType.OTHER, ActionType.SELECT_ALL, ActionType.RESET})
-        public @interface ActionType {
-        /** User typed over the selection. */
-        int OVERTYPE = 100;
-        /** User copied the selection. */
-        int COPY = 101;
-        /** User pasted over the selection. */
-        int PASTE = 102;
-        /** User cut the selection. */
-        int CUT = 103;
-        /** User shared the selection. */
-        int SHARE = 104;
-        /** User clicked the textAssist menu item. */
-        int SMART_SHARE = 105;
-        /** User dragged+dropped the selection. */
-        int DRAG = 106;
-        /** User abandoned the selection. */
-        int ABANDON = 107;
-        /** User performed an action on the selection. */
-        int OTHER = 108;
-
-        /* Non-terminal actions. */
-        /** User activated Select All */
-        int SELECT_ALL = 200;
-        /** User reset the smart selection. */
-        int RESET = 201;
-        }
-
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({ActionType.OVERTYPE, ActionType.COPY, ActionType.PASTE, ActionType.CUT,
-                ActionType.SHARE, ActionType.SMART_SHARE, ActionType.DRAG, ActionType.ABANDON,
-                ActionType.OTHER, ActionType.SELECT_ALL, ActionType.RESET,
-                EventType.SELECTION_STARTED, EventType.SELECTION_MODIFIED,
-                EventType.SMART_SELECTION_SINGLE, EventType.SMART_SELECTION_MULTI,
-                EventType.AUTO_SELECTION})
-        private @interface EventType {
-        /** User started a new selection. */
-        int SELECTION_STARTED = 1;
-        /** User modified an existing selection. */
-        int SELECTION_MODIFIED = 2;
-        /** Smart selection triggered for a single token (word). */
-        int SMART_SELECTION_SINGLE = 3;
-        /** Smart selection triggered spanning multiple tokens (words). */
-        int SMART_SELECTION_MULTI = 4;
-        /** Something else other than User or the default TextClassifier triggered a selection. */
-        int AUTO_SELECTION = 5;
-        }
-
-        private final int mStart;
-        private final int mEnd;
-        private @EventType int mEventType;
-        private final @TextClassifier.EntityType String mEntityType;
-        private final String mVersionTag;
-
-        private SelectionEvent(
-                int start, int end, int eventType,
-                @TextClassifier.EntityType String entityType, String versionTag) {
-            Preconditions.checkArgument(end >= start, "end cannot be less than start");
-            mStart = start;
-            mEnd = end;
-            mEventType = eventType;
-            mEntityType = Objects.requireNonNull(entityType);
-            mVersionTag = Objects.requireNonNull(versionTag);
-        }
-
-        /**
-         * Creates a "selection started" event.
-         *
-         * @param start  the word index of the selected word
-         */
-        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
-                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
-        public static SelectionEvent selectionStarted(int start) {
-            return new SelectionEvent(
-                    start, start + 1, EventType.SELECTION_STARTED,
-                    TextClassifier.TYPE_UNKNOWN, NO_VERSION_TAG);
-        }
-
-        /**
-         * Creates a "selection modified" event.
-         * Use when the user modifies the selection.
-         *
-         * @param start  the start word (inclusive) index of the selection
-         * @param end  the end word (exclusive) index of the selection
-         */
-        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
-                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
-        public static SelectionEvent selectionModified(int start, int end) {
-            return new SelectionEvent(
-                    start, end, EventType.SELECTION_MODIFIED,
-                    TextClassifier.TYPE_UNKNOWN, NO_VERSION_TAG);
-        }
-
-        /**
-         * Creates a "selection modified" event.
-         * Use when the user modifies the selection and the selection's entity type is known.
-         *
-         * @param start  the start word (inclusive) index of the selection
-         * @param end  the end word (exclusive) index of the selection
-         * @param classification  the TextClassification object returned by the TextClassifier that
-         *      classified the selected text
-         */
-        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
-                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
-        public static SelectionEvent selectionModified(
-                int start, int end, @NonNull TextClassification classification) {
-            final String entityType = classification.getEntityCount() > 0
-                    ? classification.getEntity(0)
-                    : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = getVersionInfo(classification.getId());
-            return new SelectionEvent(
-                    start, end, EventType.SELECTION_MODIFIED, entityType, versionTag);
-        }
-
-        /**
-         * Creates a "selection modified" event.
-         * Use when a TextClassifier modifies the selection.
-         *
-         * @param start  the start word (inclusive) index of the selection
-         * @param end  the end word (exclusive) index of the selection
-         * @param selection  the TextSelection object returned by the TextClassifier for the
-         *      specified selection
-         */
-        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
-                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
-        public static SelectionEvent selectionModified(
-                int start, int end, @NonNull TextSelection selection) {
-            final boolean smartSelection = getSourceClassifier(selection.getId())
-                    .equals(TextClassifier.DEFAULT_LOG_TAG);
-            final int eventType;
-            if (smartSelection) {
-                eventType = end - start > 1
-                        ? EventType.SMART_SELECTION_MULTI
-                        : EventType.SMART_SELECTION_SINGLE;
-
-            } else {
-                eventType = EventType.AUTO_SELECTION;
-            }
-            final String entityType = selection.getEntityCount() > 0
-                    ? selection.getEntity(0)
-                    : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = getVersionInfo(selection.getId());
-            return new SelectionEvent(start, end, eventType, entityType, versionTag);
-        }
-
-        /**
-         * Creates an event specifying an action taken on a selection.
-         * Use when the user clicks on an action to act on the selected text.
-         *
-         * @param start  the start word (inclusive) index of the selection
-         * @param end  the end word (exclusive) index of the selection
-         * @param actionType  the action that was performed on the selection
-         */
-        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
-                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
-        public static SelectionEvent selectionAction(
-                int start, int end, @ActionType int actionType) {
-            return new SelectionEvent(
-                    start, end, actionType, TextClassifier.TYPE_UNKNOWN, NO_VERSION_TAG);
-        }
-
-        /**
-         * Creates an event specifying an action taken on a selection.
-         * Use when the user clicks on an action to act on the selected text and the selection's
-         * entity type is known.
-         *
-         * @param start  the start word (inclusive) index of the selection
-         * @param end  the end word (exclusive) index of the selection
-         * @param actionType  the action that was performed on the selection
-         * @param classification  the TextClassification object returned by the TextClassifier that
-         *      classified the selected text
-         */
-        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
-                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
-        public static SelectionEvent selectionAction(
-                int start, int end, @ActionType int actionType,
-                @NonNull TextClassification classification) {
-            final String entityType = classification.getEntityCount() > 0
-                    ? classification.getEntity(0)
-                    : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = getVersionInfo(classification.getId());
-            return new SelectionEvent(start, end, actionType, entityType, versionTag);
-        }
-
-        @VisibleForTesting
-        public static String getVersionInfo(String signature) {
-            final int start = signature.indexOf("|") + 1;
-            final int end = signature.indexOf("|", start);
-            if (start >= 1 && end >= start) {
-                return signature.substring(start, end);
-            }
-            return "";
-        }
-
-        private static String getSourceClassifier(String signature) {
-            final int end = signature.indexOf("|");
-            if (end >= 0) {
-                return signature.substring(0, end);
-            }
-            return "";
-        }
-
-        private boolean isTerminal() {
-            switch (mEventType) {
-                case ActionType.OVERTYPE:  // fall through
-                case ActionType.COPY:  // fall through
-                case ActionType.PASTE:  // fall through
-                case ActionType.CUT:  // fall through
-                case ActionType.SHARE:  // fall through
-                case ActionType.SMART_SHARE:  // fall through
-                case ActionType.DRAG:  // fall through
-                case ActionType.ABANDON:  // fall through
-                case ActionType.OTHER:  // fall through
-                    return true;
-                default:
-                    return false;
-            }
-        }
-    }
-}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 816612f..16d9ed3 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4589,7 +4589,7 @@
         private float mTouchOffsetY;
         // Where the touch position should be on the handle to ensure a maximum cursor visibility.
         // This is the distance in pixels from the top of the handle view.
-        private float mIdealVerticalOffset;
+        private final float mIdealVerticalOffset;
         // Parent's (TextView) previous position in window
         private int mLastParentX, mLastParentY;
         // Parent's (TextView) previous position on screen
@@ -4638,8 +4638,18 @@
 
             final int handleHeight = getPreferredHeight();
             mTouchOffsetY = -0.3f * handleHeight;
-            mIdealVerticalOffset = 0.7f * handleHeight;
-            mIdealFingerToCursorOffset = (int)(mIdealVerticalOffset - mTouchOffsetY);
+            final int distance = AppGlobals.getIntCoreSetting(
+                    WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE,
+                    WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT);
+            if (distance < 0 || distance > 100) {
+                mIdealVerticalOffset = 0.7f * handleHeight;
+                mIdealFingerToCursorOffset = (int)(mIdealVerticalOffset - mTouchOffsetY);
+            } else {
+                mIdealFingerToCursorOffset = (int) TypedValue.applyDimension(
+                        TypedValue.COMPLEX_UNIT_DIP, distance,
+                        mTextView.getContext().getResources().getDisplayMetrics());
+                mIdealVerticalOffset = mIdealFingerToCursorOffset + mTouchOffsetY;
+            }
         }
 
         public float getIdealVerticalOffset() {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 01a0e9b..7f6c0d2 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -2818,7 +2818,7 @@
 
     /**
      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
-     * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
+     * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
      * this method should be used to set a single PendingIntent template on the collection, and
      * individual items can differentiate their on-click behavior using
      * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}.
@@ -2834,7 +2834,7 @@
 
     /**
      * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is very
-     * costly to set PendingIntents on the individual items, and is hence not permitted. Instead
+     * costly to set PendingIntents on the individual items, and is hence not recommended. Instead
      * a single PendingIntent template can be set on the collection, see {@link
      * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
      * action of a given item can be distinguished by setting a fillInIntent on that item. The
@@ -3960,7 +3960,7 @@
 
         /**
          * When using collections (eg. {@link ListView}, {@link StackView} etc.) in widgets, it is
-         * very costly to set PendingIntents on the individual items, and is hence not permitted.
+         * very costly to set PendingIntents on the individual items, and is hence not recommended.
          * Instead a single PendingIntent template can be set on the collection, see {@link
          * RemoteViews#setPendingIntentTemplate(int, PendingIntent)}, and the individual on-click
          * action of a given item can be distinguished by setting a fillInIntent on that item. The
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 4ef3f61..45943f5 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -39,7 +39,6 @@
 import android.view.textclassifier.ExtrasUtils;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
-import android.view.textclassifier.SelectionSessionLogger;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationConstants;
 import android.view.textclassifier.TextClassificationContext;
@@ -705,7 +704,7 @@
         SelectionMetricsLogger(TextView textView) {
             Objects.requireNonNull(textView);
             mEditTextLogger = textView.isTextEditable();
-            mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
+            mTokenIterator = BreakIterator.getWordInstance(textView.getTextLocale());
         }
 
         public void logSelectionStarted(
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
index bce5497..09ab5aa 100644
--- a/core/java/android/widget/WidgetFlags.java
+++ b/core/java/android/widget/WidgetFlags.java
@@ -41,6 +41,25 @@
     public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true;
 
     /**
+     * The flag of finger-to-cursor distance in DP for cursor dragging.
+     * The value unit is DP and the range is {0..100}. If the value is out of range, the legacy
+     * value, which is based on handle size, will be used.
+     */
+    public static final String FINGER_TO_CURSOR_DISTANCE =
+            "CursorControlFeature__finger_to_cursor_distance";
+
+    /**
+     * The key used in app core settings for the flag {@link #FINGER_TO_CURSOR_DISTANCE}.
+     */
+    public static final String KEY_FINGER_TO_CURSOR_DISTANCE =
+            "widget__finger_to_cursor_distance";
+
+    /**
+     * Default value for the flag {@link #FINGER_TO_CURSOR_DISTANCE}.
+     */
+    public static final int FINGER_TO_CURSOR_DISTANCE_DEFAULT = -1;
+
+    /**
      * Whether additional gestures should be enabled for the insertion cursor handle (e.g.
      * long-press or double-tap on the handle to trigger selection).
      */
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index fa567f2..ec2653f 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.pm.IPackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.AsyncTask;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.stats.devicepolicy.DevicePolicyEnums;
@@ -90,7 +91,9 @@
 
             @Override
             public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
-                userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+                AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+                    userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+                });
                 mIsWaitingToEnableWorkProfile = true;
             }
         };
@@ -284,7 +287,7 @@
     }
 
     private int userHandleToPageIndex(UserHandle userHandle) {
-        if (userHandle == getPersonalListAdapter().mResolverListController.getUserHandle()) {
+        if (userHandle.equals(getPersonalListAdapter().mResolverListController.getUserHandle())) {
             return PROFILE_PERSONAL;
         } else {
             return PROFILE_WORK;
@@ -293,7 +296,7 @@
 
     private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
         UserHandle listUserHandle = activeListAdapter.getUserHandle();
-        if (listUserHandle == mWorkProfileUserHandle
+        if (listUserHandle.equals(mWorkProfileUserHandle)
                 && mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) {
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.RESOLVER_EMPTY_STATE_WORK_APPS_DISABLED)
@@ -311,7 +314,7 @@
         if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
             if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
                     UserHandle.myUserId(), listUserHandle.getIdentifier())) {
-                if (listUserHandle == mPersonalProfileUserHandle) {
+                if (listUserHandle.equals(mPersonalProfileUserHandle)) {
                     DevicePolicyEventLogger.createEvent(
                                 DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL)
                             .setStrings(getMetricsCategory())
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 188041e..78a0ae0 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1536,10 +1536,12 @@
                 labels.add(innerInfo.getResolveInfo().loadLabel(getPackageManager()));
             }
             f = new ResolverTargetActionsDialogFragment(mti.getDisplayLabel(), name,
-                    mti.getTargets(), labels);
+                    mti.getTargets(), labels,
+                    mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
         } else {
             f = new ResolverTargetActionsDialogFragment(
-                    ti.getResolveInfo().loadLabel(getPackageManager()), name, pinned);
+                    ti.getResolveInfo().loadLabel(getPackageManager()), name, pinned,
+                    mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
         }
 
         f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
@@ -2140,7 +2142,7 @@
             return null;
         }
 
-        if (getPersonalProfileUserHandle() == userHandle) {
+        if (getPersonalProfileUserHandle().equals(userHandle)) {
             if (mPersonalAppPredictor != null) {
                 return mPersonalAppPredictor;
             }
@@ -2166,7 +2168,7 @@
                         .getSystemService(AppPredictionManager.class);
         AppPredictor appPredictionSession = appPredictionManager.createAppPredictionSession(
                 appPredictionContext);
-        if (getPersonalProfileUserHandle() == userHandle) {
+        if (getPersonalProfileUserHandle().equals(userHandle)) {
             mPersonalAppPredictor = appPredictionSession;
         } else {
             mWorkAppPredictor = appPredictionSession;
@@ -2566,7 +2568,7 @@
 
         ChooserListAdapter chooserListAdapter = (ChooserListAdapter) listAdapter;
         if (chooserListAdapter.getUserHandle()
-                == mChooserMultiProfilePagerAdapter.getCurrentUserHandle()) {
+                .equals(mChooserMultiProfilePagerAdapter.getCurrentUserHandle())) {
             mChooserMultiProfilePagerAdapter.getActiveAdapterView()
                     .setAdapter(mChooserMultiProfilePagerAdapter.getCurrentRootAdapter());
             mChooserMultiProfilePagerAdapter
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
index 2167b1e..c6a00f3 100644
--- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -93,10 +93,10 @@
     @Override
     @Nullable
     ChooserListAdapter getListAdapterForUserHandle(UserHandle userHandle) {
-        if (getActiveListAdapter().getUserHandle() == userHandle) {
+        if (getActiveListAdapter().getUserHandle().equals(userHandle)) {
             return getActiveListAdapter();
         } else if (getInactiveListAdapter() != null
-                && getInactiveListAdapter().getUserHandle() == userHandle) {
+                && getInactiveListAdapter().getUserHandle().equals(userHandle)) {
             return getInactiveListAdapter();
         }
         return null;
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 7a0afa2..9bdfa4a 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.util.Slog;
 import android.widget.Toast;
 
@@ -153,6 +154,9 @@
     }
 
     private boolean shouldShowDisclosure(@Nullable ResolveInfo ri, Intent intent) {
+        if (!isDeviceProvisioned()) {
+            return false;
+        }
         if (ri == null || ri.activityInfo == null) {
             return true;
         }
@@ -163,6 +167,11 @@
         return !isTargetResolverOrChooserActivity(ri.activityInfo);
     }
 
+    private boolean isDeviceProvisioned() {
+        return Settings.Global.getInt(getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, /* def= */ 0) != 0;
+    }
+
     private boolean isTextMessageIntent(Intent intent) {
         return (Intent.ACTION_SENDTO.equals(intent.getAction()) || isViewActionIntent(intent))
                 && ALLOWED_TEXT_MESSAGE_SCHEMES.contains(intent.getScheme());
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ec371d9..8e64b97 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -49,6 +49,7 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Insets;
 import android.net.Uri;
 import android.os.Build;
@@ -65,6 +66,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -1242,12 +1244,12 @@
     }
 
     private void maybeLogCrossProfileTargetLaunch(TargetInfo cti, UserHandle currentUserHandle) {
-        if (!hasWorkProfile() || currentUserHandle == getUser()) {
+        if (!hasWorkProfile() || currentUserHandle.equals(getUser())) {
             return;
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.RESOLVER_CROSS_PROFILE_TARGET_OPENED)
-                .setBoolean(currentUserHandle == getPersonalProfileUserHandle())
+                .setBoolean(currentUserHandle.equals(getPersonalProfileUserHandle()))
                 .setStrings(getMetricsCategory(),
                         cti instanceof ChooserTargetInfo ? "direct_share" : "other_target")
                 .write();
@@ -1303,7 +1305,7 @@
         Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
-        startActivity(in);
+        startActivityAsUser(in, mMultiProfilePagerAdapter.getCurrentUserHandle());
     }
 
     @VisibleForTesting
@@ -1486,7 +1488,8 @@
 
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET)
-                .setBoolean(activeListAdapter.getUserHandle() == getPersonalProfileUserHandle())
+                .setBoolean(activeListAdapter.getUserHandle()
+                        .equals(getPersonalProfileUserHandle()))
                 .setStrings(getMetricsCategory())
                 .write();
         safelyStartActivity(activeProfileTarget);
@@ -1605,7 +1608,10 @@
         for (int i = 0; i < tabWidget.getChildCount(); i++) {
             View tabView = tabWidget.getChildAt(i);
             TextView title = tabView.findViewById(android.R.id.title);
-            title.setTextColor(getColor(R.color.resolver_tabs_inactive_color));
+            title.setTextAppearance(android.R.style.TextAppearance_DeviceDefault_DialogWindowTitle);
+            title.setTextColor(getAttrColor(this, android.R.attr.textColorTertiary));
+            title.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+                    getResources().getDimension(R.dimen.resolver_tab_text_size));
             if (title.getText().equals(getString(R.string.resolver_personal_tab))) {
                 tabView.setContentDescription(personalContentDescription);
             } else if (title.getText().equals(getString(R.string.resolver_work_tab))) {
@@ -1614,10 +1620,17 @@
         }
     }
 
+    private static int getAttrColor(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+        int colorAccent = ta.getColor(0, 0);
+        ta.recycle();
+        return colorAccent;
+    }
+
     private void updateActiveTabStyle(TabHost tabHost) {
         TextView title = tabHost.getTabWidget().getChildAt(tabHost.getCurrentTab())
                 .findViewById(android.R.id.title);
-        title.setTextColor(getColor(R.color.resolver_tabs_active_color));
+        title.setTextColor(getAttrColor(this, android.R.attr.colorAccent));
     }
 
     private void setupViewVisibilities() {
@@ -1778,7 +1791,7 @@
     @Override // ResolverListCommunicator
     public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
         if (listAdapter == mMultiProfilePagerAdapter.getActiveListAdapter()) {
-            if (listAdapter.getUserHandle() == getWorkProfileUserHandle()
+            if (listAdapter.getUserHandle().equals(getWorkProfileUserHandle())
                     && mMultiProfilePagerAdapter.isWaitingToEnableWorkProfile()) {
                 // We have just turned on the work profile and entered the pass code to start it,
                 // now we are waiting to receive the ACTION_USER_UNLOCKED broadcast. There is no
@@ -1819,7 +1832,7 @@
                     mMultiProfilePagerAdapter.markWorkProfileEnabledBroadcastReceived();
                 }
                 if (mMultiProfilePagerAdapter.getCurrentUserHandle()
-                        == getWorkProfileUserHandle()) {
+                        .equals(getWorkProfileUserHandle())) {
                     mMultiProfilePagerAdapter.rebuildActiveTab(true);
                 } else {
                     mMultiProfilePagerAdapter.clearInactiveProfileCache();
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 0440f5e..578f6ae 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -103,10 +103,10 @@
     @Override
     @Nullable
     ResolverListAdapter getListAdapterForUserHandle(UserHandle userHandle) {
-        if (getActiveListAdapter().getUserHandle() == userHandle) {
+        if (getActiveListAdapter().getUserHandle().equals(userHandle)) {
             return getActiveListAdapter();
         } else if (getInactiveListAdapter() != null
-                && getInactiveListAdapter().getUserHandle() == userHandle) {
+                && getInactiveListAdapter().getUserHandle().equals(userHandle)) {
             return getInactiveListAdapter();
         }
         return null;
diff --git a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
index 21efc78..35d9bcd 100644
--- a/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ResolverTargetActionsDialogFragment.java
@@ -27,6 +27,7 @@
 import android.content.res.Configuration;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.provider.Settings;
 
 import com.android.internal.R;
@@ -43,6 +44,7 @@
     private static final String NAME_KEY = "componentName";
     private static final String TITLE_KEY = "title";
     private static final String PINNED_KEY = "pinned";
+    private static final String USER_ID_KEY = "userId";
 
     // Sync with R.array.resolver_target_actions_* resources
     private static final int TOGGLE_PIN_INDEX = 0;
@@ -56,19 +58,21 @@
     }
 
     public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name,
-            boolean pinned) {
+            boolean pinned, UserHandle userHandle) {
         Bundle args = new Bundle();
         args.putCharSequence(TITLE_KEY, title);
         args.putParcelable(NAME_KEY, name);
         args.putBoolean(PINNED_KEY, pinned);
+        args.putParcelable(USER_ID_KEY, userHandle);
         setArguments(args);
     }
 
     public ResolverTargetActionsDialogFragment(CharSequence title, ComponentName name,
-            List<DisplayResolveInfo> targets, List<CharSequence> labels) {
+            List<DisplayResolveInfo> targets, List<CharSequence> labels, UserHandle userHandle) {
         Bundle args = new Bundle();
         args.putCharSequence(TITLE_KEY, title);
         args.putParcelable(NAME_KEY, name);
+        args.putParcelable(USER_ID_KEY, userHandle);
         mTargetInfos = targets;
         mLabels = labels;
         setArguments(args);
@@ -122,7 +126,8 @@
             Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                     .setData(Uri.fromParts("package", name.getPackageName(), null))
                     .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
-            startActivity(in);
+            UserHandle userHandle = args.getParcelable(USER_ID_KEY);
+            getActivity().startActivityAsUser(in, userHandle);
         }
         dismiss();
     }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 633aa2c..ff03f1a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -205,6 +205,9 @@
     /** List of packages with the same uid, and its app data info: volume uuid and inode. */
     public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
 
+    /** Bind mount app storage dirs to lower fs not via fuse */
+    public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
+
     /**
      * An extraArg passed when a zygote process is forking a child-zygote, specifying a name
      * in the abstract socket namespace. This socket name is what the new child zygote
@@ -310,6 +313,7 @@
      * @param isTopApp true if the process is for top (high priority) application.
      * @param pkgDataInfoList A list that stores related packages and its app data
      * info: volume uuid and inode.
+     * @param bindMountAppStorageDirs  True if the zygote needs to mount storage dirs.
      *
      * @return 0 if this is the child, pid of the child
      * if this is the parent, or -1 on error.
@@ -317,13 +321,13 @@
     static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
             int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
-            boolean isTopApp, String[] pkgDataInfoList) {
+            boolean isTopApp, String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
         ZygoteHooks.preFork();
 
         int pid = nativeForkAndSpecialize(
                 uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                 fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
-                pkgDataInfoList);
+                pkgDataInfoList, bindMountAppStorageDirs);
         if (pid == 0) {
             // Note that this event ends at the end of handleChildProc,
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -339,7 +343,8 @@
     private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
             int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
             int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
-            String appDataDir, boolean isTopApp, String[] pkgDataInfoList);
+            String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
+            boolean bindMountAppStorageDirs);
 
     /**
      * Specialize an unspecialized app process.  The current VM must have been started
@@ -366,14 +371,15 @@
      * volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
      * app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
      * app_b_ce_inode, ...];
+     * @param bindMountAppStorageDirs  True if the zygote needs to mount storage dirs.
      */
     private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
-            String[] pkgDataInfoList) {
+            String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
         nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
                 niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
-                pkgDataInfoList);
+                pkgDataInfoList, bindMountAppStorageDirs);
 
         // Note that this event ends at the end of handleChildProc.
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -393,7 +399,7 @@
     private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
             int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
-            String[] pkgDataInfoList);
+            String[] pkgDataInfoList, boolean bindMountAppStorageDirs);
 
     /**
      * Called to do any initialization before starting an application.
@@ -718,7 +724,7 @@
                                  args.mRuntimeFlags, rlimits, args.mMountExternal,
                                  args.mSeInfo, args.mNiceName, args.mStartChildZygote,
                                  args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
-                                 args.mPkgDataInfoList);
+                                 args.mPkgDataInfoList, args.mBindMountAppStorageDirs);
 
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 37f570b..1a63765 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -227,6 +227,11 @@
     String[] mPkgDataInfoList;
 
     /**
+     * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
+     */
+    boolean mBindMountAppStorageDirs;
+
+    /**
      * Constructs instance and parses args
      *
      * @param args zygote command-line args
@@ -447,6 +452,8 @@
                 }
             } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
                 mPkgDataInfoList = getAssignmentList(arg);
+            } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
+                mBindMountAppStorageDirs = true;
             } else {
                 break;
             }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4949811..bc8dfd4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -258,7 +258,7 @@
                 parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                 parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                 parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
-                parsedArgs.mPkgDataInfoList);
+                parsedArgs.mPkgDataInfoList, parsedArgs.mBindMountAppStorageDirs);
 
         try {
             if (pid == 0) {
diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
new file mode 100644
index 0000000..ebfea450
--- /dev/null
+++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
@@ -0,0 +1,78 @@
+/*
+ * 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.policy;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+/**
+ * @hide
+ */
+public class GestureNavigationSettingsObserver extends ContentObserver {
+    private Context mContext;
+    private Runnable mOnChangeRunnable;
+
+    public GestureNavigationSettingsObserver(Handler handler, Context context,
+            Runnable onChangeRunnable) {
+        super(handler);
+        mContext = context;
+        mOnChangeRunnable = onChangeRunnable;
+    }
+
+    public void register() {
+        ContentResolver r = mContext.getContentResolver();
+        r.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT),
+                false, this, UserHandle.USER_ALL);
+        r.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
+                false, this, UserHandle.USER_ALL);
+    }
+
+    public void unregister() {
+        mContext.getContentResolver().unregisterContentObserver(this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange) {
+        super.onChange(selfChange);
+        if (mOnChangeRunnable != null) {
+            mOnChangeRunnable.run();
+        }
+    }
+
+    public int getLeftSensitivity(Resources userRes) {
+        return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT);
+    }
+
+    public int getRightSensitivity(Resources userRes) {
+        return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT);
+    }
+
+    private int getSensitivity(Resources userRes, String side) {
+        final int inset = userRes.getDimensionPixelSize(
+                com.android.internal.R.dimen.config_backGestureInset);
+        final float scale = Settings.Secure.getFloatForUser(
+                mContext.getContentResolver(), side, 1.0f, UserHandle.USER_CURRENT);
+        return (int) (inset * scale);
+    }
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 8446bbd..e4a4408 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -200,8 +200,9 @@
         try {
             return doInvoke();
         } finally {
-            if (isRecycleOnUse()) doRecycle();
-            if (!isRecycled()) {
+            if (isRecycleOnUse()) {
+                doRecycle();
+            } else if (!isRecycled()) {
                 int argsSize = ArrayUtils.size(mArgs);
                 for (int i = 0; i < argsSize; i++) {
                     popArg(i);
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 07be113..73da600 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -122,6 +122,11 @@
     private CharSequence mFallbackChatName;
     private CharSequence mFallbackGroupChatName;
     private CharSequence mConversationTitle;
+    private int mNotificationHeaderExpandedPadding;
+    private View mConversationHeader;
+    private View mContentContainer;
+    private boolean mExpandable = true;
+    private int mContentMarginEnd;
 
     public ConversationLayout(@NonNull Context context) {
         super(context);
@@ -166,12 +171,18 @@
         });
         mConversationText = findViewById(R.id.conversation_text);
         mExpandButtonContainer = findViewById(R.id.expand_button_container);
+        mConversationHeader = findViewById(R.id.conversation_header);
+        mContentContainer = findViewById(R.id.notification_action_list_margin_target);
         mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
         mExpandButton = findViewById(R.id.expand_button);
         mExpandButtonExpandedTopMargin = getResources().getDimensionPixelSize(
                 R.dimen.conversation_expand_button_top_margin_expanded);
         mExpandButtonExpandedSize = getResources().getDimensionPixelSize(
                 R.dimen.conversation_expand_button_expanded_size);
+        mNotificationHeaderExpandedPadding = getResources().getDimensionPixelSize(
+                R.dimen.conversation_header_expanded_padding_end);
+        mContentMarginEnd = getResources().getDimensionPixelSize(
+                R.dimen.notification_content_margin_end);
         mBadgedSideMargins = getResources().getDimensionPixelSize(
                 R.dimen.conversation_badge_side_margin);
         mIconSizeBadged = getResources().getDimensionPixelSize(
@@ -207,6 +218,7 @@
         mIsCollapsed = isCollapsed;
         mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE);
         updateExpandButton();
+        updateContentPaddings();
     }
 
     @RemotableViewMethod
@@ -652,6 +664,7 @@
                 mAddedGroups.add(newGroup);
             }
             newGroup.setDisplayImagesAtEnd(mIsCollapsed);
+            newGroup.setIsInConversation(true);
             newGroup.setLayoutColor(mLayoutColor);
             newGroup.setTextColors(mSenderTextColor, mMessageTextColor);
             Person sender = senders.get(groupIndex);
@@ -851,10 +864,39 @@
         mExpandButton.setLayoutParams(layoutParams);
 
         mExpandButtonContainer.setContentDescription(mContext.getText(contentDescriptionId));
+    }
 
+    private void updateContentPaddings() {
+
+        // Let's make sure the conversation header can't run into the expand button when we're
+        // collapsed and update the paddings of the content
+        int headerPaddingEnd;
+        int contentPaddingEnd;
+        if (!mExpandable) {
+            headerPaddingEnd = 0;
+            contentPaddingEnd = mContentMarginEnd;
+        } else if (mIsCollapsed) {
+            headerPaddingEnd = 0;
+            contentPaddingEnd = 0;
+        } else {
+            headerPaddingEnd = mNotificationHeaderExpandedPadding;
+            contentPaddingEnd = mContentMarginEnd;
+        }
+        mConversationHeader.setPaddingRelative(
+                mConversationHeader.getPaddingStart(),
+                mConversationHeader.getPaddingTop(),
+                headerPaddingEnd,
+                mConversationHeader.getPaddingBottom());
+
+        mContentContainer.setPaddingRelative(
+                mContentContainer.getPaddingStart(),
+                mContentContainer.getPaddingTop(),
+                contentPaddingEnd,
+                mContentContainer.getPaddingBottom());
     }
 
     public void updateExpandability(boolean expandable, @Nullable OnClickListener onClickListener) {
+        mExpandable = expandable;
         if (expandable) {
             mExpandButtonContainer.setVisibility(VISIBLE);
             mExpandButtonContainer.setOnClickListener(onClickListener);
@@ -862,5 +904,6 @@
             // TODO: handle content paddings to end of layout
             mExpandButtonContainer.setVisibility(GONE);
         }
+        updateContentPaddings();
     }
 }
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 9977903..c68da97 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -85,6 +85,10 @@
     private int mSenderTextPaddingSingleLine;
     private boolean mIsFirstGroupInLayout = true;
     private boolean mCanHideSenderIfFirst;
+    private boolean mIsInConversation = true;
+    private ViewGroup mMessagingIconContainer;
+    private int mConversationContentStart;
+    private int mNonConversationMarginEnd;
 
     public MessagingGroup(@NonNull Context context) {
         super(context);
@@ -112,6 +116,7 @@
         mAvatarView = findViewById(R.id.message_icon);
         mImageContainer = findViewById(R.id.messaging_group_icon_container);
         mSendingSpinner = findViewById(R.id.messaging_group_sending_progress);
+        mMessagingIconContainer = findViewById(R.id.message_icon_container);
         mContentContainer = findViewById(R.id.messaging_group_content_container);
         mSendingSpinnerContainer = findViewById(R.id.messaging_group_sending_progress_container);
         DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
@@ -119,6 +124,10 @@
         mDisplaySize.y = displayMetrics.heightPixels;
         mSenderTextPaddingSingleLine = getResources().getDimensionPixelSize(
                 R.dimen.messaging_group_singleline_sender_padding_end);
+        mConversationContentStart = getResources().getDimensionPixelSize(
+                R.dimen.conversation_content_start);
+        mNonConversationMarginEnd = getResources().getDimensionPixelSize(
+                R.dimen.messaging_layout_margin_end);
     }
 
     public void updateClipRect() {
@@ -644,4 +653,21 @@
     public boolean isSingleLine() {
         return mSingleLine;
     }
+
+    /**
+     * Set this group to be displayed in a conversation and adjust the visual appearance
+     *
+     * @param isInConversation is this in a conversation
+     */
+    public void setIsInConversation(boolean isInConversation) {
+        if (mIsInConversation != isInConversation) {
+            mIsInConversation = isInConversation;
+            MarginLayoutParams layoutParams =
+                    (MarginLayoutParams) mMessagingIconContainer.getLayoutParams();
+            layoutParams.width = mIsInConversation ? mConversationContentStart
+                    : ViewPager.LayoutParams.WRAP_CONTENT;
+            layoutParams.setMarginEnd(mIsInConversation ? 0 : mNonConversationMarginEnd);
+            mMessagingIconContainer.setLayoutParams(layoutParams);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 503f3f1..3fb5d43 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -448,6 +448,7 @@
                 mAddedGroups.add(newGroup);
             }
             newGroup.setDisplayImagesAtEnd(mDisplayImagesAtEnd);
+            newGroup.setIsInConversation(false);
             newGroup.setLayoutColor(mLayoutColor);
             newGroup.setTextColors(mSenderTextColor, mMessageTextColor);
             Person sender = senders.get(groupIndex);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 0eb364d..b32b4ae 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -346,6 +346,22 @@
     }
 }
 
+void android_os_Process_enableFreezer(
+        JNIEnv *env, jobject clazz, jboolean enable)
+{
+    bool success = true;
+
+    if (enable) {
+        success = SetTaskProfiles(0, {"FreezerFrozen"}, true);
+    } else {
+        success = SetTaskProfiles(0, {"FreezerThawed"}, true);
+    }
+
+    if (!success) {
+        jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
+    }
+}
+
 jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid)
 {
     SchedPolicy sp;
@@ -1344,6 +1360,7 @@
         {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
         {"sendSignalQuiet", "(II)V", (void*)android_os_Process_sendSignalQuiet},
         {"setProcessFrozen", "(IIZ)V", (void*)android_os_Process_setProcessFrozen},
+        {"enableFreezer", "(Z)V", (void*)android_os_Process_enableFreezer},
         {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
         {"getTotalMemory", "()J", (void*)android_os_Process_getTotalMemory},
         {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index dafb377..ea3c0fa 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1567,23 +1567,6 @@
   auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
   const userid_t user_id = multiuser_get_user_id(uid);
 
-  // If FUSE is not ready for this user, skip it
-  // TODO(148772775): Pass primary volume name from zygote argument to here
-  std::string tmp = GetProperty("vold.fuse_running_users", "");
-  std::istringstream fuse_running_users(tmp);
-  bool user_found = false;
-  std::string s;
-  std::string user_id_str = std::to_string(user_id);
-  while (!user_found && std::getline(fuse_running_users, s, ',')) {
-    if (user_id_str == s) {
-      user_found = true;
-    }
-  }
-  if (!user_found) {
-    ALOGI("User %d is not running fuse yet, fuse_running_users=%s", user_id, tmp.c_str());
-    return;
-  }
-
   // Fuse is ready, so we can start using fuse path.
   int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
 
@@ -1611,7 +1594,7 @@
                              jstring managed_nice_name, bool is_system_server,
                              bool is_child_zygote, jstring managed_instruction_set,
                              jstring managed_app_data_dir, bool is_top_app,
-                             jobjectArray pkg_data_info_list) {
+                             jobjectArray pkg_data_info_list, bool mount_storage_dirs) {
   const char* process_name = is_system_server ? "system_server" : "zygote";
   auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
   auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1650,10 +1633,7 @@
     isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
     isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
   }
-
-  if ((mount_external != MOUNT_EXTERNAL_INSTALLER) &&
-        GetBoolProperty(kPropFuse, false) &&
-        GetBoolProperty(ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+  if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) {
     BindMountStorageDirs(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
   }
 
@@ -2023,7 +2003,7 @@
         jint mount_external, jstring se_info, jstring nice_name,
         jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
         jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
-        jobjectArray pkg_data_info_list) {
+        jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
     jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
     if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -2060,7 +2040,8 @@
                        capabilities, capabilities,
                        mount_external, se_info, nice_name, false,
                        is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
-                       is_top_app == JNI_TRUE, pkg_data_info_list);
+                       is_top_app == JNI_TRUE, pkg_data_info_list,
+                       mount_storage_dirs == JNI_TRUE);
     }
     return pid;
 }
@@ -2095,7 +2076,7 @@
                        permitted_capabilities, effective_capabilities,
                        MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
                        false, nullptr, nullptr, /* is_top_app= */ false,
-                       /* pkg_data_info_list */ nullptr);
+                       /* pkg_data_info_list */ nullptr, false);
   } else if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
@@ -2225,14 +2206,15 @@
     jint runtime_flags, jobjectArray rlimits,
     jint mount_external, jstring se_info, jstring nice_name,
     jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
-    jobjectArray pkg_data_info_list) {
+    jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
   jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
   SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
                    capabilities, capabilities,
                    mount_external, se_info, nice_name, false,
                    is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
-                   is_top_app == JNI_TRUE, pkg_data_info_list);
+                   is_top_app == JNI_TRUE, pkg_data_info_list,
+                   mount_storage_dirs == JNI_TRUE);
 }
 
 /**
@@ -2426,7 +2408,7 @@
 static const JNINativeMethod gMethods[] = {
         {"nativeForkAndSpecialize",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
-         "String;Z[Ljava/lang/String;)I",
+         "String;Z[Ljava/lang/String;Z)I",
          (void*)com_android_internal_os_Zygote_nativeForkAndSpecialize},
         {"nativeForkSystemServer", "(II[II[[IJJ)I",
          (void*)com_android_internal_os_Zygote_nativeForkSystemServer},
@@ -2439,7 +2421,7 @@
         {"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
         {"nativeSpecializeAppProcess",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
-         "String;Z[Ljava/lang/String;)V",
+         "String;Z[Ljava/lang/String;Z)V",
          (void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},
         {"nativeInitNativeState", "(Z)V",
          (void*)com_android_internal_os_Zygote_nativeInitNativeState},
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index d6687f0..64cf75d 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -376,8 +376,7 @@
     optional bytes system_trace = 3026 [
         (section).type = SECTION_FILE,
         (section).args = "/data/misc/perfetto-traces/incident-trace",
-        (privacy).dest = DEST_AUTOMATIC,
-        (section).userdebug_and_eng_only = true
+        (privacy).dest = DEST_AUTOMATIC
     ];
 
     // Dropbox entries split by tags.
diff --git a/core/proto/android/util/quotatracker.proto b/core/proto/android/util/quotatracker.proto
index 5d022ed..d98e5ee 100644
--- a/core/proto/android/util/quotatracker.proto
+++ b/core/proto/android/util/quotatracker.proto
@@ -131,93 +131,3 @@
 
   // Next tag: 4
 }
-
-// A com.android.util.quota.DurationQuotaTracker object.
-message DurationQuotaTrackerProto {
-  option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-  optional QuotaTrackerProto base_quota_data = 1;
-
-  message DurationLimit {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional CategoryProto category = 1;
-    optional int64 limit_ms = 2;
-    optional int64 window_size_ms = 3;
-  }
-  repeated DurationLimit duration_limit = 2;
-
-  message ExecutionStats {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    // The time after which this record should be considered invalid (out of date), in the
-    // elapsed realtime timebase.
-    optional int64 expiration_time_elapsed = 1;
-
-    optional int32 window_size_ms = 2;
-    optional int64 duration_limit_ms = 3;
-
-    // The overall session duration in the window.
-    optional int64 session_duration_in_window_ms = 4;
-    // The number of individual long-running events in the window.
-    optional int32 event_count_in_window = 5;
-
-    // The time after which the app will be under the bucket quota. This is only valid if
-    // session_duration_in_window_ms >= duration_limit_ms.
-    optional int64 in_quota_time_elapsed = 6;
-  }
-
-  message Timer {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    // True if the Timer is actively tracking long-running events.
-    optional bool is_active = 1;
-    // The time this timer last became active. Only valid if is_active is true.
-    optional int64 start_time_elapsed = 2;
-    // How many long-running events are currently running. Valid only if is_active is true.
-    optional int32 event_count = 3;
-  }
-
-  message TimingSession {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional int64 start_time_elapsed = 1;
-    optional int64 end_time_elapsed = 2;
-    // How many events started during this session. This only count long-running events, not
-    // instantaneous events.
-    optional int32 event_count = 3;
-  }
-
-  message UptcStats {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional UptcProto uptc = 1;
-
-    // True if the UPTC has been given free quota.
-    optional bool is_quota_free = 2;
-
-    optional Timer timer = 3;
-
-    repeated TimingSession saved_sessions = 4;
-
-    repeated ExecutionStats execution_stats = 5;
-  }
-  repeated UptcStats uptc_stats = 3;
-
-  message ReachedQuotaAlarmListener {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional int64 trigger_time_elapsed = 1;
-
-    message UptcTimes {
-      option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-      optional UptcProto uptc = 1;
-      optional int64 out_of_quota_time_elapsed = 2;
-    }
-    repeated UptcTimes uptc_times = 2;
-  }
-  optional ReachedQuotaAlarmListener reached_quota_alarm_listener = 4;
-
-  // Next tag: 5
-}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7a3ec95..1f45c31 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1689,6 +1689,10 @@
     <permission android:name="android.permission.NETWORK_FACTORY"
                 android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Allows applications to access network stats provider -->
+    <permission android:name="android.permission.NETWORK_STATS_PROVIDER"
+                android:protectionLevel="signature" />
+
     <!-- Allows Settings and SystemUI to call methods in Networking services
          <p>Not for use by third-party or privileged applications.
          @SystemApi @TestApi
diff --git a/core/res/res/drawable/tab_indicator_resolver.xml b/core/res/res/drawable/tab_indicator_resolver.xml
index ff16d81a..f97773e 100644
--- a/core/res/res/drawable/tab_indicator_resolver.xml
+++ b/core/res/res/drawable/tab_indicator_resolver.xml
@@ -25,7 +25,7 @@
     </item>
     <item android:gravity="bottom">
         <shape android:shape="rectangle"
-               android:tint="@color/resolver_tabs_active_color">
+               android:tint="?attr/colorAccent">
             <size android:height="2dp" />
             <solid android:color="@color/tab_indicator_material" />
         </shape>
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index dc52e97..2348dee 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -93,9 +93,10 @@
             android:clipChildren="false"
             android:clipToPadding="false">
             <!--TODO: move this into a separate layout and share logic with the header to bring back app opps etc-->
-            <FrameLayout
+            <com.android.internal.widget.RemeasuringLinearLayout
                 android:id="@+id/notification_action_list_margin_target"
                 android:layout_width="0dp"
+                android:orientation="vertical"
                 android:layout_height="wrap_content"
                 android:layout_weight="1">
 
@@ -163,12 +164,12 @@
                     android:id="@+id/notification_messaging"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_marginTop="40dp"
+                    android:layout_marginTop="2dp"
                     android:spacing="@dimen/notification_messaging_spacing"
                     android:clipToPadding="false"
                     android:clipChildren="false"
                     />
-            </FrameLayout>
+            </com.android.internal.widget.RemeasuringLinearLayout>
             <!-- Unread Count -->
             <!-- <TextView /> -->
 
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index 15146c0..3188861 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -21,8 +21,9 @@
     android:layout_height="wrap_content"
     android:orientation="horizontal" >
     <FrameLayout
+        android:id="@+id/message_icon_container"
         android:layout_width="@dimen/conversation_content_start"
-        android:layout_height="wrap_content">         <!--TODO: make sure to make this padding dynamic-->
+        android:layout_height="wrap_content">
         <ImageView
             android:layout_gravity="top|center_horizontal"
             android:id="@+id/message_icon"
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 619b392..5fdf190 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -38,7 +38,7 @@
         android:layout_height="wrap_content"
         android:fontFamily="@string/config_headlineFontFamilyMedium"
         android:textColor="@color/resolver_empty_state_text"
-        android:textSize="18sp"
+        android:textSize="14sp"
         android:layout_centerHorizontal="true" />
     <TextView
         android:id="@+id/resolver_empty_state_subtitle"
@@ -47,7 +47,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textColor="@color/resolver_empty_state_text"
-        android:textSize="14sp"
+        android:textSize="12sp"
         android:gravity="center_horizontal"
         android:layout_centerHorizontal="true" />
     <Button
@@ -60,7 +60,7 @@
         android:background="@null"
         android:fontFamily="@string/config_headlineFontFamilyMedium"
         android:textSize="14sp"
-        android:textColor="@color/resolver_tabs_active_color"
+        android:textColor="?attr/colorAccent"
         android:layout_centerHorizontal="true" />
     <ProgressBar
         android:id="@+id/resolver_empty_state_progress"
@@ -71,5 +71,5 @@
         android:indeterminate="true"
         android:layout_centerHorizontal="true"
         android:layout_below="@+id/resolver_empty_state_subtitle"
-        android:indeterminateTint="@color/resolver_tabs_active_color"/>
+        android:indeterminateTint="?attr/colorAccent"/>
 </RelativeLayout>
\ No newline at end of file
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 7f77e6c..708b4f3 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -33,7 +33,6 @@
     <color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color>
     <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color>
 
-    <color name="resolver_tabs_active_color">#FF8AB4F8</color>
     <color name="resolver_empty_state_text">#FFFFFF</color>
     <color name="resolver_empty_state_icon">#FFFFFF</color>
 </resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index bdec096..91248f1 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -224,8 +224,6 @@
 
     <!-- Resolver/Chooser -->
     <color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
-    <color name="resolver_tabs_active_color">#FF1A73E8</color>
-    <color name="resolver_tabs_inactive_color">#FF80868B</color>
     <color name="resolver_empty_state_text">#FF202124</color>
     <color name="resolver_empty_state_icon">#FF5F6368</color>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6f468e0..6060d9a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4420,5 +4420,9 @@
     <string name="config_customSessionPolicyProvider"></string>
 
     <!-- The max scale for the wallpaper when it's zoomed in -->
-    <item name="config_wallpaperMaxScale" format="float" type="dimen">1</item>
+    <item name="config_wallpaperMaxScale" format="float" type="dimen">1.15</item>
+
+    <!-- Package name that will receive an explicit manifest broadcast for
+         android.os.action.POWER_SAVE_MODE_CHANGED. -->
+    <string name="config_powerSaveModeChangedListenerPackage" translatable="false"></string>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index e3fe982..4dedc63 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -699,6 +699,12 @@
     <!-- margin on the top when the icon is centered for group conversations -->
     <dimen name="conversation_icon_margin_top_centered">5dp</dimen>
 
+    <!-- The padding of the conversation header when expanded. This is calculated from the expand button size + notification_content_margin_end -->
+    <dimen name="conversation_header_expanded_padding_end">38dp</dimen>
+
+    <!-- margin at the end of messaging group icons when not conversations -->
+    <dimen name="messaging_layout_margin_end">12dp</dimen>
+
     <!-- Padding between text and sender when singleline -->
     <dimen name="messaging_group_singleline_sender_padding_end">4dp</dimen>
 
@@ -794,6 +800,7 @@
     <dimen name="resolver_empty_state_height_with_tabs">268dp</dimen>
     <dimen name="resolver_max_collapsed_height">192dp</dimen>
     <dimen name="resolver_max_collapsed_height_with_tabs">248dp</dimen>
+    <dimen name="resolver_tab_text_size">14sp</dimen>
 
     <dimen name="chooser_action_button_icon_size">18dp</dimen>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7230cc4..cf68aff 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3044,8 +3044,6 @@
       <public name="config_defaultCallScreening" />
       <!-- @hide @SystemApi @TestApi -->
       <public name="config_systemGallery" />
-      <!-- @hide @SystemApi -->
-      <public name="low_memory" />
     </public-group>
 
     <public-group type="bool" first-id="0x01110005">
@@ -3073,12 +3071,6 @@
     <public-group type="array" first-id="0x01070006">
       <!-- @hide @SystemApi -->
       <public name="simColors" />
-      <!-- @hide @SystemApi -->
-      <public name="config_restrictedPreinstalledCarrierApps" />
-      <!-- @hide @SystemApi -->
-      <public name="config_sms_enabled_single_shift_tables" />
-      <!-- @hide @SystemApi -->
-      <public name="config_sms_enabled_locking_shift_tables" />
     </public-group>
 
     <public-group type="string" first-id="0x0104002c">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f101f59..ff49c45 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1169,7 +1169,7 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
     <string name="permlab_cameraOpenCloseListener">Allow an application or service to receive callbacks about camera devices being opened or closed.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
-    <string name="permdesc_cameraOpenCloseListener">This signature app can receive callbacks when any camera device is being opened (by what application package) or closed.</string>
+    <string name="permdesc_cameraOpenCloseListener">This app can receive callbacks when any camera device is being opened (by what application) or closed.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_vibrate">control vibration</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 49a0f17..9c64a70 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3615,6 +3615,7 @@
   <java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
   <java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
   <java-symbol type="string" name="config_batterySaverScheduleProvider" />
+  <java-symbol type="string" name="config_powerSaveModeChangedListenerPackage" />
 
   <!-- For car devices -->
   <java-symbol type="string" name="car_loading_profile" />
@@ -3890,6 +3891,7 @@
   <java-symbol type="id" name="conversation_face_pile_top" />
   <java-symbol type="id" name="conversation_face_pile" />
   <java-symbol type="id" name="conversation_text" />
+  <java-symbol type="id" name="message_icon_container" />
   <java-symbol type="dimen" name="conversation_expand_button_top_margin_expanded" />
   <java-symbol type="dimen" name="conversation_expand_button_expanded_size" />
   <java-symbol type="dimen" name="messaging_group_singleline_sender_padding_end" />
@@ -3897,12 +3899,13 @@
   <java-symbol type="dimen" name="conversation_icon_size_badged" />
   <java-symbol type="dimen" name="conversation_icon_size_centered" />
   <java-symbol type="dimen" name="conversation_icon_margin_top_centered" />
+  <java-symbol type="dimen" name="conversation_content_start" />
+  <java-symbol type="dimen" name="messaging_layout_margin_end" />
+  <java-symbol type="dimen" name="conversation_header_expanded_padding_end" />
   <java-symbol type="layout" name="notification_template_material_conversation" />
   <java-symbol type="layout" name="conversation_face_pile_layout" />
 
   <!-- Intent resolver and share sheet -->
-  <java-symbol type="color" name="resolver_tabs_active_color" />
-  <java-symbol type="color" name="resolver_tabs_inactive_color" />
   <java-symbol type="string" name="resolver_personal_tab" />
   <java-symbol type="string" name="resolver_personal_tab_accessibility" />
   <java-symbol type="string" name="resolver_work_tab" />
@@ -3933,6 +3936,7 @@
   <java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" />
   <java-symbol type="dimen" name="resolver_max_collapsed_height_with_tabs" />
   <java-symbol type="bool" name="sharesheet_show_content_preview" />
+  <java-symbol type="dimen" name="resolver_tab_text_size" />
 
   <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
   <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 5d42915..4b42f4ae 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -268,7 +268,7 @@
         File snd_stat = new File (root_filepath + "tcp_snd");
         int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat);
         NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
-        stats.addEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
+        stats.insertEntry(NetworkStats.IFACE_ALL, uid, NetworkStats.SET_DEFAULT,
                 NetworkStats.TAG_NONE, rx, 0, tx, 0, 0);
         return stats;
     }
diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
index 239f971..3ebe103 100644
--- a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
+++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
@@ -54,7 +54,7 @@
             recycle.txBytes = 150000;
             recycle.txPackets = 1500;
             recycle.operations = 0;
-            mNetworkStats.addEntry(recycle);
+            mNetworkStats.insertEntry(recycle);
             if (recycle.set == 1) {
                 uid++;
             }
@@ -70,7 +70,7 @@
             recycle.txBytes = 180000 * mSize;
             recycle.txPackets = 1200 * mSize;
             recycle.operations = 0;
-            mNetworkStats.addEntry(recycle);
+            mNetworkStats.insertEntry(recycle);
         }
     }
 
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index f1ab8dd..42ab2e7 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -58,12 +58,12 @@
 import android.view.test.InsetsModeSession;
 import android.widget.TextView;
 
-import com.android.server.testutils.OffsettableClock;
-import com.android.server.testutils.TestHandler;
-
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -74,7 +74,6 @@
 import org.mockito.Mockito;
 
 import java.util.concurrent.CountDownLatch;
-import java.util.function.Supplier;
 
 /**
  * Tests for {@link InsetsController}.
@@ -199,7 +198,7 @@
             WindowInsetsAnimationControlListener mockListener =
                     mock(WindowInsetsAnimationControlListener.class);
             mController.controlWindowInsetsAnimation(statusBars(), 10 /* durationMs */,
-                    new LinearInterpolator(), mockListener);
+                    new LinearInterpolator(), new CancellationSignal(), mockListener);
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -219,7 +218,7 @@
         WindowInsetsAnimationControlListener controlListener =
                 mock(WindowInsetsAnimationControlListener.class);
         mController.controlWindowInsetsAnimation(0, 0 /* durationMs */, new LinearInterpolator(),
-                controlListener);
+                new CancellationSignal(), controlListener);
         mController.addOnControllableInsetsChangedListener(
                 (controller, typeMask) -> assertEquals(0, typeMask));
         verify(controlListener).onCancelled();
@@ -498,7 +497,7 @@
             WindowInsetsAnimationControlListener mockListener =
                     mock(WindowInsetsAnimationControlListener.class);
             mController.controlWindowInsetsAnimation(statusBars(), 0 /* durationMs */,
-                    new LinearInterpolator(), mockListener);
+                    new LinearInterpolator(), new CancellationSignal(), mockListener);
 
             ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor =
                     ArgumentCaptor.forClass(WindowInsetsAnimationController.class);
@@ -523,9 +522,10 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener mockListener =
                     mock(WindowInsetsAnimationControlListener.class);
-            CancellationSignal cancellationSignal = mController.controlWindowInsetsAnimation(
+            CancellationSignal cancellationSignal = new CancellationSignal();
+            mController.controlWindowInsetsAnimation(
                     statusBars(), 0 /* durationMs */,
-                    new LinearInterpolator(), mockListener);
+                    new LinearInterpolator(), cancellationSignal, mockListener);
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -548,7 +548,8 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
-            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
+            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+                    null /* cancellationSignal */, listener);
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -572,7 +573,8 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
-            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
+            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+                    null /* cancellationSignal */, listener);
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -592,7 +594,8 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
-            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
+            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+                    null /* cancellationSignal */, listener);
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -613,8 +616,10 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
-            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener)
-                    .cancel();
+            CancellationSignal cancellationSignal = new CancellationSignal();
+            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(),
+                    cancellationSignal, listener);
+            cancellationSignal.cancel();
 
             verify(listener).onCancelled();
 
diff --git a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
index 9797178..33f859e 100644
--- a/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/PendingInsetsControllerTest.java
@@ -20,8 +20,9 @@
 import static android.view.WindowInsets.Type.systemBars;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -35,12 +36,12 @@
 import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
 import android.view.animation.LinearInterpolator;
 
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import androidx.test.runner.AndroidJUnit4;
-
 /**
  * Tests for {@link PendingInsetsControllerTest}.
  *
@@ -95,10 +96,11 @@
     public void testControl() {
         WindowInsetsAnimationControlListener listener =
                 mock(WindowInsetsAnimationControlListener.class);
-        CancellationSignal signal = mPendingInsetsController.controlWindowInsetsAnimation(
-                systemBars(), 0, new LinearInterpolator(), listener);
+        CancellationSignal cancellationSignal = new CancellationSignal();
+        mPendingInsetsController.controlWindowInsetsAnimation(
+                systemBars(), 0, new LinearInterpolator(), cancellationSignal, listener);
         verify(listener).onCancelled();
-        assertTrue(signal.isCanceled());
+        assertFalse(cancellationSignal.isCanceled());
     }
 
     @Test
@@ -106,10 +108,11 @@
         WindowInsetsAnimationControlListener listener =
                 mock(WindowInsetsAnimationControlListener.class);
         mPendingInsetsController.replayAndAttach(mReplayedController);
-        mPendingInsetsController.controlWindowInsetsAnimation(
-                systemBars(), 0L, new LinearInterpolator(), listener);
+        CancellationSignal cancellationSignal = new CancellationSignal();
+        mPendingInsetsController.controlWindowInsetsAnimation(systemBars(), 0L,
+                new LinearInterpolator(), cancellationSignal, listener);
         verify(mReplayedController).controlWindowInsetsAnimation(eq(systemBars()), eq(0L), any(),
-                eq(listener));
+                eq(cancellationSignal), eq(listener));
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java
deleted file mode 100644
index 8744997..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsModelParamsSupplierTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.util.Collections;
-import java.util.Locale;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ActionsModelParamsSupplierTest {
-
-    @Test
-    public void getSerializedPreconditions_validActionsModelParams() {
-        ModelFileManager.ModelFile modelFile = new ModelFileManager.ModelFile(
-                new File("/model/file"),
-                200 /* version */,
-                Collections.singletonList(Locale.forLanguageTag("en")),
-                "en",
-                false);
-        byte[] serializedPreconditions = new byte[]{0x12, 0x24, 0x36};
-        ActionsModelParamsSupplier.ActionsModelParams params =
-                new ActionsModelParamsSupplier.ActionsModelParams(
-                        200 /* version */,
-                        "en",
-                        serializedPreconditions);
-
-        byte[] actual = params.getSerializedPreconditions(modelFile);
-
-        assertThat(actual).isEqualTo(serializedPreconditions);
-    }
-
-    @Test
-    public void getSerializedPreconditions_invalidVersion() {
-        ModelFileManager.ModelFile modelFile = new ModelFileManager.ModelFile(
-                new File("/model/file"),
-                201 /* version */,
-                Collections.singletonList(Locale.forLanguageTag("en")),
-                "en",
-                false);
-        byte[] serializedPreconditions = new byte[]{0x12, 0x24, 0x36};
-        ActionsModelParamsSupplier.ActionsModelParams params =
-                new ActionsModelParamsSupplier.ActionsModelParams(
-                        200 /* version */,
-                        "en",
-                        serializedPreconditions);
-
-        byte[] actual = params.getSerializedPreconditions(modelFile);
-
-        assertThat(actual).isNull();
-    }
-
-    @Test
-    public void getSerializedPreconditions_invalidLocales() {
-        final String LANGUAGE_TAG = "zh";
-        ModelFileManager.ModelFile modelFile = new ModelFileManager.ModelFile(
-                new File("/model/file"),
-                200 /* version */,
-                Collections.singletonList(Locale.forLanguageTag(LANGUAGE_TAG)),
-                LANGUAGE_TAG,
-                false);
-        byte[] serializedPreconditions = new byte[]{0x12, 0x24, 0x36};
-        ActionsModelParamsSupplier.ActionsModelParams params =
-                new ActionsModelParamsSupplier.ActionsModelParams(
-                        200 /* version */,
-                        "en",
-                        serializedPreconditions);
-
-        byte[] actual = params.getSerializedPreconditions(modelFile);
-
-        assertThat(actual).isNull();
-    }
-
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java b/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
deleted file mode 100644
index ec7e83f..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/ActionsSuggestionsHelperTest.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_OTHERS;
-import static android.view.textclassifier.ConversationActions.Message.PERSON_USER_SELF;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.textclassifier.intent.LabeledIntent;
-import android.view.textclassifier.intent.TemplateIntentFactory;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.ActionsSuggestionsModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Function;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ActionsSuggestionsHelperTest {
-    private static final String LOCALE_TAG = Locale.US.toLanguageTag();
-    private static final Function<CharSequence, String> LANGUAGE_DETECTOR =
-            charSequence -> LOCALE_TAG;
-
-    @Test
-    public void testToNativeMessages_emptyInput() {
-        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
-                ActionsSuggestionsHelper.toNativeMessages(
-                        Collections.emptyList(), LANGUAGE_DETECTOR);
-
-        assertThat(conversationMessages).isEmpty();
-    }
-
-    @Test
-    public void testToNativeMessages_noTextMessages() {
-        ConversationActions.Message messageWithoutText =
-                new ConversationActions.Message.Builder(PERSON_USER_OTHERS).build();
-
-        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
-                ActionsSuggestionsHelper.toNativeMessages(
-                        Collections.singletonList(messageWithoutText), LANGUAGE_DETECTOR);
-
-        assertThat(conversationMessages).isEmpty();
-    }
-
-    @Test
-    public void testToNativeMessages_userIdEncoding() {
-        Person userA = new Person.Builder().setName("userA").build();
-        Person userB = new Person.Builder().setName("userB").build();
-
-        ConversationActions.Message firstMessage =
-                new ConversationActions.Message.Builder(userB)
-                        .setText("first")
-                        .build();
-        ConversationActions.Message secondMessage =
-                new ConversationActions.Message.Builder(userA)
-                        .setText("second")
-                        .build();
-        ConversationActions.Message thirdMessage =
-                new ConversationActions.Message.Builder(PERSON_USER_SELF)
-                        .setText("third")
-                        .build();
-        ConversationActions.Message fourthMessage =
-                new ConversationActions.Message.Builder(userA)
-                        .setText("fourth")
-                        .build();
-
-        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
-                ActionsSuggestionsHelper.toNativeMessages(
-                        Arrays.asList(firstMessage, secondMessage, thirdMessage, fourthMessage),
-                        LANGUAGE_DETECTOR);
-
-        assertThat(conversationMessages).hasLength(4);
-        assertNativeMessage(conversationMessages[0], firstMessage.getText(), 2, 0);
-        assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
-        assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 0, 0);
-        assertNativeMessage(conversationMessages[3], fourthMessage.getText(), 1, 0);
-    }
-
-    @Test
-    public void testToNativeMessages_referenceTime() {
-        ConversationActions.Message firstMessage =
-                new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
-                        .setText("first")
-                        .setReferenceTime(createZonedDateTimeFromMsUtc(1000))
-                        .build();
-        ConversationActions.Message secondMessage =
-                new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
-                        .setText("second")
-                        .build();
-        ConversationActions.Message thirdMessage =
-                new ConversationActions.Message.Builder(PERSON_USER_OTHERS)
-                        .setText("third")
-                        .setReferenceTime(createZonedDateTimeFromMsUtc(2000))
-                        .build();
-
-        ActionsSuggestionsModel.ConversationMessage[] conversationMessages =
-                ActionsSuggestionsHelper.toNativeMessages(
-                        Arrays.asList(firstMessage, secondMessage, thirdMessage),
-                        LANGUAGE_DETECTOR);
-
-        assertThat(conversationMessages).hasLength(3);
-        assertNativeMessage(conversationMessages[0], firstMessage.getText(), 1, 1000);
-        assertNativeMessage(conversationMessages[1], secondMessage.getText(), 1, 0);
-        assertNativeMessage(conversationMessages[2], thirdMessage.getText(), 1, 2000);
-    }
-
-    @Test
-    public void testDeduplicateActions() {
-        Bundle phoneExtras = new Bundle();
-        Intent phoneIntent = new Intent();
-        phoneIntent.setComponent(new ComponentName("phone", "intent"));
-        ExtrasUtils.putActionIntent(phoneExtras, phoneIntent);
-
-        Bundle anotherPhoneExtras = new Bundle();
-        Intent anotherPhoneIntent = new Intent();
-        anotherPhoneIntent.setComponent(new ComponentName("phone", "another.intent"));
-        ExtrasUtils.putActionIntent(anotherPhoneExtras, anotherPhoneIntent);
-
-        Bundle urlExtras = new Bundle();
-        Intent urlIntent = new Intent();
-        urlIntent.setComponent(new ComponentName("url", "intent"));
-        ExtrasUtils.putActionIntent(urlExtras, urlIntent);
-
-        PendingIntent pendingIntent = PendingIntent.getActivity(
-                InstrumentationRegistry.getTargetContext(),
-                0,
-                phoneIntent,
-                0);
-        Icon icon = Icon.createWithData(new byte[0], 0, 0);
-        ConversationAction action =
-                new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
-                        .setAction(new RemoteAction(icon, "label", "1", pendingIntent))
-                        .setExtras(phoneExtras)
-                        .build();
-        ConversationAction actionWithSameLabel =
-                new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
-                        .setAction(new RemoteAction(
-                                icon, "label", "2", pendingIntent))
-                        .setExtras(phoneExtras)
-                        .build();
-        ConversationAction actionWithSamePackageButDifferentClass =
-                new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
-                        .setAction(new RemoteAction(
-                                icon, "label", "3", pendingIntent))
-                        .setExtras(anotherPhoneExtras)
-                        .build();
-        ConversationAction actionWithDifferentLabel =
-                new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
-                        .setAction(new RemoteAction(
-                                icon, "another_label", "4", pendingIntent))
-                        .setExtras(phoneExtras)
-                        .build();
-        ConversationAction actionWithDifferentPackage =
-                new ConversationAction.Builder(ConversationAction.TYPE_OPEN_URL)
-                        .setAction(new RemoteAction(icon, "label", "5", pendingIntent))
-                        .setExtras(urlExtras)
-                        .build();
-        ConversationAction actionWithoutRemoteAction =
-                new ConversationAction.Builder(ConversationAction.TYPE_CREATE_REMINDER)
-                        .build();
-
-        List<ConversationAction> conversationActions =
-                ActionsSuggestionsHelper.removeActionsWithDuplicates(
-                        Arrays.asList(action, actionWithSameLabel,
-                                actionWithSamePackageButDifferentClass, actionWithDifferentLabel,
-                                actionWithDifferentPackage, actionWithoutRemoteAction));
-
-        assertThat(conversationActions).hasSize(3);
-        assertThat(conversationActions.get(0).getAction().getContentDescription()).isEqualTo("4");
-        assertThat(conversationActions.get(1).getAction().getContentDescription()).isEqualTo("5");
-        assertThat(conversationActions.get(2).getAction()).isNull();
-    }
-
-    @Test
-    public void testDeduplicateActions_nullComponent() {
-        Bundle phoneExtras = new Bundle();
-        Intent phoneIntent = new Intent(Intent.ACTION_DIAL);
-        ExtrasUtils.putActionIntent(phoneExtras, phoneIntent);
-        PendingIntent pendingIntent = PendingIntent.getActivity(
-                InstrumentationRegistry.getTargetContext(),
-                0,
-                phoneIntent,
-                0);
-        Icon icon = Icon.createWithData(new byte[0], 0, 0);
-        ConversationAction action =
-                new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
-                        .setAction(new RemoteAction(icon, "label", "1", pendingIntent))
-                        .setExtras(phoneExtras)
-                        .build();
-        ConversationAction actionWithSameLabel =
-                new ConversationAction.Builder(ConversationAction.TYPE_CALL_PHONE)
-                        .setAction(new RemoteAction(
-                                icon, "label", "2", pendingIntent))
-                        .setExtras(phoneExtras)
-                        .build();
-
-        List<ConversationAction> conversationActions =
-                ActionsSuggestionsHelper.removeActionsWithDuplicates(
-                        Arrays.asList(action, actionWithSameLabel));
-
-        assertThat(conversationActions).isEmpty();
-    }
-
-    @Test
-    public void createLabeledIntentResult_null() {
-        ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
-                new ActionsSuggestionsModel.ActionSuggestion(
-                        "text",
-                        ConversationAction.TYPE_OPEN_URL,
-                        1.0f,
-                        null,
-                        null,
-                        null
-                );
-
-        LabeledIntent.Result labeledIntentResult =
-                ActionsSuggestionsHelper.createLabeledIntentResult(
-                        InstrumentationRegistry.getTargetContext(),
-                        new TemplateIntentFactory(),
-                        nativeSuggestion);
-
-        assertThat(labeledIntentResult).isNull();
-    }
-
-    @Test
-    public void createLabeledIntentResult_emptyList() {
-        ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
-                new ActionsSuggestionsModel.ActionSuggestion(
-                        "text",
-                        ConversationAction.TYPE_OPEN_URL,
-                        1.0f,
-                        null,
-                        null,
-                        new RemoteActionTemplate[0]
-                );
-
-        LabeledIntent.Result labeledIntentResult =
-                ActionsSuggestionsHelper.createLabeledIntentResult(
-                        InstrumentationRegistry.getTargetContext(),
-                        new TemplateIntentFactory(),
-                        nativeSuggestion);
-
-        assertThat(labeledIntentResult).isNull();
-    }
-
-    @Test
-    public void createLabeledIntentResult() {
-        ActionsSuggestionsModel.ActionSuggestion nativeSuggestion =
-                new ActionsSuggestionsModel.ActionSuggestion(
-                        "text",
-                        ConversationAction.TYPE_OPEN_URL,
-                        1.0f,
-                        null,
-                        null,
-                        new RemoteActionTemplate[]{
-                                new RemoteActionTemplate(
-                                        "title",
-                                        null,
-                                        "description",
-                                        null,
-                                        Intent.ACTION_VIEW,
-                                        Uri.parse("http://www.android.com").toString(),
-                                        null,
-                                        0,
-                                        null,
-                                        null,
-                                        null,
-                                        0)});
-
-        LabeledIntent.Result labeledIntentResult =
-                ActionsSuggestionsHelper.createLabeledIntentResult(
-                        InstrumentationRegistry.getTargetContext(),
-                        new TemplateIntentFactory(),
-                        nativeSuggestion);
-
-        assertThat(labeledIntentResult.remoteAction.getTitle()).isEqualTo("title");
-        assertThat(labeledIntentResult.resolvedIntent.getAction()).isEqualTo(Intent.ACTION_VIEW);
-    }
-
-    private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
-        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneId.of("UTC"));
-    }
-
-    private static void assertNativeMessage(
-            ActionsSuggestionsModel.ConversationMessage nativeMessage,
-            CharSequence text,
-            int userId,
-            long referenceTimeInMsUtc) {
-        assertThat(nativeMessage.getText()).isEqualTo(text.toString());
-        assertThat(nativeMessage.getUserId()).isEqualTo(userId);
-        assertThat(nativeMessage.getDetectedTextLanguageTags()).isEqualTo(LOCALE_TAG);
-        assertThat(nativeMessage.getReferenceTimeMsUtc()).isEqualTo(referenceTimeInMsUtc);
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
deleted file mode 100644
index 79e1406..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.os.LocaleList;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ModelFileManagerTest {
-    private static final Locale DEFAULT_LOCALE = Locale.forLanguageTag("en-US");
-    @Mock
-    private Supplier<List<ModelFileManager.ModelFile>> mModelFileSupplier;
-    private ModelFileManager.ModelFileSupplierImpl mModelFileSupplierImpl;
-    private ModelFileManager mModelFileManager;
-    private File mRootTestDir;
-    private File mFactoryModelDir;
-    private File mUpdatedModelFile;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mModelFileManager = new ModelFileManager(mModelFileSupplier);
-        mRootTestDir = InstrumentationRegistry.getContext().getCacheDir();
-        mFactoryModelDir = new File(mRootTestDir, "factory");
-        mUpdatedModelFile = new File(mRootTestDir, "updated.model");
-
-        mModelFileSupplierImpl =
-                new ModelFileManager.ModelFileSupplierImpl(
-                        mFactoryModelDir,
-                        "test\\d.model",
-                        mUpdatedModelFile,
-                        fd -> 1,
-                        fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT
-                );
-
-        mRootTestDir.mkdirs();
-        mFactoryModelDir.mkdirs();
-
-        Locale.setDefault(DEFAULT_LOCALE);
-    }
-
-    @After
-    public void removeTestDir() {
-        recursiveDelete(mRootTestDir);
-    }
-
-    @Test
-    public void get() {
-        ModelFileManager.ModelFile modelFile =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1, Collections.emptyList(), "", true);
-        when(mModelFileSupplier.get()).thenReturn(Collections.singletonList(modelFile));
-
-        List<ModelFileManager.ModelFile> modelFiles = mModelFileManager.listModelFiles();
-
-        assertThat(modelFiles).hasSize(1);
-        assertThat(modelFiles.get(0)).isEqualTo(modelFile);
-    }
-
-    @Test
-    public void findBestModel_versionCode() {
-        ModelFileManager.ModelFile olderModelFile =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.emptyList(), "", true);
-
-        ModelFileManager.ModelFile newerModelFile =
-                new ModelFileManager.ModelFile(
-                        new File("/path/b"), 2,
-                        Collections.emptyList(), "", true);
-        when(mModelFileSupplier.get())
-                .thenReturn(Arrays.asList(olderModelFile, newerModelFile));
-
-        ModelFileManager.ModelFile bestModelFile =
-                mModelFileManager.findBestModelFile(LocaleList.getEmptyLocaleList());
-
-        assertThat(bestModelFile).isEqualTo(newerModelFile);
-    }
-
-    @Test
-    public void findBestModel_languageDependentModelIsPreferred() {
-        Locale locale = Locale.forLanguageTag("ja");
-        ModelFileManager.ModelFile languageIndependentModelFile =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.emptyList(), "", true);
-
-        ModelFileManager.ModelFile languageDependentModelFile =
-                new ModelFileManager.ModelFile(
-                        new File("/path/b"), 1,
-                        Collections.singletonList(locale), locale.toLanguageTag(), false);
-        when(mModelFileSupplier.get())
-                .thenReturn(
-                        Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
-
-        ModelFileManager.ModelFile bestModelFile =
-                mModelFileManager.findBestModelFile(
-                        LocaleList.forLanguageTags(locale.toLanguageTag()));
-        assertThat(bestModelFile).isEqualTo(languageDependentModelFile);
-    }
-
-    @Test
-    public void findBestModel_noMatchedLanguageModel() {
-        Locale locale = Locale.forLanguageTag("ja");
-        ModelFileManager.ModelFile languageIndependentModelFile =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.emptyList(), "", true);
-
-        ModelFileManager.ModelFile languageDependentModelFile =
-                new ModelFileManager.ModelFile(
-                        new File("/path/b"), 1,
-                        Collections.singletonList(locale), locale.toLanguageTag(), false);
-
-        when(mModelFileSupplier.get())
-                .thenReturn(
-                        Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
-
-        ModelFileManager.ModelFile bestModelFile =
-                mModelFileManager.findBestModelFile(
-                        LocaleList.forLanguageTags("zh-hk"));
-        assertThat(bestModelFile).isEqualTo(languageIndependentModelFile);
-    }
-
-    @Test
-    public void findBestModel_noMatchedLanguageModel_defaultLocaleModelExists() {
-        ModelFileManager.ModelFile languageIndependentModelFile =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.emptyList(), "", true);
-
-        ModelFileManager.ModelFile languageDependentModelFile =
-                new ModelFileManager.ModelFile(
-                        new File("/path/b"), 1,
-                        Collections.singletonList(
-                                DEFAULT_LOCALE), DEFAULT_LOCALE.toLanguageTag(), false);
-
-        when(mModelFileSupplier.get())
-                .thenReturn(
-                        Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
-
-        ModelFileManager.ModelFile bestModelFile =
-                mModelFileManager.findBestModelFile(
-                        LocaleList.forLanguageTags("zh-hk"));
-        assertThat(bestModelFile).isEqualTo(languageIndependentModelFile);
-    }
-
-    @Test
-    public void findBestModel_languageIsMoreImportantThanVersion() {
-        ModelFileManager.ModelFile matchButOlderModel =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.singletonList(Locale.forLanguageTag("fr")), "fr", false);
-
-        ModelFileManager.ModelFile mismatchButNewerModel =
-                new ModelFileManager.ModelFile(
-                        new File("/path/b"), 2,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        when(mModelFileSupplier.get())
-                .thenReturn(
-                        Arrays.asList(matchButOlderModel, mismatchButNewerModel));
-
-        ModelFileManager.ModelFile bestModelFile =
-                mModelFileManager.findBestModelFile(
-                        LocaleList.forLanguageTags("fr"));
-        assertThat(bestModelFile).isEqualTo(matchButOlderModel);
-    }
-
-    @Test
-    public void findBestModel_languageIsMoreImportantThanVersion_bestModelComesFirst() {
-        ModelFileManager.ModelFile matchLocaleModel =
-                new ModelFileManager.ModelFile(
-                        new File("/path/b"), 1,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        ModelFileManager.ModelFile languageIndependentModel =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 2,
-                        Collections.emptyList(), "", true);
-        when(mModelFileSupplier.get())
-                .thenReturn(
-                        Arrays.asList(matchLocaleModel, languageIndependentModel));
-
-        ModelFileManager.ModelFile bestModelFile =
-                mModelFileManager.findBestModelFile(
-                        LocaleList.forLanguageTags("ja"));
-
-        assertThat(bestModelFile).isEqualTo(matchLocaleModel);
-    }
-
-    @Test
-    public void modelFileEquals() {
-        ModelFileManager.ModelFile modelA =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        ModelFileManager.ModelFile modelB =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        assertThat(modelA).isEqualTo(modelB);
-    }
-
-    @Test
-    public void modelFile_different() {
-        ModelFileManager.ModelFile modelA =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        ModelFileManager.ModelFile modelB =
-                new ModelFileManager.ModelFile(
-                        new File("/path/b"), 1,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        assertThat(modelA).isNotEqualTo(modelB);
-    }
-
-
-    @Test
-    public void modelFile_getPath() {
-        ModelFileManager.ModelFile modelA =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        assertThat(modelA.getPath()).isEqualTo("/path/a");
-    }
-
-    @Test
-    public void modelFile_getName() {
-        ModelFileManager.ModelFile modelA =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        assertThat(modelA.getName()).isEqualTo("a");
-    }
-
-    @Test
-    public void modelFile_isPreferredTo_languageDependentIsBetter() {
-        ModelFileManager.ModelFile modelA =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 1,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        ModelFileManager.ModelFile modelB =
-                new ModelFileManager.ModelFile(
-                        new File("/path/b"), 2,
-                        Collections.emptyList(), "", true);
-
-        assertThat(modelA.isPreferredTo(modelB)).isTrue();
-    }
-
-    @Test
-    public void modelFile_isPreferredTo_version() {
-        ModelFileManager.ModelFile modelA =
-                new ModelFileManager.ModelFile(
-                        new File("/path/a"), 2,
-                        Collections.singletonList(Locale.forLanguageTag("ja")), "ja", false);
-
-        ModelFileManager.ModelFile modelB =
-                new ModelFileManager.ModelFile(
-                        new File("/path/b"), 1,
-                        Collections.emptyList(), "", false);
-
-        assertThat(modelA.isPreferredTo(modelB)).isTrue();
-    }
-
-    @Test
-    public void testFileSupplierImpl_updatedFileOnly() throws IOException {
-        mUpdatedModelFile.createNewFile();
-        File model1 = new File(mFactoryModelDir, "test1.model");
-        model1.createNewFile();
-        File model2 = new File(mFactoryModelDir, "test2.model");
-        model2.createNewFile();
-        new File(mFactoryModelDir, "not_match_regex.model").createNewFile();
-
-        List<ModelFileManager.ModelFile> modelFiles = mModelFileSupplierImpl.get();
-        List<String> modelFilePaths =
-                modelFiles
-                        .stream()
-                        .map(modelFile -> modelFile.getPath())
-                        .collect(Collectors.toList());
-
-        assertThat(modelFiles).hasSize(3);
-        assertThat(modelFilePaths).containsExactly(
-                mUpdatedModelFile.getAbsolutePath(),
-                model1.getAbsolutePath(),
-                model2.getAbsolutePath());
-    }
-
-    @Test
-    public void testFileSupplierImpl_empty() {
-        mFactoryModelDir.delete();
-        List<ModelFileManager.ModelFile> modelFiles = mModelFileSupplierImpl.get();
-
-        assertThat(modelFiles).hasSize(0);
-    }
-
-    private static void recursiveDelete(File f) {
-        if (f.isDirectory()) {
-            for (File innerFile : f.listFiles()) {
-                recursiveDelete(innerFile);
-            }
-        }
-        f.delete();
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index 82fa73f..2f21b7f 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -16,7 +16,6 @@
 
 package android.view.textclassifier;
 
-import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.provider.DeviceConfig;
@@ -24,8 +23,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.google.common.primitives.Floats;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -57,17 +54,17 @@
     public void testLoadFromDeviceConfig_IntValue() throws Exception {
         // Saves config original value.
         final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH);
+                TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH);
 
         final TextClassificationConstants constants = new TextClassificationConstants();
         try {
             // Sets and checks different value.
-            setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH, "8");
-            assertWithMessage(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH)
-                    .that(constants.getSuggestSelectionMaxRangeLength()).isEqualTo(8);
+            setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH, "8");
+            assertWithMessage(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH)
+                    .that(constants.getGenerateLinksMaxTextLength()).isEqualTo(8);
         } finally {
             // Restores config original value.
-            setDeviceConfig(TextClassificationConstants.SUGGEST_SELECTION_MAX_RANGE_LENGTH,
+            setDeviceConfig(TextClassificationConstants.GENERATE_LINKS_MAX_TEXT_LENGTH,
                     originalValue);
         }
     }
@@ -94,61 +91,6 @@
         }
     }
 
-    @Test
-    public void testLoadFromDeviceConfig_FloatValue() throws Exception {
-        // Saves config original value.
-        final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE);
-
-        final TextClassificationConstants constants = new TextClassificationConstants();
-        try {
-            // Sets and checks different value.
-            setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, "2");
-            assertWithMessage(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE)
-                    .that(constants.getLangIdThresholdOverride()).isWithin(EPSILON).of(2f);
-        } finally {
-            // Restores config original value.
-            setDeviceConfig(TextClassificationConstants.LANG_ID_THRESHOLD_OVERRIDE, originalValue);
-        }
-    }
-
-    @Test
-    public void testLoadFromDeviceConfig_StringList() throws Exception {
-        // Saves config original value.
-        final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                TextClassificationConstants.ENTITY_LIST_DEFAULT);
-
-        final TextClassificationConstants constants = new TextClassificationConstants();
-        try {
-            // Sets and checks different value.
-            setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, "email:url");
-            assertWithMessage(TextClassificationConstants.ENTITY_LIST_DEFAULT)
-                    .that(constants.getEntityListDefault())
-                    .containsExactly("email", "url");
-        } finally {
-            // Restores config original value.
-            setDeviceConfig(TextClassificationConstants.ENTITY_LIST_DEFAULT, originalValue);
-        }
-    }
-
-    @Test
-    public void testLoadFromDeviceConfig_FloatList() throws Exception {
-        // Saves config original value.
-        final String originalValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
-                TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS);
-
-        final TextClassificationConstants constants = new TextClassificationConstants();
-        try {
-            // Sets and checks different value.
-            setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, "30:0.5:0.3");
-            assertThat(Floats.asList(constants.getLangIdContextSettings())).containsExactly(30f,
-                    0.5f, 0.3f).inOrder();
-        } finally {
-            // Restores config original value.
-            setDeviceConfig(TextClassificationConstants.LANG_ID_CONTEXT_SETTINGS, originalValue);
-        }
-    }
-
     private void setDeviceConfig(String key, String value) {
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key,
                 value, /* makeDefault */ false);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 1ca4649..628252d 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -16,19 +16,15 @@
 
 package android.view.textclassifier;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.mock;
 
 import android.content.Context;
-import android.content.Intent;
-import android.os.LocaleList;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -38,14 +34,12 @@
 @RunWith(AndroidJUnit4.class)
 public class TextClassificationManagerTest {
 
-    private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
-
     private Context mContext;
     private TextClassificationManager mTcm;
 
     @Before
     public void setup() {
-        mContext = InstrumentationRegistry.getTargetContext();
+        mContext = ApplicationProvider.getApplicationContext();
         mTcm = mContext.getSystemService(TextClassificationManager.class);
     }
 
@@ -53,45 +47,17 @@
     public void testSetTextClassifier() {
         TextClassifier classifier = mock(TextClassifier.class);
         mTcm.setTextClassifier(classifier);
-        assertEquals(classifier, mTcm.getTextClassifier());
+        assertThat(mTcm.getTextClassifier()).isEqualTo(classifier);
     }
 
     @Test
     public void testGetLocalTextClassifier() {
-        assertTrue(mTcm.getTextClassifier(TextClassifier.LOCAL) instanceof TextClassifierImpl);
+        assertThat(mTcm.getTextClassifier(TextClassifier.LOCAL)).isSameAs(TextClassifier.NO_OP);
     }
 
     @Test
     public void testGetSystemTextClassifier() {
-        assertTrue(mTcm.getTextClassifier(TextClassifier.SYSTEM) instanceof SystemTextClassifier);
-    }
-
-    @Test
-    public void testCannotResolveIntent() {
-        Context fakeContext = new FakeContextBuilder()
-                .setAllIntentComponent(FakeContextBuilder.DEFAULT_COMPONENT)
-                .setIntentComponent(Intent.ACTION_INSERT_OR_EDIT, null)
-                .build();
-
-        TextClassifier fallback = TextClassifier.NO_OP;
-        TextClassifier classifier = new TextClassifierImpl(
-                fakeContext, new TextClassificationConstants(), fallback);
-
-        String text = "Contact me at +12122537077";
-        String classifiedText = "+12122537077";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification result = classifier.classifyText(request);
-        TextClassification fallbackResult = fallback.classifyText(request);
-
-        // classifier should not totally fail in which case it returns a fallback result.
-        // It should skip the failing intent and return a result for non-failing intents.
-        assertFalse(result.getActions().isEmpty());
-        assertNotSame(result, fallbackResult);
+        assertThat(mTcm.getTextClassifier(TextClassifier.SYSTEM))
+                .isInstanceOf(SystemTextClassifier.class);
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
deleted file mode 100644
index 372a478..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ /dev/null
@@ -1,719 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import android.app.RemoteAction;
-import android.content.Context;
-import android.content.Intent;
-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;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-
-import com.google.common.truth.Truth;
-
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Testing {@link TextClassifierTest} APIs on local and system textclassifier.
- * <p>
- * Tests are skipped if such a textclassifier does not exist.
- */
-@SmallTest
-@RunWith(Parameterized.class)
-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, DEFAULT);
-    }
-
-    @Parameterized.Parameter
-    public String mTextClassifierType;
-
-    private static final TextClassificationConstants TC_CONSTANTS =
-            new TextClassificationConstants();
-    private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
-    private static final String NO_TYPE = null;
-
-    private Context mContext;
-    private TextClassificationManager mTcm;
-    private TextClassifier mClassifier;
-
-    @Before
-    public void setup() {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mTcm = mContext.getSystemService(TextClassificationManager.class);
-
-        if (mTextClassifierType.equals(LOCAL)) {
-            mClassifier = mTcm.getTextClassifier(TextClassifier.LOCAL);
-        } 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);
-        }
-    }
-
-    @Test
-    public void testSuggestSelection() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Contact me at droid@android.com";
-        String selected = "droid";
-        String suggested = "droid@android.com";
-        int startIndex = text.indexOf(selected);
-        int endIndex = startIndex + selected.length();
-        int smartStartIndex = text.indexOf(suggested);
-        int smartEndIndex = smartStartIndex + suggested.length();
-        TextSelection.Request request = new TextSelection.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextSelection selection = mClassifier.suggestSelection(request);
-        assertThat(selection,
-                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
-    }
-
-    @Test
-    public void testSuggestSelection_url() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Visit http://www.android.com for more information";
-        String selected = "http";
-        String suggested = "http://www.android.com";
-        int startIndex = text.indexOf(selected);
-        int endIndex = startIndex + selected.length();
-        int smartStartIndex = text.indexOf(suggested);
-        int smartEndIndex = smartStartIndex + suggested.length();
-        TextSelection.Request request = new TextSelection.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextSelection selection = mClassifier.suggestSelection(request);
-        assertThat(selection,
-                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
-    }
-
-    @Test
-    public void testSmartSelection_withEmoji() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "\uD83D\uDE02 Hello.";
-        String selected = "Hello";
-        int startIndex = text.indexOf(selected);
-        int endIndex = startIndex + selected.length();
-        TextSelection.Request request = new TextSelection.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextSelection selection = mClassifier.suggestSelection(request);
-        assertThat(selection,
-                isTextSelection(startIndex, endIndex, NO_TYPE));
-    }
-
-    @Test
-    public void testClassifyText() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Contact me at droid@android.com";
-        String classifiedText = "droid@android.com";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_EMAIL));
-    }
-
-    @Test
-    public void testClassifyText_url() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Visit www.android.com for more information";
-        String classifiedText = "www.android.com";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
-        assertThat(classification, containsIntentWithAction(Intent.ACTION_VIEW));
-    }
-
-    @Test
-    public void testClassifyText_address() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Brandschenkestrasse 110, Zürich, Switzerland";
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, 0, text.length())
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(text, TextClassifier.TYPE_ADDRESS));
-    }
-
-    @Test
-    public void testClassifyText_url_inCaps() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Visit HTTP://ANDROID.COM for more information";
-        String classifiedText = "HTTP://ANDROID.COM";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
-        assertThat(classification, containsIntentWithAction(Intent.ACTION_VIEW));
-    }
-
-    @Test
-    public void testClassifyText_date() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Let's meet on January 9, 2018.";
-        String classifiedText = "January 9, 2018";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_DATE));
-        Bundle extras = classification.getExtras();
-        List<Bundle> entities = ExtrasUtils.getEntities(extras);
-        Truth.assertThat(entities).hasSize(1);
-        Bundle entity = entities.get(0);
-        Truth.assertThat(ExtrasUtils.getEntityType(entity)).isEqualTo(TextClassifier.TYPE_DATE);
-    }
-
-    @Test
-    public void testClassifyText_datetime() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "Let's meet 2018/01/01 10:30:20.";
-        String classifiedText = "2018/01/01 10:30:20";
-        int startIndex = text.indexOf(classifiedText);
-        int endIndex = startIndex + classifiedText.length();
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                text, startIndex, endIndex)
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = mClassifier.classifyText(request);
-        assertThat(classification,
-                isTextClassification(classifiedText, TextClassifier.TYPE_DATE_TIME));
-    }
-
-    @Test
-    public void testClassifyText_foreignText() {
-        LocaleList originalLocales = LocaleList.getDefault();
-        LocaleList.setDefault(LocaleList.forLanguageTags("en"));
-        String japaneseText = "これは日本語のテキストです";
-
-        Context context = new FakeContextBuilder()
-                .setIntentComponent(Intent.ACTION_TRANSLATE, FakeContextBuilder.DEFAULT_COMPONENT)
-                .build();
-        TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
-        TextClassification.Request request = new TextClassification.Request.Builder(
-                japaneseText, 0, japaneseText.length())
-                .setDefaultLocales(LOCALES)
-                .build();
-
-        TextClassification classification = classifier.classifyText(request);
-        RemoteAction translateAction = classification.getActions().get(0);
-        assertEquals(1, classification.getActions().size());
-        assertEquals(
-                context.getString(com.android.internal.R.string.translate),
-                translateAction.getTitle());
-
-        assertEquals(translateAction, ExtrasUtils.findTranslateAction(classification));
-        Intent intent = ExtrasUtils.getActionsIntents(classification).get(0);
-        assertEquals(Intent.ACTION_TRANSLATE, intent.getAction());
-        Bundle foreignLanguageInfo = ExtrasUtils.getForeignLanguageExtra(classification);
-        assertEquals("ja", ExtrasUtils.getEntityType(foreignLanguageInfo));
-        assertTrue(ExtrasUtils.getScore(foreignLanguageInfo) >= 0);
-        assertTrue(ExtrasUtils.getScore(foreignLanguageInfo) <= 1);
-        assertTrue(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER));
-        assertEquals("ja", ExtrasUtils.getTopLanguage(intent).getLanguage());
-
-        LocaleList.setDefault(originalLocales);
-    }
-
-    @Test
-    public void testGenerateLinks_phone() {
-        if (isTextClassifierDisabled()) return;
-        String text = "The number is +12122537077. See you tonight!";
-        TextLinks.Request request = new TextLinks.Request.Builder(text).build();
-        assertThat(mClassifier.generateLinks(request),
-                isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE));
-    }
-
-    @Test
-    public void testGenerateLinks_exclude() {
-        if (isTextClassifierDisabled()) return;
-        String text = "You want apple@banana.com. See you tonight!";
-        List<String> hints = Collections.EMPTY_LIST;
-        List<String> included = Collections.EMPTY_LIST;
-        List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
-        TextLinks.Request request = new TextLinks.Request.Builder(text)
-                .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
-                .setDefaultLocales(LOCALES)
-                .build();
-        assertThat(mClassifier.generateLinks(request),
-                not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
-    }
-
-    @Test
-    public void testGenerateLinks_explicit_address() {
-        if (isTextClassifierDisabled()) return;
-        String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
-        List<String> explicit = Arrays.asList(TextClassifier.TYPE_ADDRESS);
-        TextLinks.Request request = new TextLinks.Request.Builder(text)
-                .setEntityConfig(TextClassifier.EntityConfig.createWithExplicitEntityList(explicit))
-                .setDefaultLocales(LOCALES)
-                .build();
-        assertThat(mClassifier.generateLinks(request),
-                isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
-                        TextClassifier.TYPE_ADDRESS));
-    }
-
-    @Test
-    public void testGenerateLinks_exclude_override() {
-        if (isTextClassifierDisabled()) return;
-        String text = "You want apple@banana.com. See you tonight!";
-        List<String> hints = Collections.EMPTY_LIST;
-        List<String> included = Arrays.asList(TextClassifier.TYPE_EMAIL);
-        List<String> excluded = Arrays.asList(TextClassifier.TYPE_EMAIL);
-        TextLinks.Request request = new TextLinks.Request.Builder(text)
-                .setEntityConfig(TextClassifier.EntityConfig.create(hints, included, excluded))
-                .setDefaultLocales(LOCALES)
-                .build();
-        assertThat(mClassifier.generateLinks(request),
-                not(isTextLinksContaining(text, "apple@banana.com", TextClassifier.TYPE_EMAIL)));
-    }
-
-    @Test
-    public void testGenerateLinks_maxLength() {
-        if (isTextClassifierDisabled()) return;
-        char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength()];
-        Arrays.fill(manySpaces, ' ');
-        TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
-        TextLinks links = mClassifier.generateLinks(request);
-        assertTrue(links.getLinks().isEmpty());
-    }
-
-    @Test
-    public void testApplyLinks_unsupportedCharacter() {
-        if (isTextClassifierDisabled()) return;
-        Spannable url = new SpannableString("\u202Emoc.diordna.com");
-        TextLinks.Request request = new TextLinks.Request.Builder(url).build();
-        assertEquals(
-                TextLinks.STATUS_UNSUPPORTED_CHARACTER,
-                mClassifier.generateLinks(request).apply(url, 0, null));
-    }
-
-    @Test
-    public void testGenerateLinks_tooLong() {
-        if (isTextClassifierDisabled()) return;
-        char[] manySpaces = new char[mClassifier.getMaxGenerateLinksTextLength() + 1];
-        Arrays.fill(manySpaces, ' ');
-        TextLinks.Request request = new TextLinks.Request.Builder(new String(manySpaces)).build();
-        TextLinks links = mClassifier.generateLinks(request);
-        assertTrue(links.getLinks().isEmpty());
-    }
-
-    @Test
-    public void testGenerateLinks_entityData() {
-        if (isTextClassifierDisabled()) return;
-        String text = "The number is +12122537077.";
-        Bundle extras = new Bundle();
-        ExtrasUtils.putIsSerializedEntityDataEnabled(extras, true);
-        TextLinks.Request request = new TextLinks.Request.Builder(text).setExtras(extras).build();
-
-        TextLinks textLinks = mClassifier.generateLinks(request);
-
-        Truth.assertThat(textLinks.getLinks()).hasSize(1);
-        TextLinks.TextLink textLink = textLinks.getLinks().iterator().next();
-        List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras());
-        Truth.assertThat(entities).hasSize(1);
-        Bundle entity = entities.get(0);
-        Truth.assertThat(ExtrasUtils.getEntityType(entity)).isEqualTo(TextClassifier.TYPE_PHONE);
-    }
-
-    @Test
-    public void testGenerateLinks_entityData_disabled() {
-        if (isTextClassifierDisabled()) return;
-        String text = "The number is +12122537077.";
-        TextLinks.Request request = new TextLinks.Request.Builder(text).build();
-
-        TextLinks textLinks = mClassifier.generateLinks(request);
-
-        Truth.assertThat(textLinks.getLinks()).hasSize(1);
-        TextLinks.TextLink textLink = textLinks.getLinks().iterator().next();
-        List<Bundle> entities = ExtrasUtils.getEntities(textLink.getExtras());
-        Truth.assertThat(entities).isNull();
-    }
-
-    @Test
-    public void testDetectLanguage() {
-        if (isTextClassifierDisabled()) return;
-        String text = "This is English text";
-        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
-        TextLanguage textLanguage = mClassifier.detectLanguage(request);
-        assertThat(textLanguage, isTextLanguage("en"));
-    }
-
-    @Test
-    public void testDetectLanguage_japanese() {
-        if (isTextClassifierDisabled()) return;
-        String text = "これは日本語のテキストです";
-        TextLanguage.Request request = new TextLanguage.Request.Builder(text).build();
-        TextLanguage textLanguage = mClassifier.detectLanguage(request);
-        assertThat(textLanguage, isTextLanguage("ja"));
-    }
-
-    @Ignore  // Doesn't work without a language-based model.
-    @Test
-    public void testSuggestConversationActions_textReplyOnly_maxOne() {
-        if (isTextClassifierDisabled()) return;
-        ConversationActions.Message message =
-                new ConversationActions.Message.Builder(
-                        ConversationActions.Message.PERSON_USER_OTHERS)
-                        .setText("Where are you?")
-                        .build();
-        TextClassifier.EntityConfig typeConfig =
-                new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
-                        .setIncludedTypes(
-                                Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
-                        .build();
-        ConversationActions.Request request =
-                new ConversationActions.Request.Builder(Collections.singletonList(message))
-                        .setMaxSuggestions(1)
-                        .setTypeConfig(typeConfig)
-                        .build();
-
-        ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
-        Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
-        ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
-        Truth.assertThat(conversationAction.getType()).isEqualTo(
-                ConversationAction.TYPE_TEXT_REPLY);
-        Truth.assertThat(conversationAction.getTextReply()).isNotNull();
-    }
-
-    @Ignore  // Doesn't work without a language-based model.
-    @Test
-    public void testSuggestConversationActions_textReplyOnly_noMax() {
-        if (isTextClassifierDisabled()) return;
-        ConversationActions.Message message =
-                new ConversationActions.Message.Builder(
-                        ConversationActions.Message.PERSON_USER_OTHERS)
-                        .setText("Where are you?")
-                        .build();
-        TextClassifier.EntityConfig typeConfig =
-                new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
-                        .setIncludedTypes(
-                                Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
-                        .build();
-        ConversationActions.Request request =
-                new ConversationActions.Request.Builder(Collections.singletonList(message))
-                        .setTypeConfig(typeConfig)
-                        .build();
-
-        ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
-        assertTrue(conversationActions.getConversationActions().size() > 1);
-        for (ConversationAction conversationAction :
-                conversationActions.getConversationActions()) {
-            assertThat(conversationAction,
-                    isConversationAction(ConversationAction.TYPE_TEXT_REPLY));
-        }
-    }
-
-    @Test
-    public void testSuggestConversationActions_openUrl() {
-        if (isTextClassifierDisabled()) return;
-        ConversationActions.Message message =
-                new ConversationActions.Message.Builder(
-                        ConversationActions.Message.PERSON_USER_OTHERS)
-                        .setText("Check this out: https://www.android.com")
-                        .build();
-        TextClassifier.EntityConfig typeConfig =
-                new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
-                        .setIncludedTypes(
-                                Collections.singletonList(ConversationAction.TYPE_OPEN_URL))
-                        .build();
-        ConversationActions.Request request =
-                new ConversationActions.Request.Builder(Collections.singletonList(message))
-                        .setMaxSuggestions(1)
-                        .setTypeConfig(typeConfig)
-                        .build();
-
-        ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
-        Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
-        ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
-        Truth.assertThat(conversationAction.getType()).isEqualTo(ConversationAction.TYPE_OPEN_URL);
-        Intent actionIntent = ExtrasUtils.getActionIntent(conversationAction.getExtras());
-        Truth.assertThat(actionIntent.getAction()).isEqualTo(Intent.ACTION_VIEW);
-        Truth.assertThat(actionIntent.getData()).isEqualTo(Uri.parse("https://www.android.com"));
-    }
-
-    @Ignore  // Doesn't work without a language-based model.
-    @Test
-    public void testSuggestConversationActions_copy() {
-        if (isTextClassifierDisabled()) return;
-        ConversationActions.Message message =
-                new ConversationActions.Message.Builder(
-                        ConversationActions.Message.PERSON_USER_OTHERS)
-                        .setText("Authentication code: 12345")
-                        .build();
-        TextClassifier.EntityConfig typeConfig =
-                new TextClassifier.EntityConfig.Builder().includeTypesFromTextClassifier(false)
-                        .setIncludedTypes(
-                                Collections.singletonList(ConversationAction.TYPE_COPY))
-                        .build();
-        ConversationActions.Request request =
-                new ConversationActions.Request.Builder(Collections.singletonList(message))
-                        .setMaxSuggestions(1)
-                        .setTypeConfig(typeConfig)
-                        .build();
-
-        ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
-        Truth.assertThat(conversationActions.getConversationActions()).hasSize(1);
-        ConversationAction conversationAction = conversationActions.getConversationActions().get(0);
-        Truth.assertThat(conversationAction.getType()).isEqualTo(ConversationAction.TYPE_COPY);
-        Truth.assertThat(conversationAction.getTextReply()).isAnyOf(null, "");
-        Truth.assertThat(conversationAction.getAction()).isNull();
-        String code = ExtrasUtils.getCopyText(conversationAction.getExtras());
-        Truth.assertThat(code).isEqualTo("12345");
-        Truth.assertThat(
-                ExtrasUtils.getSerializedEntityData(conversationAction.getExtras())).isNotEmpty();
-    }
-
-    @Test
-    public void testSuggestConversationActions_deduplicate() {
-        Context context = new FakeContextBuilder()
-                .setIntentComponent(Intent.ACTION_SENDTO, FakeContextBuilder.DEFAULT_COMPONENT)
-                .build();
-        ConversationActions.Message message =
-                new ConversationActions.Message.Builder(
-                        ConversationActions.Message.PERSON_USER_OTHERS)
-                        .setText("a@android.com b@android.com")
-                        .build();
-        ConversationActions.Request request =
-                new ConversationActions.Request.Builder(Collections.singletonList(message))
-                        .setMaxSuggestions(3)
-                        .build();
-
-        TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
-        ConversationActions conversationActions = classifier.suggestConversationActions(request);
-
-        Truth.assertThat(conversationActions.getConversationActions()).isEmpty();
-    }
-
-    private boolean isTextClassifierDisabled() {
-        return mClassifier == null || mClassifier == TextClassifier.NO_OP;
-    }
-
-    private static Matcher<TextSelection> isTextSelection(
-            final int startIndex, final int endIndex, final String type) {
-        return new BaseMatcher<TextSelection>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof TextSelection) {
-                    TextSelection selection = (TextSelection) o;
-                    return startIndex == selection.getSelectionStartIndex()
-                            && endIndex == selection.getSelectionEndIndex()
-                            && typeMatches(selection, type);
-                }
-                return false;
-            }
-
-            private boolean typeMatches(TextSelection selection, String type) {
-                return type == null
-                        || (selection.getEntityCount() > 0
-                        && type.trim().equalsIgnoreCase(selection.getEntity(0)));
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendValue(
-                        String.format("%d, %d, %s", startIndex, endIndex, type));
-            }
-        };
-    }
-
-    private static Matcher<TextLinks> isTextLinksContaining(
-            final String text, final String substring, final String type) {
-        return new BaseMatcher<TextLinks>() {
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("text=").appendValue(text)
-                        .appendText(", substring=").appendValue(substring)
-                        .appendText(", type=").appendValue(type);
-            }
-
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof TextLinks) {
-                    for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) {
-                        if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) {
-                            return type.equals(link.getEntity(0));
-                        }
-                    }
-                }
-                return false;
-            }
-        };
-    }
-
-    private static Matcher<TextClassification> isTextClassification(
-            final String text, final String type) {
-        return new BaseMatcher<TextClassification>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof TextClassification) {
-                    TextClassification result = (TextClassification) o;
-                    return text.equals(result.getText())
-                            && result.getEntityCount() > 0
-                            && type.equals(result.getEntity(0));
-                }
-                return false;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("text=").appendValue(text)
-                        .appendText(", type=").appendValue(type);
-            }
-        };
-    }
-
-    private static Matcher<TextClassification> containsIntentWithAction(final String action) {
-        return new BaseMatcher<TextClassification>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof TextClassification) {
-                    TextClassification result = (TextClassification) o;
-                    return ExtrasUtils.findAction(result, action) != null;
-                }
-                return false;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("intent action=").appendValue(action);
-            }
-        };
-    }
-
-    private static Matcher<TextLanguage> isTextLanguage(final String languageTag) {
-        return new BaseMatcher<TextLanguage>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof TextLanguage) {
-                    TextLanguage result = (TextLanguage) o;
-                    return result.getLocaleHypothesisCount() > 0
-                            && languageTag.equals(result.getLocale(0).toLanguageTag());
-                }
-                return false;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("locale=").appendValue(languageTag);
-            }
-        };
-    }
-
-    private static Matcher<ConversationAction> isConversationAction(String actionType) {
-        return new BaseMatcher<ConversationAction>() {
-            @Override
-            public boolean matches(Object o) {
-                if (!(o instanceof ConversationAction)) {
-                    return false;
-                }
-                ConversationAction conversationAction =
-                        (ConversationAction) o;
-                if (!actionType.equals(conversationAction.getType())) {
-                    return false;
-                }
-                if (ConversationAction.TYPE_TEXT_REPLY.equals(actionType)) {
-                    if (conversationAction.getTextReply() == null) {
-                        return false;
-                    }
-                }
-                if (conversationAction.getConfidenceScore() < 0
-                        || conversationAction.getConfidenceScore() > 1) {
-                    return false;
-                }
-                return true;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendText("actionType=").appendValue(actionType);
-            }
-        };
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java
deleted file mode 100644
index 3ad26f5..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/LabeledIntentTest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.textclassifier.FakeContextBuilder;
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public final class LabeledIntentTest {
-    private static final String TITLE_WITHOUT_ENTITY = "Map";
-    private static final String TITLE_WITH_ENTITY = "Map NW14D1";
-    private static final String DESCRIPTION = "Check the map";
-    private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open map";
-    private static final Intent INTENT =
-            new Intent(Intent.ACTION_VIEW).setDataAndNormalize(Uri.parse("http://www.android.com"));
-    private static final int REQUEST_CODE = 42;
-    private static final Bundle TEXT_LANGUAGES_BUNDLE = Bundle.EMPTY;
-    private static final String APP_LABEL = "fake";
-
-    private Context mContext;
-
-    @Before
-    public void setup() {
-        final ComponentName component = FakeContextBuilder.DEFAULT_COMPONENT;
-        mContext = new FakeContextBuilder()
-                .setIntentComponent(Intent.ACTION_VIEW, component)
-                .setAppLabel(component.getPackageName(), APP_LABEL)
-                .build();
-    }
-
-    @Test
-    public void resolve_preferTitleWithEntity() {
-        LabeledIntent labeledIntent = new LabeledIntent(
-                TITLE_WITHOUT_ENTITY,
-                TITLE_WITH_ENTITY,
-                DESCRIPTION,
-                null,
-                INTENT,
-                REQUEST_CODE
-        );
-
-        LabeledIntent.Result result = labeledIntent.resolve(
-                mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
-
-        assertThat(result).isNotNull();
-        assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITH_ENTITY);
-        assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
-        Intent intent = result.resolvedIntent;
-        assertThat(intent.getAction()).isEqualTo(intent.getAction());
-        assertThat(intent.getComponent()).isNotNull();
-        assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue();
-    }
-
-    @Test
-    public void resolve_useAvailableTitle() {
-        LabeledIntent labeledIntent = new LabeledIntent(
-                TITLE_WITHOUT_ENTITY,
-                null,
-                DESCRIPTION,
-                null,
-                INTENT,
-                REQUEST_CODE
-        );
-
-        LabeledIntent.Result result = labeledIntent.resolve(
-                mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
-
-        assertThat(result).isNotNull();
-        assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY);
-        assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
-        Intent intent = result.resolvedIntent;
-        assertThat(intent.getAction()).isEqualTo(intent.getAction());
-        assertThat(intent.getComponent()).isNotNull();
-    }
-
-    @Test
-    public void resolve_titleChooser() {
-        LabeledIntent labeledIntent = new LabeledIntent(
-                TITLE_WITHOUT_ENTITY,
-                null,
-                DESCRIPTION,
-                null,
-                INTENT,
-                REQUEST_CODE
-        );
-
-        LabeledIntent.Result result = labeledIntent.resolve(
-                mContext, (labeledIntent1, resolveInfo) -> "chooser", TEXT_LANGUAGES_BUNDLE);
-
-        assertThat(result).isNotNull();
-        assertThat(result.remoteAction.getTitle()).isEqualTo("chooser");
-        assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
-        Intent intent = result.resolvedIntent;
-        assertThat(intent.getAction()).isEqualTo(intent.getAction());
-        assertThat(intent.getComponent()).isNotNull();
-    }
-
-    @Test
-    public void resolve_titleChooserReturnsNull() {
-        LabeledIntent labeledIntent = new LabeledIntent(
-                TITLE_WITHOUT_ENTITY,
-                null,
-                DESCRIPTION,
-                null,
-                INTENT,
-                REQUEST_CODE
-        );
-
-        LabeledIntent.Result result = labeledIntent.resolve(
-                mContext, (labeledIntent1, resolveInfo) -> null, TEXT_LANGUAGES_BUNDLE);
-
-        assertThat(result).isNotNull();
-        assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY);
-        assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION);
-        Intent intent = result.resolvedIntent;
-        assertThat(intent.getAction()).isEqualTo(intent.getAction());
-        assertThat(intent.getComponent()).isNotNull();
-    }
-
-    @Test
-    public void resolve_missingTitle() {
-        assertThrows(
-                IllegalArgumentException.class,
-                () ->
-                        new LabeledIntent(
-                                null,
-                                null,
-                                DESCRIPTION,
-                                null,
-                                INTENT,
-                                REQUEST_CODE
-                        ));
-    }
-
-    @Test
-    public void resolve_noIntentHandler() {
-        // See setup(). mContext can only resolve Intent.ACTION_VIEW.
-        Intent unresolvableIntent = new Intent(Intent.ACTION_TRANSLATE);
-        LabeledIntent labeledIntent = new LabeledIntent(
-                TITLE_WITHOUT_ENTITY,
-                null,
-                DESCRIPTION,
-                null,
-                unresolvableIntent,
-                REQUEST_CODE);
-
-        LabeledIntent.Result result = labeledIntent.resolve(mContext, null, null);
-
-        assertThat(result).isNull();
-    }
-
-    @Test
-    public void resolve_descriptionWithAppName() {
-        LabeledIntent labeledIntent = new LabeledIntent(
-                TITLE_WITHOUT_ENTITY,
-                TITLE_WITH_ENTITY,
-                DESCRIPTION,
-                DESCRIPTION_WITH_APP_NAME,
-                INTENT,
-                REQUEST_CODE
-        );
-
-        LabeledIntent.Result result = labeledIntent.resolve(
-                mContext, /*titleChooser*/ null, TEXT_LANGUAGES_BUNDLE);
-
-        assertThat(result).isNotNull();
-        assertThat(result.remoteAction.getContentDescription()).isEqualTo("Use fake to open map");
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
deleted file mode 100644
index 8891d3f..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/LegacyIntentClassificationFactoryTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Intent;
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.AnnotatorModel;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class LegacyIntentClassificationFactoryTest {
-
-    private static final String TEXT = "text";
-
-    private LegacyClassificationIntentFactory mLegacyIntentClassificationFactory;
-
-    @Before
-    public void setup() {
-        mLegacyIntentClassificationFactory = new LegacyClassificationIntentFactory();
-    }
-
-    @Test
-    public void create_typeDictionary() {
-        AnnotatorModel.ClassificationResult classificationResult =
-                new AnnotatorModel.ClassificationResult(
-                        TextClassifier.TYPE_DICTIONARY,
-                        1.0f,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        0L,
-                        0L,
-                        0d);
-
-        List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
-                InstrumentationRegistry.getContext(),
-                TEXT,
-                /* foreignText */ false,
-                null,
-                classificationResult);
-
-        assertThat(intents).hasSize(1);
-        LabeledIntent labeledIntent = intents.get(0);
-        Intent intent = labeledIntent.intent;
-        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
-        assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
-    }
-
-    @Test
-    public void create_translateAndDictionary() {
-        AnnotatorModel.ClassificationResult classificationResult =
-                new AnnotatorModel.ClassificationResult(
-                        TextClassifier.TYPE_DICTIONARY,
-                        1.0f,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        0L,
-                        0L,
-                        0d);
-
-        List<LabeledIntent> intents = mLegacyIntentClassificationFactory.create(
-                InstrumentationRegistry.getContext(),
-                TEXT,
-                /* foreignText */ true,
-                null,
-                classificationResult);
-
-        assertThat(intents).hasSize(2);
-        assertThat(intents.get(0).intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
-        assertThat(intents.get(1).intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
deleted file mode 100644
index bcea5fe..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateClassificationIntentFactoryTest.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.same;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.Intent;
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.AnnotatorModel;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TemplateClassificationIntentFactoryTest {
-
-    private static final String TEXT = "text";
-    private static final String TITLE_WITHOUT_ENTITY = "Map";
-    private static final String DESCRIPTION = "Opens in Maps";
-    private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open Map";
-    private static final String ACTION = Intent.ACTION_VIEW;
-
-    @Mock
-    private ClassificationIntentFactory mFallback;
-    private TemplateClassificationIntentFactory mTemplateClassificationIntentFactory;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mTemplateClassificationIntentFactory = new TemplateClassificationIntentFactory(
-                new TemplateIntentFactory(),
-                mFallback);
-    }
-
-    @Test
-    public void create_foreignText() {
-        AnnotatorModel.ClassificationResult classificationResult =
-                new AnnotatorModel.ClassificationResult(
-                        TextClassifier.TYPE_ADDRESS,
-                        1.0f,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        createRemoteActionTemplates(),
-                        0L,
-                        0L,
-                        0d);
-
-        List<LabeledIntent> intents =
-                mTemplateClassificationIntentFactory.create(
-                        InstrumentationRegistry.getContext(),
-                        TEXT,
-                        /* foreignText */ true,
-                        null,
-                        classificationResult);
-
-        assertThat(intents).hasSize(2);
-        LabeledIntent labeledIntent = intents.get(0);
-        assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
-        Intent intent = labeledIntent.intent;
-        assertThat(intent.getAction()).isEqualTo(ACTION);
-
-        labeledIntent = intents.get(1);
-        intent = labeledIntent.intent;
-        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE);
-    }
-
-    @Test
-    public void create_notForeignText() {
-        AnnotatorModel.ClassificationResult classificationResult =
-                new AnnotatorModel.ClassificationResult(
-                        TextClassifier.TYPE_ADDRESS,
-                        1.0f,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        createRemoteActionTemplates(),
-                        0L,
-                        0L,
-                        0d);
-
-        List<LabeledIntent> intents =
-                mTemplateClassificationIntentFactory.create(
-                        InstrumentationRegistry.getContext(),
-                        TEXT,
-                        /* foreignText */ false,
-                        null,
-                        classificationResult);
-
-        assertThat(intents).hasSize(1);
-        LabeledIntent labeledIntent = intents.get(0);
-        assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
-        Intent intent = labeledIntent.intent;
-        assertThat(intent.getAction()).isEqualTo(ACTION);
-    }
-
-    @Test
-    public void create_nullTemplate() {
-        AnnotatorModel.ClassificationResult classificationResult =
-                new AnnotatorModel.ClassificationResult(
-                        TextClassifier.TYPE_ADDRESS,
-                        1.0f,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        0L,
-                        0L,
-                        0d);
-
-        mTemplateClassificationIntentFactory.create(
-                InstrumentationRegistry.getContext(),
-                TEXT,
-                /* foreignText */ false,
-                null,
-                classificationResult);
-
-
-        verify(mFallback).create(
-                same(InstrumentationRegistry.getContext()), eq(TEXT), eq(false), eq(null),
-                same(classificationResult));
-    }
-
-    @Test
-    public void create_emptyResult() {
-        AnnotatorModel.ClassificationResult classificationResult =
-                new AnnotatorModel.ClassificationResult(
-                        TextClassifier.TYPE_ADDRESS,
-                        1.0f,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        new RemoteActionTemplate[0],
-                        0L,
-                        0L,
-                        0d);
-
-        mTemplateClassificationIntentFactory.create(
-                InstrumentationRegistry.getContext(),
-                TEXT,
-                /* foreignText */ false,
-                null,
-                classificationResult);
-
-
-        verify(mFallback, never()).create(
-                any(Context.class), eq(TEXT), eq(false), eq(null),
-                any(AnnotatorModel.ClassificationResult.class));
-    }
-
-
-    private static RemoteActionTemplate[] createRemoteActionTemplates() {
-        return new RemoteActionTemplate[]{
-                new RemoteActionTemplate(
-                        TITLE_WITHOUT_ENTITY,
-                        null,
-                        DESCRIPTION,
-                        DESCRIPTION_WITH_APP_NAME,
-                        ACTION,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        null
-                )
-        };
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java
deleted file mode 100644
index a33c358..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/intent/TemplateIntentFactoryTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.textclassifier.intent;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Intent;
-import android.net.Uri;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.google.android.textclassifier.NamedVariant;
-import com.google.android.textclassifier.RemoteActionTemplate;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TemplateIntentFactoryTest {
-
-    private static final String TEXT = "text";
-    private static final String TITLE_WITHOUT_ENTITY = "Map";
-    private static final String TITLE_WITH_ENTITY = "Map NW14D1";
-    private static final String DESCRIPTION = "Check the map";
-    private static final String DESCRIPTION_WITH_APP_NAME = "Use %1$s to open map";
-    private static final String ACTION = Intent.ACTION_VIEW;
-    private static final String DATA = Uri.parse("http://www.android.com").toString();
-    private static final String TYPE = "text/html";
-    private static final Integer FLAG = Intent.FLAG_ACTIVITY_NEW_TASK;
-    private static final String[] CATEGORY =
-            new String[]{Intent.CATEGORY_DEFAULT, Intent.CATEGORY_APP_BROWSER};
-    private static final String PACKAGE_NAME = "pkg.name";
-    private static final String KEY_ONE = "key1";
-    private static final String VALUE_ONE = "value1";
-    private static final String KEY_TWO = "key2";
-    private static final int VALUE_TWO = 42;
-
-    private static final NamedVariant[] NAMED_VARIANTS = new NamedVariant[]{
-            new NamedVariant(KEY_ONE, VALUE_ONE),
-            new NamedVariant(KEY_TWO, VALUE_TWO)
-    };
-    private static final Integer REQUEST_CODE = 10;
-
-    private TemplateIntentFactory mTemplateIntentFactory;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mTemplateIntentFactory = new TemplateIntentFactory();
-    }
-
-    @Test
-    public void create_full() {
-        RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE_WITHOUT_ENTITY,
-                TITLE_WITH_ENTITY,
-                DESCRIPTION,
-                DESCRIPTION_WITH_APP_NAME,
-                ACTION,
-                DATA,
-                TYPE,
-                FLAG,
-                CATEGORY,
-                /* packageName */ null,
-                NAMED_VARIANTS,
-                REQUEST_CODE
-        );
-
-        List<LabeledIntent> intents =
-                mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
-
-        assertThat(intents).hasSize(1);
-        LabeledIntent labeledIntent = intents.get(0);
-        assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
-        assertThat(labeledIntent.titleWithEntity).isEqualTo(TITLE_WITH_ENTITY);
-        assertThat(labeledIntent.description).isEqualTo(DESCRIPTION);
-        assertThat(labeledIntent.descriptionWithAppName).isEqualTo(DESCRIPTION_WITH_APP_NAME);
-        assertThat(labeledIntent.requestCode).isEqualTo(REQUEST_CODE);
-        Intent intent = labeledIntent.intent;
-        assertThat(intent.getAction()).isEqualTo(ACTION);
-        assertThat(intent.getData().toString()).isEqualTo(DATA);
-        assertThat(intent.getType()).isEqualTo(TYPE);
-        assertThat(intent.getFlags()).isEqualTo(FLAG);
-        assertThat(intent.getCategories()).containsExactly((Object[]) CATEGORY);
-        assertThat(intent.getPackage()).isNull();
-        assertThat(intent.getStringExtra(KEY_ONE)).isEqualTo(VALUE_ONE);
-        assertThat(intent.getIntExtra(KEY_TWO, 0)).isEqualTo(VALUE_TWO);
-    }
-
-    @Test
-    public void normalizesScheme() {
-        RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE_WITHOUT_ENTITY,
-                TITLE_WITH_ENTITY,
-                DESCRIPTION,
-                DESCRIPTION_WITH_APP_NAME,
-                ACTION,
-                "HTTp://www.android.com",
-                TYPE,
-                FLAG,
-                CATEGORY,
-                /* packageName */ null,
-                NAMED_VARIANTS,
-                REQUEST_CODE
-        );
-
-        List<LabeledIntent> intents =
-                mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
-        String data = intents.get(0).intent.getData().toString();
-        assertThat(data).isEqualTo("http://www.android.com");
-    }
-
-    @Test
-    public void create_minimal() {
-        RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE_WITHOUT_ENTITY,
-                null,
-                DESCRIPTION,
-                null,
-                ACTION,
-                null,
-                null,
-                null,
-                null,
-                null,
-                null,
-                null
-        );
-
-        List<LabeledIntent> intents =
-                mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate});
-
-        assertThat(intents).hasSize(1);
-        LabeledIntent labeledIntent = intents.get(0);
-        assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY);
-        assertThat(labeledIntent.titleWithEntity).isNull();
-        assertThat(labeledIntent.description).isEqualTo(DESCRIPTION);
-        assertThat(labeledIntent.requestCode).isEqualTo(
-                LabeledIntent.DEFAULT_REQUEST_CODE);
-        Intent intent = labeledIntent.intent;
-        assertThat(intent.getAction()).isEqualTo(ACTION);
-        assertThat(intent.getData()).isNull();
-        assertThat(intent.getType()).isNull();
-        assertThat(intent.getFlags()).isEqualTo(0);
-        assertThat(intent.getCategories()).isNull();
-        assertThat(intent.getPackage()).isNull();
-    }
-
-    @Test
-    public void invalidTemplate_nullTemplate() {
-        RemoteActionTemplate remoteActionTemplate = null;
-
-        List<LabeledIntent> intents =
-                mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
-        assertThat(intents).isEmpty();
-    }
-
-    @Test
-    public void invalidTemplate_nonEmptyPackageName() {
-        RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE_WITHOUT_ENTITY,
-                TITLE_WITH_ENTITY,
-                DESCRIPTION,
-                DESCRIPTION_WITH_APP_NAME,
-                ACTION,
-                DATA,
-                TYPE,
-                FLAG,
-                CATEGORY,
-                PACKAGE_NAME,
-                NAMED_VARIANTS,
-                REQUEST_CODE
-        );
-
-        List<LabeledIntent> intents =
-                mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
-        assertThat(intents).isEmpty();
-    }
-
-    @Test
-    public void invalidTemplate_emptyTitle() {
-        RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                null,
-                null,
-                DESCRIPTION,
-                DESCRIPTION_WITH_APP_NAME,
-                ACTION,
-                null,
-                null,
-                null,
-                null,
-                null,
-                null,
-                null
-        );
-
-        List<LabeledIntent> intents =
-                mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
-        assertThat(intents).isEmpty();
-    }
-
-    @Test
-    public void invalidTemplate_emptyDescription() {
-        RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE_WITHOUT_ENTITY,
-                TITLE_WITH_ENTITY,
-                null,
-                null,
-                ACTION,
-                null,
-                null,
-                null,
-                null,
-                null,
-                null,
-                null
-        );
-
-        List<LabeledIntent> intents =
-                mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
-        assertThat(intents).isEmpty();
-    }
-
-    @Test
-    public void invalidTemplate_emptyIntentAction() {
-        RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate(
-                TITLE_WITHOUT_ENTITY,
-                TITLE_WITH_ENTITY,
-                DESCRIPTION,
-                DESCRIPTION_WITH_APP_NAME,
-                null,
-                null,
-                null,
-                null,
-                null,
-                null,
-                null,
-                null
-        );
-
-        List<LabeledIntent> intents =
-                mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate});
-
-        assertThat(intents).isEmpty();
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
deleted file mode 100644
index 5e8e582..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.logging;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-
-import android.metrics.LogMaker;
-import android.util.ArrayMap;
-import android.view.textclassifier.GenerateLinksLogger;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextLinks;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class GenerateLinksLoggerTest {
-
-    private static final String PACKAGE_NAME = "packageName";
-    private static final String ZERO = "0";
-    private static final int LATENCY_MS = 123;
-
-    @Test
-    public void testLogGenerateLinks() {
-        final String phoneText = "+12122537077";
-        final String addressText = "1600 Amphitheater Parkway, Mountain View, CA";
-        final String testText = "The number is " + phoneText + ", the address is " + addressText;
-        final int phoneOffset = testText.indexOf(phoneText);
-        final int addressOffset = testText.indexOf(addressText);
-
-        final Map<String, Float> phoneEntityScores = new ArrayMap<>();
-        phoneEntityScores.put(TextClassifier.TYPE_PHONE, 0.9f);
-        phoneEntityScores.put(TextClassifier.TYPE_OTHER, 0.1f);
-        final Map<String, Float> addressEntityScores = new ArrayMap<>();
-        addressEntityScores.put(TextClassifier.TYPE_ADDRESS, 1f);
-
-        TextLinks links = new TextLinks.Builder(testText)
-                .addLink(phoneOffset, phoneOffset + phoneText.length(), phoneEntityScores)
-                .addLink(addressOffset, addressOffset + addressText.length(), addressEntityScores)
-                .build();
-
-        // Set up mock.
-        MetricsLogger metricsLogger = mock(MetricsLogger.class);
-        ArgumentCaptor<LogMaker> logMakerCapture = ArgumentCaptor.forClass(LogMaker.class);
-        doNothing().when(metricsLogger).write(logMakerCapture.capture());
-
-        // Generate the log.
-        GenerateLinksLogger logger = new GenerateLinksLogger(1 /* sampleRate */, metricsLogger);
-        logger.logGenerateLinks(testText, links, PACKAGE_NAME, LATENCY_MS);
-
-        // Validate.
-        List<LogMaker> logs = logMakerCapture.getAllValues();
-        assertEquals(3, logs.size());
-        assertHasLog(logs, "" /* entityType */, 2, phoneText.length() + addressText.length(),
-                testText.length());
-        assertHasLog(logs, TextClassifier.TYPE_ADDRESS, 1, addressText.length(),
-                testText.length());
-        assertHasLog(logs, TextClassifier.TYPE_PHONE, 1, phoneText.length(),
-                testText.length());
-    }
-
-    private void assertHasLog(List<LogMaker> logs, String entityType, int numLinks,
-            int linkTextLength, int textLength) {
-        for (LogMaker log : logs) {
-            if (!entityType.equals(getEntityType(log))) {
-                continue;
-            }
-            assertEquals(PACKAGE_NAME, log.getPackageName());
-            assertNotNull(Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID)));
-            assertEquals(numLinks, getIntValue(log, MetricsEvent.FIELD_LINKIFY_NUM_LINKS));
-            assertEquals(linkTextLength, getIntValue(log, MetricsEvent.FIELD_LINKIFY_LINK_LENGTH));
-            assertEquals(textLength, getIntValue(log, MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH));
-            assertEquals(LATENCY_MS, getIntValue(log, MetricsEvent.FIELD_LINKIFY_LATENCY));
-            return;
-        }
-        fail("No log for entity type \"" + entityType + "\"");
-    }
-
-    private static String getEntityType(LogMaker log) {
-        return Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "");
-    }
-
-    private static int getIntValue(LogMaker log, int eventField) {
-        return Integer.parseInt(Objects.toString(log.getTaggedData(eventField), ZERO));
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java
deleted file mode 100644
index 321a7f2..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/logging/SmartSelectionEventTrackerTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.view.textclassifier.logging;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.google.common.truth.Truth;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SmartSelectionEventTrackerTest {
-
-    @Test
-    public void getVersionInfo_valid() {
-        String signature = "a|702|b";
-        String versionInfo = SmartSelectionEventTracker.SelectionEvent.getVersionInfo(signature);
-        Truth.assertThat(versionInfo).isEqualTo("702");
-    }
-
-    @Test
-    public void getVersionInfo_invalid() {
-        String signature = "|702";
-        String versionInfo = SmartSelectionEventTracker.SelectionEvent.getVersionInfo(signature);
-        Truth.assertThat(versionInfo).isEmpty();
-    }
-}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
deleted file mode 100644
index 2c540e5..0000000
--- a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier.logging;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.CONVERSATION_ACTIONS;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_SCORE;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_WIDGET_TYPE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.metrics.LogMaker;
-import android.view.textclassifier.ConversationAction;
-import android.view.textclassifier.TextClassificationContext;
-import android.view.textclassifier.TextClassifierEvent;
-import android.view.textclassifier.TextClassifierEventTronLogger;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.logging.MetricsLogger;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class TextClassifierEventTronLoggerTest {
-    private static final String WIDGET_TYPE = "notification";
-    private static final String PACKAGE_NAME = "pkg";
-
-    @Mock
-    private MetricsLogger mMetricsLogger;
-    private TextClassifierEventTronLogger mTextClassifierEventTronLogger;
-
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mTextClassifierEventTronLogger = new TextClassifierEventTronLogger(mMetricsLogger);
-    }
-
-    @Test
-    public void testWriteEvent() {
-        TextClassificationContext textClassificationContext =
-                new TextClassificationContext.Builder(PACKAGE_NAME, WIDGET_TYPE)
-                        .build();
-        TextClassifierEvent.ConversationActionsEvent textClassifierEvent =
-                new TextClassifierEvent.ConversationActionsEvent.Builder(
-                        TextClassifierEvent.TYPE_SMART_ACTION)
-                        .setEntityTypes(ConversationAction.TYPE_CALL_PHONE)
-                        .setScores(0.5f)
-                        .setEventContext(textClassificationContext)
-                        .build();
-
-        mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
-
-        ArgumentCaptor<LogMaker> captor = ArgumentCaptor.forClass(LogMaker.class);
-        Mockito.verify(mMetricsLogger).write(captor.capture());
-        LogMaker logMaker = captor.getValue();
-        assertThat(logMaker.getCategory()).isEqualTo(CONVERSATION_ACTIONS);
-        assertThat(logMaker.getSubtype()).isEqualTo(ACTION_TEXT_SELECTION_SMART_SHARE);
-        assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_FIRST_ENTITY_TYPE))
-                .isEqualTo(ConversationAction.TYPE_CALL_PHONE);
-        assertThat((float) logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_SCORE))
-                .isWithin(0.00001f).of(0.5f);
-        // Never write event time.
-        assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME)).isNull();
-        assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
-        assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_WIDGET_TYPE))
-                .isEqualTo(WIDGET_TYPE);
-
-    }
-
-    @Test
-    public void testWriteEvent_unsupportedCategory() {
-        TextClassifierEvent.TextSelectionEvent textClassifierEvent =
-                new TextClassifierEvent.TextSelectionEvent.Builder(
-                        TextClassifierEvent.TYPE_SMART_ACTION)
-                        .build();
-
-        mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
-
-        Mockito.verify(mMetricsLogger, Mockito.never()).write(Mockito.any(LogMaker.class));
-    }
-}
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index abfb4fb..8cf146e 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -48,6 +48,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
@@ -56,6 +57,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -87,6 +89,7 @@
     static {
         MANAGED_PROFILE_INFO.id = 10;
         MANAGED_PROFILE_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE;
+        MANAGED_PROFILE_INFO.userType = UserManager.USER_TYPE_PROFILE_MANAGED;
     }
 
     private static UserInfo CURRENT_USER_INFO = new UserInfo();
@@ -116,12 +119,21 @@
 
     private Context mContext;
     public static final String PHONE_NUMBER = "123-456-789";
+    private int mDeviceProvisionedInitialValue;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mContext = InstrumentationRegistry.getTargetContext();
         sInjector = spy(new TestInjector());
+        mDeviceProvisionedInitialValue = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, /* def= */ 0);
+    }
+
+    @After
+    public void tearDown() {
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+                mDeviceProvisionedInitialValue);
     }
 
     @Test
@@ -533,6 +545,22 @@
     }
 
     @Test
+    public void shouldSkipDisclosure_duringDeviceSetup() throws RemoteException {
+        setupShouldSkipDisclosureTest();
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+                /* value= */ 0);
+        Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class)
+                .setAction(Intent.ACTION_VIEW)
+                .addCategory(Intent.CATEGORY_BROWSABLE)
+                .setData(Uri.fromParts("http", "apache.org", null));
+
+        mActivityRule.launchActivity(intent);
+
+        verify(mIPm).canForwardTo(any(), any(), anyInt(), anyInt());
+        verify(sInjector, never()).showToast(anyInt(), anyInt());
+    }
+
+    @Test
     public void forwardToManagedProfile_LoggingTest() throws Exception {
         sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
 
@@ -590,6 +618,8 @@
         sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
         sActivityName = "MyTestActivity";
         sPackageName = "test.package.name";
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
+                /* value= */ 1);
         when(mApplicationInfo.isSystemApp()).thenReturn(true);
         // Managed profile exists.
         List<UserInfo> profiles = new ArrayList<>();
diff --git a/data/etc/com.android.launcher3.xml b/data/etc/com.android.launcher3.xml
index 337e153..17d614e 100644
--- a/data/etc/com.android.launcher3.xml
+++ b/data/etc/com.android.launcher3.xml
@@ -19,5 +19,6 @@
         <permission name="android.permission.BIND_APPWIDGET"/>
         <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
         <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 487a6e9..5466ac8 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -62,6 +62,10 @@
         <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.launcher3">
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.location.fused">
         <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
         <permission name="android.permission.UPDATE_DEVICE_STATS"/>
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index bc933ff..670ef5e 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -82,6 +82,63 @@
         }
     }
 
+    private static void addSids(KeymasterArguments args, UserAuthArgs spec) {
+        // If both biometric and credential are accepted, then just use the root sid from gatekeeper
+        if (spec.getUserAuthenticationType() == (KeyProperties.AUTH_BIOMETRIC_STRONG
+                                                 | KeyProperties.AUTH_DEVICE_CREDENTIAL)) {
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+                        KeymasterArguments.toUint64(spec.getBoundToSpecificSecureUserId()));
+            } else {
+                // The key is authorized for use for the specified amount of time after the user has
+                // authenticated. Whatever unlocks the secure lock screen should authorize this key.
+                args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+                        KeymasterArguments.toUint64(getRootSid()));
+            }
+        } else {
+            List<Long> sids = new ArrayList<>();
+            if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_BIOMETRIC_STRONG) != 0) {
+                final BiometricManager bm = KeyStore.getApplicationContext()
+                        .getSystemService(BiometricManager.class);
+
+                // TODO: Restore permission check in getAuthenticatorIds once the ID is no longer
+                // needed here.
+
+                final long[] biometricSids = bm.getAuthenticatorIds();
+
+                if (biometricSids.length == 0) {
+                    throw new IllegalStateException(
+                            "At least one biometric must be enrolled to create keys requiring user"
+                            + " authentication for every use");
+                }
+
+                if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                    sids.add(spec.getBoundToSpecificSecureUserId());
+                } else if (spec.isInvalidatedByBiometricEnrollment()) {
+                    // The biometric-only SIDs will change on biometric enrollment or removal of all
+                    // enrolled templates, invalidating the key.
+                    for (long sid : biometricSids) {
+                        sids.add(sid);
+                    }
+                } else {
+                    // The root SID will *not* change on fingerprint enrollment, or removal of all
+                    // enrolled fingerprints, allowing the key to remain valid.
+                    sids.add(getRootSid());
+                }
+            } else if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_DEVICE_CREDENTIAL)
+                            != 0) {
+                sids.add(getRootSid());
+            } else {
+                throw new IllegalStateException("Invalid or no authentication type specified.");
+            }
+
+            for (int i = 0; i < sids.size(); i++) {
+                args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
+                        KeymasterArguments.toUint64(sids.get(i)));
+            }
+        }
+    }
+
     /**
      * Adds keymaster arguments to express the key's authorization policy supported by user
      * authentication.
@@ -114,40 +171,7 @@
 
         if (spec.getUserAuthenticationValidityDurationSeconds() == 0) {
             // Every use of this key needs to be authorized by the user.
-            final BiometricManager bm = KeyStore.getApplicationContext()
-                    .getSystemService(BiometricManager.class);
-
-            // TODO: Restore permission check in getAuthenticatorIds once the ID is no longer
-            // needed here.
-
-            final long[] biometricSids = bm.getAuthenticatorIds();
-
-            if (biometricSids.length == 0) {
-                throw new IllegalStateException(
-                        "At least one biometric must be enrolled to create keys requiring user"
-                        + " authentication for every use");
-            }
-
-            List<Long> sids = new ArrayList<>();
-            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
-                sids.add(spec.getBoundToSpecificSecureUserId());
-            } else if (spec.isInvalidatedByBiometricEnrollment()) {
-                // The biometric-only SIDs will change on biometric enrollment or removal of all
-                // enrolled templates, invalidating the key.
-                for (long sid : biometricSids) {
-                    sids.add(sid);
-                }
-            } else {
-                // The root SID will *not* change on fingerprint enrollment, or removal of all
-                // enrolled fingerprints, allowing the key to remain valid.
-                sids.add(getRootSid());
-            }
-
-            for (int i = 0; i < sids.size(); i++) {
-                args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
-                        KeymasterArguments.toUint64(sids.get(i)));
-            }
-
+            addSids(args, spec);
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
 
             if (spec.isUserAuthenticationValidWhileOnBody()) {
@@ -155,16 +179,7 @@
                         + "supported for keys requiring fingerprint authentication");
             }
         } else {
-            long sid;
-            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = spec.getBoundToSpecificSecureUserId();
-            } else {
-                // The key is authorized for use for the specified amount of time after the user has
-                // authenticated. Whatever unlocks the secure lock screen should authorize this key.
-                sid = getRootSid();
-            }
-            args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
-                    KeymasterArguments.toUint64(sid));
+            addSids(args, spec);
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
                     spec.getUserAuthenticationValidityDurationSeconds());
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index e748bd8..6c6c5c9 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -15,1474 +15,1478 @@
     /* 11 */ {'C', 'a', 'r', 'i'},
     /* 12 */ {'C', 'h', 'a', 'm'},
     /* 13 */ {'C', 'h', 'e', 'r'},
-    /* 14 */ {'C', 'o', 'p', 't'},
-    /* 15 */ {'C', 'p', 'r', 't'},
-    /* 16 */ {'C', 'y', 'r', 'l'},
-    /* 17 */ {'D', 'e', 'v', 'a'},
-    /* 18 */ {'E', 'g', 'y', 'p'},
-    /* 19 */ {'E', 't', 'h', 'i'},
-    /* 20 */ {'G', 'e', 'o', 'r'},
-    /* 21 */ {'G', 'o', 'n', 'g'},
-    /* 22 */ {'G', 'o', 'n', 'm'},
-    /* 23 */ {'G', 'o', 't', 'h'},
-    /* 24 */ {'G', 'r', 'e', 'k'},
-    /* 25 */ {'G', 'u', 'j', 'r'},
-    /* 26 */ {'G', 'u', 'r', 'u'},
-    /* 27 */ {'H', 'a', 'n', 's'},
-    /* 28 */ {'H', 'a', 'n', 't'},
-    /* 29 */ {'H', 'a', 't', 'r'},
-    /* 30 */ {'H', 'e', 'b', 'r'},
-    /* 31 */ {'H', 'l', 'u', 'w'},
-    /* 32 */ {'H', 'm', 'n', 'g'},
-    /* 33 */ {'H', 'm', 'n', 'p'},
-    /* 34 */ {'I', 't', 'a', 'l'},
-    /* 35 */ {'J', 'p', 'a', 'n'},
-    /* 36 */ {'K', 'a', 'l', 'i'},
-    /* 37 */ {'K', 'a', 'n', 'a'},
-    /* 38 */ {'K', 'h', 'a', 'r'},
-    /* 39 */ {'K', 'h', 'm', 'r'},
-    /* 40 */ {'K', 'n', 'd', 'a'},
-    /* 41 */ {'K', 'o', 'r', 'e'},
-    /* 42 */ {'L', 'a', 'n', 'a'},
-    /* 43 */ {'L', 'a', 'o', 'o'},
-    /* 44 */ {'L', 'a', 't', 'n'},
-    /* 45 */ {'L', 'e', 'p', 'c'},
-    /* 46 */ {'L', 'i', 'n', 'a'},
-    /* 47 */ {'L', 'i', 's', 'u'},
-    /* 48 */ {'L', 'y', 'c', 'i'},
-    /* 49 */ {'L', 'y', 'd', 'i'},
-    /* 50 */ {'M', 'a', 'n', 'd'},
-    /* 51 */ {'M', 'a', 'n', 'i'},
-    /* 52 */ {'M', 'e', 'r', 'c'},
-    /* 53 */ {'M', 'l', 'y', 'm'},
-    /* 54 */ {'M', 'o', 'n', 'g'},
-    /* 55 */ {'M', 'r', 'o', 'o'},
-    /* 56 */ {'M', 'y', 'm', 'r'},
-    /* 57 */ {'N', 'a', 'r', 'b'},
-    /* 58 */ {'N', 'k', 'o', 'o'},
-    /* 59 */ {'N', 's', 'h', 'u'},
-    /* 60 */ {'O', 'g', 'a', 'm'},
-    /* 61 */ {'O', 'r', 'k', 'h'},
-    /* 62 */ {'O', 'r', 'y', 'a'},
-    /* 63 */ {'O', 's', 'g', 'e'},
-    /* 64 */ {'P', 'a', 'u', 'c'},
-    /* 65 */ {'P', 'h', 'l', 'i'},
-    /* 66 */ {'P', 'h', 'n', 'x'},
-    /* 67 */ {'P', 'l', 'r', 'd'},
-    /* 68 */ {'P', 'r', 't', 'i'},
-    /* 69 */ {'R', 'u', 'n', 'r'},
-    /* 70 */ {'S', 'a', 'm', 'r'},
-    /* 71 */ {'S', 'a', 'r', 'b'},
-    /* 72 */ {'S', 'a', 'u', 'r'},
-    /* 73 */ {'S', 'g', 'n', 'w'},
-    /* 74 */ {'S', 'i', 'n', 'h'},
-    /* 75 */ {'S', 'o', 'g', 'd'},
-    /* 76 */ {'S', 'o', 'r', 'a'},
-    /* 77 */ {'S', 'o', 'y', 'o'},
-    /* 78 */ {'S', 'y', 'r', 'c'},
-    /* 79 */ {'T', 'a', 'l', 'e'},
-    /* 80 */ {'T', 'a', 'l', 'u'},
-    /* 81 */ {'T', 'a', 'm', 'l'},
-    /* 82 */ {'T', 'a', 'n', 'g'},
-    /* 83 */ {'T', 'a', 'v', 't'},
-    /* 84 */ {'T', 'e', 'l', 'u'},
-    /* 85 */ {'T', 'f', 'n', 'g'},
-    /* 86 */ {'T', 'h', 'a', 'a'},
-    /* 87 */ {'T', 'h', 'a', 'i'},
-    /* 88 */ {'T', 'i', 'b', 't'},
-    /* 89 */ {'U', 'g', 'a', 'r'},
-    /* 90 */ {'V', 'a', 'i', 'i'},
-    /* 91 */ {'W', 'c', 'h', 'o'},
-    /* 92 */ {'X', 'p', 'e', 'o'},
-    /* 93 */ {'X', 's', 'u', 'x'},
-    /* 94 */ {'Y', 'i', 'i', 'i'},
-    /* 95 */ {'~', '~', '~', 'A'},
-    /* 96 */ {'~', '~', '~', 'B'},
+    /* 14 */ {'C', 'h', 'r', 's'},
+    /* 15 */ {'C', 'o', 'p', 't'},
+    /* 16 */ {'C', 'p', 'r', 't'},
+    /* 17 */ {'C', 'y', 'r', 'l'},
+    /* 18 */ {'D', 'e', 'v', 'a'},
+    /* 19 */ {'E', 'g', 'y', 'p'},
+    /* 20 */ {'E', 't', 'h', 'i'},
+    /* 21 */ {'G', 'e', 'o', 'r'},
+    /* 22 */ {'G', 'o', 'n', 'g'},
+    /* 23 */ {'G', 'o', 'n', 'm'},
+    /* 24 */ {'G', 'o', 't', 'h'},
+    /* 25 */ {'G', 'r', 'e', 'k'},
+    /* 26 */ {'G', 'u', 'j', 'r'},
+    /* 27 */ {'G', 'u', 'r', 'u'},
+    /* 28 */ {'H', 'a', 'n', 's'},
+    /* 29 */ {'H', 'a', 'n', 't'},
+    /* 30 */ {'H', 'a', 't', 'r'},
+    /* 31 */ {'H', 'e', 'b', 'r'},
+    /* 32 */ {'H', 'l', 'u', 'w'},
+    /* 33 */ {'H', 'm', 'n', 'g'},
+    /* 34 */ {'H', 'm', 'n', 'p'},
+    /* 35 */ {'I', 't', 'a', 'l'},
+    /* 36 */ {'J', 'p', 'a', 'n'},
+    /* 37 */ {'K', 'a', 'l', 'i'},
+    /* 38 */ {'K', 'a', 'n', 'a'},
+    /* 39 */ {'K', 'h', 'a', 'r'},
+    /* 40 */ {'K', 'h', 'm', 'r'},
+    /* 41 */ {'K', 'i', 't', 's'},
+    /* 42 */ {'K', 'n', 'd', 'a'},
+    /* 43 */ {'K', 'o', 'r', 'e'},
+    /* 44 */ {'L', 'a', 'n', 'a'},
+    /* 45 */ {'L', 'a', 'o', 'o'},
+    /* 46 */ {'L', 'a', 't', 'n'},
+    /* 47 */ {'L', 'e', 'p', 'c'},
+    /* 48 */ {'L', 'i', 'n', 'a'},
+    /* 49 */ {'L', 'i', 's', 'u'},
+    /* 50 */ {'L', 'y', 'c', 'i'},
+    /* 51 */ {'L', 'y', 'd', 'i'},
+    /* 52 */ {'M', 'a', 'n', 'd'},
+    /* 53 */ {'M', 'a', 'n', 'i'},
+    /* 54 */ {'M', 'e', 'r', 'c'},
+    /* 55 */ {'M', 'l', 'y', 'm'},
+    /* 56 */ {'M', 'o', 'n', 'g'},
+    /* 57 */ {'M', 'r', 'o', 'o'},
+    /* 58 */ {'M', 'y', 'm', 'r'},
+    /* 59 */ {'N', 'a', 'r', 'b'},
+    /* 60 */ {'N', 'k', 'o', 'o'},
+    /* 61 */ {'N', 's', 'h', 'u'},
+    /* 62 */ {'O', 'g', 'a', 'm'},
+    /* 63 */ {'O', 'r', 'k', 'h'},
+    /* 64 */ {'O', 'r', 'y', 'a'},
+    /* 65 */ {'O', 's', 'g', 'e'},
+    /* 66 */ {'P', 'a', 'u', 'c'},
+    /* 67 */ {'P', 'h', 'l', 'i'},
+    /* 68 */ {'P', 'h', 'n', 'x'},
+    /* 69 */ {'P', 'l', 'r', 'd'},
+    /* 70 */ {'P', 'r', 't', 'i'},
+    /* 71 */ {'R', 'u', 'n', 'r'},
+    /* 72 */ {'S', 'a', 'm', 'r'},
+    /* 73 */ {'S', 'a', 'r', 'b'},
+    /* 74 */ {'S', 'a', 'u', 'r'},
+    /* 75 */ {'S', 'g', 'n', 'w'},
+    /* 76 */ {'S', 'i', 'n', 'h'},
+    /* 77 */ {'S', 'o', 'g', 'd'},
+    /* 78 */ {'S', 'o', 'r', 'a'},
+    /* 79 */ {'S', 'o', 'y', 'o'},
+    /* 80 */ {'S', 'y', 'r', 'c'},
+    /* 81 */ {'T', 'a', 'l', 'e'},
+    /* 82 */ {'T', 'a', 'l', 'u'},
+    /* 83 */ {'T', 'a', 'm', 'l'},
+    /* 84 */ {'T', 'a', 'n', 'g'},
+    /* 85 */ {'T', 'a', 'v', 't'},
+    /* 86 */ {'T', 'e', 'l', 'u'},
+    /* 87 */ {'T', 'f', 'n', 'g'},
+    /* 88 */ {'T', 'h', 'a', 'a'},
+    /* 89 */ {'T', 'h', 'a', 'i'},
+    /* 90 */ {'T', 'i', 'b', 't'},
+    /* 91 */ {'U', 'g', 'a', 'r'},
+    /* 92 */ {'V', 'a', 'i', 'i'},
+    /* 93 */ {'W', 'c', 'h', 'o'},
+    /* 94 */ {'X', 'p', 'e', 'o'},
+    /* 95 */ {'X', 's', 'u', 'x'},
+    /* 96 */ {'Y', 'i', 'i', 'i'},
+    /* 97 */ {'~', '~', '~', 'A'},
+    /* 98 */ {'~', '~', '~', 'B'},
 };
 
 
 const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
-    {0x61610000u, 44u}, // aa -> Latn
-    {0xA0000000u, 44u}, // aai -> Latn
-    {0xA8000000u, 44u}, // aak -> Latn
-    {0xD0000000u, 44u}, // aau -> Latn
-    {0x61620000u, 16u}, // ab -> Cyrl
-    {0xA0200000u, 44u}, // abi -> Latn
-    {0xC0200000u, 16u}, // abq -> Cyrl
-    {0xC4200000u, 44u}, // abr -> Latn
-    {0xCC200000u, 44u}, // abt -> Latn
-    {0xE0200000u, 44u}, // aby -> Latn
-    {0x8C400000u, 44u}, // acd -> Latn
-    {0x90400000u, 44u}, // ace -> Latn
-    {0x9C400000u, 44u}, // ach -> Latn
-    {0x80600000u, 44u}, // ada -> Latn
-    {0x90600000u, 44u}, // ade -> Latn
-    {0xA4600000u, 44u}, // adj -> Latn
-    {0xBC600000u, 88u}, // adp -> Tibt
-    {0xE0600000u, 16u}, // ady -> Cyrl
-    {0xE4600000u, 44u}, // adz -> Latn
+    {0x61610000u, 46u}, // aa -> Latn
+    {0xA0000000u, 46u}, // aai -> Latn
+    {0xA8000000u, 46u}, // aak -> Latn
+    {0xD0000000u, 46u}, // aau -> Latn
+    {0x61620000u, 17u}, // ab -> Cyrl
+    {0xA0200000u, 46u}, // abi -> Latn
+    {0xC0200000u, 17u}, // abq -> Cyrl
+    {0xC4200000u, 46u}, // abr -> Latn
+    {0xCC200000u, 46u}, // abt -> Latn
+    {0xE0200000u, 46u}, // aby -> Latn
+    {0x8C400000u, 46u}, // acd -> Latn
+    {0x90400000u, 46u}, // ace -> Latn
+    {0x9C400000u, 46u}, // ach -> Latn
+    {0x80600000u, 46u}, // ada -> Latn
+    {0x90600000u, 46u}, // ade -> Latn
+    {0xA4600000u, 46u}, // adj -> Latn
+    {0xBC600000u, 90u}, // adp -> Tibt
+    {0xE0600000u, 17u}, // ady -> Cyrl
+    {0xE4600000u, 46u}, // adz -> Latn
     {0x61650000u,  4u}, // ae -> Avst
     {0x84800000u,  1u}, // aeb -> Arab
-    {0xE0800000u, 44u}, // aey -> Latn
-    {0x61660000u, 44u}, // af -> Latn
-    {0x88C00000u, 44u}, // agc -> Latn
-    {0x8CC00000u, 44u}, // agd -> Latn
-    {0x98C00000u, 44u}, // agg -> Latn
-    {0xB0C00000u, 44u}, // agm -> Latn
-    {0xB8C00000u, 44u}, // ago -> Latn
-    {0xC0C00000u, 44u}, // agq -> Latn
-    {0x80E00000u, 44u}, // aha -> Latn
-    {0xACE00000u, 44u}, // ahl -> Latn
+    {0xE0800000u, 46u}, // aey -> Latn
+    {0x61660000u, 46u}, // af -> Latn
+    {0x88C00000u, 46u}, // agc -> Latn
+    {0x8CC00000u, 46u}, // agd -> Latn
+    {0x98C00000u, 46u}, // agg -> Latn
+    {0xB0C00000u, 46u}, // agm -> Latn
+    {0xB8C00000u, 46u}, // ago -> Latn
+    {0xC0C00000u, 46u}, // agq -> Latn
+    {0x80E00000u, 46u}, // aha -> Latn
+    {0xACE00000u, 46u}, // ahl -> Latn
     {0xB8E00000u,  0u}, // aho -> Ahom
-    {0x99200000u, 44u}, // ajg -> Latn
-    {0x616B0000u, 44u}, // ak -> Latn
-    {0xA9400000u, 93u}, // akk -> Xsux
-    {0x81600000u, 44u}, // ala -> Latn
-    {0xA1600000u, 44u}, // ali -> Latn
-    {0xB5600000u, 44u}, // aln -> Latn
-    {0xCD600000u, 16u}, // alt -> Cyrl
-    {0x616D0000u, 19u}, // am -> Ethi
-    {0xB1800000u, 44u}, // amm -> Latn
-    {0xB5800000u, 44u}, // amn -> Latn
-    {0xB9800000u, 44u}, // amo -> Latn
-    {0xBD800000u, 44u}, // amp -> Latn
-    {0x616E0000u, 44u}, // an -> Latn
-    {0x89A00000u, 44u}, // anc -> Latn
-    {0xA9A00000u, 44u}, // ank -> Latn
-    {0xB5A00000u, 44u}, // ann -> Latn
-    {0xE1A00000u, 44u}, // any -> Latn
-    {0xA5C00000u, 44u}, // aoj -> Latn
-    {0xB1C00000u, 44u}, // aom -> Latn
-    {0xE5C00000u, 44u}, // aoz -> Latn
+    {0x99200000u, 46u}, // ajg -> Latn
+    {0x616B0000u, 46u}, // ak -> Latn
+    {0xA9400000u, 95u}, // akk -> Xsux
+    {0x81600000u, 46u}, // ala -> Latn
+    {0xA1600000u, 46u}, // ali -> Latn
+    {0xB5600000u, 46u}, // aln -> Latn
+    {0xCD600000u, 17u}, // alt -> Cyrl
+    {0x616D0000u, 20u}, // am -> Ethi
+    {0xB1800000u, 46u}, // amm -> Latn
+    {0xB5800000u, 46u}, // amn -> Latn
+    {0xB9800000u, 46u}, // amo -> Latn
+    {0xBD800000u, 46u}, // amp -> Latn
+    {0x616E0000u, 46u}, // an -> Latn
+    {0x89A00000u, 46u}, // anc -> Latn
+    {0xA9A00000u, 46u}, // ank -> Latn
+    {0xB5A00000u, 46u}, // ann -> Latn
+    {0xE1A00000u, 46u}, // any -> Latn
+    {0xA5C00000u, 46u}, // aoj -> Latn
+    {0xB1C00000u, 46u}, // aom -> Latn
+    {0xE5C00000u, 46u}, // aoz -> Latn
     {0x89E00000u,  1u}, // apc -> Arab
     {0x8DE00000u,  1u}, // apd -> Arab
-    {0x91E00000u, 44u}, // ape -> Latn
-    {0xC5E00000u, 44u}, // apr -> Latn
-    {0xC9E00000u, 44u}, // aps -> Latn
-    {0xE5E00000u, 44u}, // apz -> Latn
+    {0x91E00000u, 46u}, // ape -> Latn
+    {0xC5E00000u, 46u}, // apr -> Latn
+    {0xC9E00000u, 46u}, // aps -> Latn
+    {0xE5E00000u, 46u}, // apz -> Latn
     {0x61720000u,  1u}, // ar -> Arab
-    {0x61725842u, 96u}, // ar-XB -> ~~~B
+    {0x61725842u, 98u}, // ar-XB -> ~~~B
     {0x8A200000u,  2u}, // arc -> Armi
-    {0x9E200000u, 44u}, // arh -> Latn
-    {0xB6200000u, 44u}, // arn -> Latn
-    {0xBA200000u, 44u}, // aro -> Latn
+    {0x9E200000u, 46u}, // arh -> Latn
+    {0xB6200000u, 46u}, // arn -> Latn
+    {0xBA200000u, 46u}, // aro -> Latn
     {0xC2200000u,  1u}, // arq -> Arab
     {0xCA200000u,  1u}, // ars -> Arab
     {0xE2200000u,  1u}, // ary -> Arab
     {0xE6200000u,  1u}, // arz -> Arab
     {0x61730000u,  7u}, // as -> Beng
-    {0x82400000u, 44u}, // asa -> Latn
-    {0x92400000u, 73u}, // ase -> Sgnw
-    {0x9A400000u, 44u}, // asg -> Latn
-    {0xBA400000u, 44u}, // aso -> Latn
-    {0xCE400000u, 44u}, // ast -> Latn
-    {0x82600000u, 44u}, // ata -> Latn
-    {0x9A600000u, 44u}, // atg -> Latn
-    {0xA6600000u, 44u}, // atj -> Latn
-    {0xE2800000u, 44u}, // auy -> Latn
-    {0x61760000u, 16u}, // av -> Cyrl
+    {0x82400000u, 46u}, // asa -> Latn
+    {0x92400000u, 75u}, // ase -> Sgnw
+    {0x9A400000u, 46u}, // asg -> Latn
+    {0xBA400000u, 46u}, // aso -> Latn
+    {0xCE400000u, 46u}, // ast -> Latn
+    {0x82600000u, 46u}, // ata -> Latn
+    {0x9A600000u, 46u}, // atg -> Latn
+    {0xA6600000u, 46u}, // atj -> Latn
+    {0xE2800000u, 46u}, // auy -> Latn
+    {0x61760000u, 17u}, // av -> Cyrl
     {0xAEA00000u,  1u}, // avl -> Arab
-    {0xB6A00000u, 44u}, // avn -> Latn
-    {0xCEA00000u, 44u}, // avt -> Latn
-    {0xD2A00000u, 44u}, // avu -> Latn
-    {0x82C00000u, 17u}, // awa -> Deva
-    {0x86C00000u, 44u}, // awb -> Latn
-    {0xBAC00000u, 44u}, // awo -> Latn
-    {0xDEC00000u, 44u}, // awx -> Latn
-    {0x61790000u, 44u}, // ay -> Latn
-    {0x87000000u, 44u}, // ayb -> Latn
-    {0x617A0000u, 44u}, // az -> Latn
+    {0xB6A00000u, 46u}, // avn -> Latn
+    {0xCEA00000u, 46u}, // avt -> Latn
+    {0xD2A00000u, 46u}, // avu -> Latn
+    {0x82C00000u, 18u}, // awa -> Deva
+    {0x86C00000u, 46u}, // awb -> Latn
+    {0xBAC00000u, 46u}, // awo -> Latn
+    {0xDEC00000u, 46u}, // awx -> Latn
+    {0x61790000u, 46u}, // ay -> Latn
+    {0x87000000u, 46u}, // ayb -> Latn
+    {0x617A0000u, 46u}, // az -> Latn
     {0x617A4951u,  1u}, // az-IQ -> Arab
     {0x617A4952u,  1u}, // az-IR -> Arab
-    {0x617A5255u, 16u}, // az-RU -> Cyrl
-    {0x62610000u, 16u}, // ba -> Cyrl
+    {0x617A5255u, 17u}, // az-RU -> Cyrl
+    {0x62610000u, 17u}, // ba -> Cyrl
     {0xAC010000u,  1u}, // bal -> Arab
-    {0xB4010000u, 44u}, // ban -> Latn
-    {0xBC010000u, 17u}, // bap -> Deva
-    {0xC4010000u, 44u}, // bar -> Latn
-    {0xC8010000u, 44u}, // bas -> Latn
-    {0xD4010000u, 44u}, // bav -> Latn
+    {0xB4010000u, 46u}, // ban -> Latn
+    {0xBC010000u, 18u}, // bap -> Deva
+    {0xC4010000u, 46u}, // bar -> Latn
+    {0xC8010000u, 46u}, // bas -> Latn
+    {0xD4010000u, 46u}, // bav -> Latn
     {0xDC010000u,  5u}, // bax -> Bamu
-    {0x80210000u, 44u}, // bba -> Latn
-    {0x84210000u, 44u}, // bbb -> Latn
-    {0x88210000u, 44u}, // bbc -> Latn
-    {0x8C210000u, 44u}, // bbd -> Latn
-    {0xA4210000u, 44u}, // bbj -> Latn
-    {0xBC210000u, 44u}, // bbp -> Latn
-    {0xC4210000u, 44u}, // bbr -> Latn
-    {0x94410000u, 44u}, // bcf -> Latn
-    {0x9C410000u, 44u}, // bch -> Latn
-    {0xA0410000u, 44u}, // bci -> Latn
-    {0xB0410000u, 44u}, // bcm -> Latn
-    {0xB4410000u, 44u}, // bcn -> Latn
-    {0xB8410000u, 44u}, // bco -> Latn
-    {0xC0410000u, 19u}, // bcq -> Ethi
-    {0xD0410000u, 44u}, // bcu -> Latn
-    {0x8C610000u, 44u}, // bdd -> Latn
-    {0x62650000u, 16u}, // be -> Cyrl
-    {0x94810000u, 44u}, // bef -> Latn
-    {0x9C810000u, 44u}, // beh -> Latn
+    {0x80210000u, 46u}, // bba -> Latn
+    {0x84210000u, 46u}, // bbb -> Latn
+    {0x88210000u, 46u}, // bbc -> Latn
+    {0x8C210000u, 46u}, // bbd -> Latn
+    {0xA4210000u, 46u}, // bbj -> Latn
+    {0xBC210000u, 46u}, // bbp -> Latn
+    {0xC4210000u, 46u}, // bbr -> Latn
+    {0x94410000u, 46u}, // bcf -> Latn
+    {0x9C410000u, 46u}, // bch -> Latn
+    {0xA0410000u, 46u}, // bci -> Latn
+    {0xB0410000u, 46u}, // bcm -> Latn
+    {0xB4410000u, 46u}, // bcn -> Latn
+    {0xB8410000u, 46u}, // bco -> Latn
+    {0xC0410000u, 20u}, // bcq -> Ethi
+    {0xD0410000u, 46u}, // bcu -> Latn
+    {0x8C610000u, 46u}, // bdd -> Latn
+    {0x62650000u, 17u}, // be -> Cyrl
+    {0x94810000u, 46u}, // bef -> Latn
+    {0x9C810000u, 46u}, // beh -> Latn
     {0xA4810000u,  1u}, // bej -> Arab
-    {0xB0810000u, 44u}, // bem -> Latn
-    {0xCC810000u, 44u}, // bet -> Latn
-    {0xD8810000u, 44u}, // bew -> Latn
-    {0xDC810000u, 44u}, // bex -> Latn
-    {0xE4810000u, 44u}, // bez -> Latn
-    {0x8CA10000u, 44u}, // bfd -> Latn
-    {0xC0A10000u, 81u}, // bfq -> Taml
+    {0xB0810000u, 46u}, // bem -> Latn
+    {0xCC810000u, 46u}, // bet -> Latn
+    {0xD8810000u, 46u}, // bew -> Latn
+    {0xDC810000u, 46u}, // bex -> Latn
+    {0xE4810000u, 46u}, // bez -> Latn
+    {0x8CA10000u, 46u}, // bfd -> Latn
+    {0xC0A10000u, 83u}, // bfq -> Taml
     {0xCCA10000u,  1u}, // bft -> Arab
-    {0xE0A10000u, 17u}, // bfy -> Deva
-    {0x62670000u, 16u}, // bg -> Cyrl
-    {0x88C10000u, 17u}, // bgc -> Deva
+    {0xE0A10000u, 18u}, // bfy -> Deva
+    {0x62670000u, 17u}, // bg -> Cyrl
+    {0x88C10000u, 18u}, // bgc -> Deva
     {0xB4C10000u,  1u}, // bgn -> Arab
-    {0xDCC10000u, 24u}, // bgx -> Grek
-    {0x84E10000u, 17u}, // bhb -> Deva
-    {0x98E10000u, 44u}, // bhg -> Latn
-    {0xA0E10000u, 17u}, // bhi -> Deva
-    {0xACE10000u, 44u}, // bhl -> Latn
-    {0xB8E10000u, 17u}, // bho -> Deva
-    {0xE0E10000u, 44u}, // bhy -> Latn
-    {0x62690000u, 44u}, // bi -> Latn
-    {0x85010000u, 44u}, // bib -> Latn
-    {0x99010000u, 44u}, // big -> Latn
-    {0xA9010000u, 44u}, // bik -> Latn
-    {0xB1010000u, 44u}, // bim -> Latn
-    {0xB5010000u, 44u}, // bin -> Latn
-    {0xB9010000u, 44u}, // bio -> Latn
-    {0xC1010000u, 44u}, // biq -> Latn
-    {0x9D210000u, 44u}, // bjh -> Latn
-    {0xA1210000u, 19u}, // bji -> Ethi
-    {0xA5210000u, 17u}, // bjj -> Deva
-    {0xB5210000u, 44u}, // bjn -> Latn
-    {0xB9210000u, 44u}, // bjo -> Latn
-    {0xC5210000u, 44u}, // bjr -> Latn
-    {0xCD210000u, 44u}, // bjt -> Latn
-    {0xE5210000u, 44u}, // bjz -> Latn
-    {0x89410000u, 44u}, // bkc -> Latn
-    {0xB1410000u, 44u}, // bkm -> Latn
-    {0xC1410000u, 44u}, // bkq -> Latn
-    {0xD1410000u, 44u}, // bku -> Latn
-    {0xD5410000u, 44u}, // bkv -> Latn
-    {0xCD610000u, 83u}, // blt -> Tavt
-    {0x626D0000u, 44u}, // bm -> Latn
-    {0x9D810000u, 44u}, // bmh -> Latn
-    {0xA9810000u, 44u}, // bmk -> Latn
-    {0xC1810000u, 44u}, // bmq -> Latn
-    {0xD1810000u, 44u}, // bmu -> Latn
+    {0xDCC10000u, 25u}, // bgx -> Grek
+    {0x84E10000u, 18u}, // bhb -> Deva
+    {0x98E10000u, 46u}, // bhg -> Latn
+    {0xA0E10000u, 18u}, // bhi -> Deva
+    {0xACE10000u, 46u}, // bhl -> Latn
+    {0xB8E10000u, 18u}, // bho -> Deva
+    {0xE0E10000u, 46u}, // bhy -> Latn
+    {0x62690000u, 46u}, // bi -> Latn
+    {0x85010000u, 46u}, // bib -> Latn
+    {0x99010000u, 46u}, // big -> Latn
+    {0xA9010000u, 46u}, // bik -> Latn
+    {0xB1010000u, 46u}, // bim -> Latn
+    {0xB5010000u, 46u}, // bin -> Latn
+    {0xB9010000u, 46u}, // bio -> Latn
+    {0xC1010000u, 46u}, // biq -> Latn
+    {0x9D210000u, 46u}, // bjh -> Latn
+    {0xA1210000u, 20u}, // bji -> Ethi
+    {0xA5210000u, 18u}, // bjj -> Deva
+    {0xB5210000u, 46u}, // bjn -> Latn
+    {0xB9210000u, 46u}, // bjo -> Latn
+    {0xC5210000u, 46u}, // bjr -> Latn
+    {0xCD210000u, 46u}, // bjt -> Latn
+    {0xE5210000u, 46u}, // bjz -> Latn
+    {0x89410000u, 46u}, // bkc -> Latn
+    {0xB1410000u, 46u}, // bkm -> Latn
+    {0xC1410000u, 46u}, // bkq -> Latn
+    {0xD1410000u, 46u}, // bku -> Latn
+    {0xD5410000u, 46u}, // bkv -> Latn
+    {0xCD610000u, 85u}, // blt -> Tavt
+    {0x626D0000u, 46u}, // bm -> Latn
+    {0x9D810000u, 46u}, // bmh -> Latn
+    {0xA9810000u, 46u}, // bmk -> Latn
+    {0xC1810000u, 46u}, // bmq -> Latn
+    {0xD1810000u, 46u}, // bmu -> Latn
     {0x626E0000u,  7u}, // bn -> Beng
-    {0x99A10000u, 44u}, // bng -> Latn
-    {0xB1A10000u, 44u}, // bnm -> Latn
-    {0xBDA10000u, 44u}, // bnp -> Latn
-    {0x626F0000u, 88u}, // bo -> Tibt
-    {0xA5C10000u, 44u}, // boj -> Latn
-    {0xB1C10000u, 44u}, // bom -> Latn
-    {0xB5C10000u, 44u}, // bon -> Latn
+    {0x99A10000u, 46u}, // bng -> Latn
+    {0xB1A10000u, 46u}, // bnm -> Latn
+    {0xBDA10000u, 46u}, // bnp -> Latn
+    {0x626F0000u, 90u}, // bo -> Tibt
+    {0xA5C10000u, 46u}, // boj -> Latn
+    {0xB1C10000u, 46u}, // bom -> Latn
+    {0xB5C10000u, 46u}, // bon -> Latn
     {0xE1E10000u,  7u}, // bpy -> Beng
-    {0x8A010000u, 44u}, // bqc -> Latn
+    {0x8A010000u, 46u}, // bqc -> Latn
     {0xA2010000u,  1u}, // bqi -> Arab
-    {0xBE010000u, 44u}, // bqp -> Latn
-    {0xD6010000u, 44u}, // bqv -> Latn
-    {0x62720000u, 44u}, // br -> Latn
-    {0x82210000u, 17u}, // bra -> Deva
+    {0xBE010000u, 46u}, // bqp -> Latn
+    {0xD6010000u, 46u}, // bqv -> Latn
+    {0x62720000u, 46u}, // br -> Latn
+    {0x82210000u, 18u}, // bra -> Deva
     {0x9E210000u,  1u}, // brh -> Arab
-    {0xDE210000u, 17u}, // brx -> Deva
-    {0xE6210000u, 44u}, // brz -> Latn
-    {0x62730000u, 44u}, // bs -> Latn
-    {0xA6410000u, 44u}, // bsj -> Latn
+    {0xDE210000u, 18u}, // brx -> Deva
+    {0xE6210000u, 46u}, // brz -> Latn
+    {0x62730000u, 46u}, // bs -> Latn
+    {0xA6410000u, 46u}, // bsj -> Latn
     {0xC2410000u,  6u}, // bsq -> Bass
-    {0xCA410000u, 44u}, // bss -> Latn
-    {0xCE410000u, 19u}, // bst -> Ethi
-    {0xBA610000u, 44u}, // bto -> Latn
-    {0xCE610000u, 44u}, // btt -> Latn
-    {0xD6610000u, 17u}, // btv -> Deva
-    {0x82810000u, 16u}, // bua -> Cyrl
-    {0x8A810000u, 44u}, // buc -> Latn
-    {0x8E810000u, 44u}, // bud -> Latn
-    {0x9A810000u, 44u}, // bug -> Latn
-    {0xAA810000u, 44u}, // buk -> Latn
-    {0xB2810000u, 44u}, // bum -> Latn
-    {0xBA810000u, 44u}, // buo -> Latn
-    {0xCA810000u, 44u}, // bus -> Latn
-    {0xD2810000u, 44u}, // buu -> Latn
-    {0x86A10000u, 44u}, // bvb -> Latn
-    {0x8EC10000u, 44u}, // bwd -> Latn
-    {0xC6C10000u, 44u}, // bwr -> Latn
-    {0x9EE10000u, 44u}, // bxh -> Latn
-    {0x93010000u, 44u}, // bye -> Latn
-    {0xB7010000u, 19u}, // byn -> Ethi
-    {0xC7010000u, 44u}, // byr -> Latn
-    {0xCB010000u, 44u}, // bys -> Latn
-    {0xD7010000u, 44u}, // byv -> Latn
-    {0xDF010000u, 44u}, // byx -> Latn
-    {0x83210000u, 44u}, // bza -> Latn
-    {0x93210000u, 44u}, // bze -> Latn
-    {0x97210000u, 44u}, // bzf -> Latn
-    {0x9F210000u, 44u}, // bzh -> Latn
-    {0xDB210000u, 44u}, // bzw -> Latn
-    {0x63610000u, 44u}, // ca -> Latn
-    {0xB4020000u, 44u}, // can -> Latn
-    {0xA4220000u, 44u}, // cbj -> Latn
-    {0x9C420000u, 44u}, // cch -> Latn
+    {0xCA410000u, 46u}, // bss -> Latn
+    {0xCE410000u, 20u}, // bst -> Ethi
+    {0xBA610000u, 46u}, // bto -> Latn
+    {0xCE610000u, 46u}, // btt -> Latn
+    {0xD6610000u, 18u}, // btv -> Deva
+    {0x82810000u, 17u}, // bua -> Cyrl
+    {0x8A810000u, 46u}, // buc -> Latn
+    {0x8E810000u, 46u}, // bud -> Latn
+    {0x9A810000u, 46u}, // bug -> Latn
+    {0xAA810000u, 46u}, // buk -> Latn
+    {0xB2810000u, 46u}, // bum -> Latn
+    {0xBA810000u, 46u}, // buo -> Latn
+    {0xCA810000u, 46u}, // bus -> Latn
+    {0xD2810000u, 46u}, // buu -> Latn
+    {0x86A10000u, 46u}, // bvb -> Latn
+    {0x8EC10000u, 46u}, // bwd -> Latn
+    {0xC6C10000u, 46u}, // bwr -> Latn
+    {0x9EE10000u, 46u}, // bxh -> Latn
+    {0x93010000u, 46u}, // bye -> Latn
+    {0xB7010000u, 20u}, // byn -> Ethi
+    {0xC7010000u, 46u}, // byr -> Latn
+    {0xCB010000u, 46u}, // bys -> Latn
+    {0xD7010000u, 46u}, // byv -> Latn
+    {0xDF010000u, 46u}, // byx -> Latn
+    {0x83210000u, 46u}, // bza -> Latn
+    {0x93210000u, 46u}, // bze -> Latn
+    {0x97210000u, 46u}, // bzf -> Latn
+    {0x9F210000u, 46u}, // bzh -> Latn
+    {0xDB210000u, 46u}, // bzw -> Latn
+    {0x63610000u, 46u}, // ca -> Latn
+    {0xB4020000u, 46u}, // can -> Latn
+    {0xA4220000u, 46u}, // cbj -> Latn
+    {0x9C420000u, 46u}, // cch -> Latn
     {0xBC420000u,  9u}, // ccp -> Cakm
-    {0x63650000u, 16u}, // ce -> Cyrl
-    {0x84820000u, 44u}, // ceb -> Latn
-    {0x80A20000u, 44u}, // cfa -> Latn
-    {0x98C20000u, 44u}, // cgg -> Latn
-    {0x63680000u, 44u}, // ch -> Latn
-    {0xA8E20000u, 44u}, // chk -> Latn
-    {0xB0E20000u, 16u}, // chm -> Cyrl
-    {0xB8E20000u, 44u}, // cho -> Latn
-    {0xBCE20000u, 44u}, // chp -> Latn
+    {0x63650000u, 17u}, // ce -> Cyrl
+    {0x84820000u, 46u}, // ceb -> Latn
+    {0x80A20000u, 46u}, // cfa -> Latn
+    {0x98C20000u, 46u}, // cgg -> Latn
+    {0x63680000u, 46u}, // ch -> Latn
+    {0xA8E20000u, 46u}, // chk -> Latn
+    {0xB0E20000u, 17u}, // chm -> Cyrl
+    {0xB8E20000u, 46u}, // cho -> Latn
+    {0xBCE20000u, 46u}, // chp -> Latn
     {0xC4E20000u, 13u}, // chr -> Cher
-    {0x89020000u, 44u}, // cic -> Latn
+    {0x89020000u, 46u}, // cic -> Latn
     {0x81220000u,  1u}, // cja -> Arab
     {0xB1220000u, 12u}, // cjm -> Cham
-    {0xD5220000u, 44u}, // cjv -> Latn
+    {0xD5220000u, 46u}, // cjv -> Latn
     {0x85420000u,  1u}, // ckb -> Arab
-    {0xAD420000u, 44u}, // ckl -> Latn
-    {0xB9420000u, 44u}, // cko -> Latn
-    {0xE1420000u, 44u}, // cky -> Latn
-    {0x81620000u, 44u}, // cla -> Latn
-    {0x91820000u, 44u}, // cme -> Latn
-    {0x99820000u, 77u}, // cmg -> Soyo
-    {0x636F0000u, 44u}, // co -> Latn
-    {0xBDC20000u, 14u}, // cop -> Copt
-    {0xC9E20000u, 44u}, // cps -> Latn
+    {0xAD420000u, 46u}, // ckl -> Latn
+    {0xB9420000u, 46u}, // cko -> Latn
+    {0xE1420000u, 46u}, // cky -> Latn
+    {0x81620000u, 46u}, // cla -> Latn
+    {0x91820000u, 46u}, // cme -> Latn
+    {0x99820000u, 79u}, // cmg -> Soyo
+    {0x636F0000u, 46u}, // co -> Latn
+    {0xBDC20000u, 15u}, // cop -> Copt
+    {0xC9E20000u, 46u}, // cps -> Latn
     {0x63720000u, 10u}, // cr -> Cans
-    {0x9E220000u, 16u}, // crh -> Cyrl
+    {0x9E220000u, 17u}, // crh -> Cyrl
     {0xA6220000u, 10u}, // crj -> Cans
     {0xAA220000u, 10u}, // crk -> Cans
     {0xAE220000u, 10u}, // crl -> Cans
     {0xB2220000u, 10u}, // crm -> Cans
-    {0xCA220000u, 44u}, // crs -> Latn
-    {0x63730000u, 44u}, // cs -> Latn
-    {0x86420000u, 44u}, // csb -> Latn
+    {0xCA220000u, 46u}, // crs -> Latn
+    {0x63730000u, 46u}, // cs -> Latn
+    {0x86420000u, 46u}, // csb -> Latn
     {0xDA420000u, 10u}, // csw -> Cans
-    {0x8E620000u, 64u}, // ctd -> Pauc
-    {0x63750000u, 16u}, // cu -> Cyrl
-    {0x63760000u, 16u}, // cv -> Cyrl
-    {0x63790000u, 44u}, // cy -> Latn
-    {0x64610000u, 44u}, // da -> Latn
-    {0x8C030000u, 44u}, // dad -> Latn
-    {0x94030000u, 44u}, // daf -> Latn
-    {0x98030000u, 44u}, // dag -> Latn
-    {0x9C030000u, 44u}, // dah -> Latn
-    {0xA8030000u, 44u}, // dak -> Latn
-    {0xC4030000u, 16u}, // dar -> Cyrl
-    {0xD4030000u, 44u}, // dav -> Latn
-    {0x8C230000u, 44u}, // dbd -> Latn
-    {0xC0230000u, 44u}, // dbq -> Latn
+    {0x8E620000u, 66u}, // ctd -> Pauc
+    {0x63750000u, 17u}, // cu -> Cyrl
+    {0x63760000u, 17u}, // cv -> Cyrl
+    {0x63790000u, 46u}, // cy -> Latn
+    {0x64610000u, 46u}, // da -> Latn
+    {0x8C030000u, 46u}, // dad -> Latn
+    {0x94030000u, 46u}, // daf -> Latn
+    {0x98030000u, 46u}, // dag -> Latn
+    {0x9C030000u, 46u}, // dah -> Latn
+    {0xA8030000u, 46u}, // dak -> Latn
+    {0xC4030000u, 17u}, // dar -> Cyrl
+    {0xD4030000u, 46u}, // dav -> Latn
+    {0x8C230000u, 46u}, // dbd -> Latn
+    {0xC0230000u, 46u}, // dbq -> Latn
     {0x88430000u,  1u}, // dcc -> Arab
-    {0xB4630000u, 44u}, // ddn -> Latn
-    {0x64650000u, 44u}, // de -> Latn
-    {0x8C830000u, 44u}, // ded -> Latn
-    {0xB4830000u, 44u}, // den -> Latn
-    {0x80C30000u, 44u}, // dga -> Latn
-    {0x9CC30000u, 44u}, // dgh -> Latn
-    {0xA0C30000u, 44u}, // dgi -> Latn
+    {0xB4630000u, 46u}, // ddn -> Latn
+    {0x64650000u, 46u}, // de -> Latn
+    {0x8C830000u, 46u}, // ded -> Latn
+    {0xB4830000u, 46u}, // den -> Latn
+    {0x80C30000u, 46u}, // dga -> Latn
+    {0x9CC30000u, 46u}, // dgh -> Latn
+    {0xA0C30000u, 46u}, // dgi -> Latn
     {0xACC30000u,  1u}, // dgl -> Arab
-    {0xC4C30000u, 44u}, // dgr -> Latn
-    {0xE4C30000u, 44u}, // dgz -> Latn
-    {0x81030000u, 44u}, // dia -> Latn
-    {0x91230000u, 44u}, // dje -> Latn
-    {0xA5A30000u, 44u}, // dnj -> Latn
-    {0x85C30000u, 44u}, // dob -> Latn
+    {0xC4C30000u, 46u}, // dgr -> Latn
+    {0xE4C30000u, 46u}, // dgz -> Latn
+    {0x81030000u, 46u}, // dia -> Latn
+    {0x91230000u, 46u}, // dje -> Latn
+    {0xA5A30000u, 46u}, // dnj -> Latn
+    {0x85C30000u, 46u}, // dob -> Latn
     {0xA1C30000u,  1u}, // doi -> Arab
-    {0xBDC30000u, 44u}, // dop -> Latn
-    {0xD9C30000u, 44u}, // dow -> Latn
-    {0x9E230000u, 54u}, // drh -> Mong
-    {0xA2230000u, 44u}, // dri -> Latn
-    {0xCA230000u, 19u}, // drs -> Ethi
-    {0x86430000u, 44u}, // dsb -> Latn
-    {0xB2630000u, 44u}, // dtm -> Latn
-    {0xBE630000u, 44u}, // dtp -> Latn
-    {0xCA630000u, 44u}, // dts -> Latn
-    {0xE2630000u, 17u}, // dty -> Deva
-    {0x82830000u, 44u}, // dua -> Latn
-    {0x8A830000u, 44u}, // duc -> Latn
-    {0x8E830000u, 44u}, // dud -> Latn
-    {0x9A830000u, 44u}, // dug -> Latn
-    {0x64760000u, 86u}, // dv -> Thaa
-    {0x82A30000u, 44u}, // dva -> Latn
-    {0xDAC30000u, 44u}, // dww -> Latn
-    {0xBB030000u, 44u}, // dyo -> Latn
-    {0xD3030000u, 44u}, // dyu -> Latn
-    {0x647A0000u, 88u}, // dz -> Tibt
-    {0x9B230000u, 44u}, // dzg -> Latn
-    {0xD0240000u, 44u}, // ebu -> Latn
-    {0x65650000u, 44u}, // ee -> Latn
-    {0xA0A40000u, 44u}, // efi -> Latn
-    {0xACC40000u, 44u}, // egl -> Latn
-    {0xE0C40000u, 18u}, // egy -> Egyp
-    {0x81440000u, 44u}, // eka -> Latn
-    {0xE1440000u, 36u}, // eky -> Kali
-    {0x656C0000u, 24u}, // el -> Grek
-    {0x81840000u, 44u}, // ema -> Latn
-    {0xA1840000u, 44u}, // emi -> Latn
-    {0x656E0000u, 44u}, // en -> Latn
-    {0x656E5841u, 95u}, // en-XA -> ~~~A
-    {0xB5A40000u, 44u}, // enn -> Latn
-    {0xC1A40000u, 44u}, // enq -> Latn
-    {0x656F0000u, 44u}, // eo -> Latn
-    {0xA2240000u, 44u}, // eri -> Latn
-    {0x65730000u, 44u}, // es -> Latn
-    {0x9A440000u, 22u}, // esg -> Gonm
-    {0xD2440000u, 44u}, // esu -> Latn
-    {0x65740000u, 44u}, // et -> Latn
-    {0xC6640000u, 44u}, // etr -> Latn
-    {0xCE640000u, 34u}, // ett -> Ital
-    {0xD2640000u, 44u}, // etu -> Latn
-    {0xDE640000u, 44u}, // etx -> Latn
-    {0x65750000u, 44u}, // eu -> Latn
-    {0xBAC40000u, 44u}, // ewo -> Latn
-    {0xCEE40000u, 44u}, // ext -> Latn
+    {0xBDC30000u, 46u}, // dop -> Latn
+    {0xD9C30000u, 46u}, // dow -> Latn
+    {0x9E230000u, 56u}, // drh -> Mong
+    {0xA2230000u, 46u}, // dri -> Latn
+    {0xCA230000u, 20u}, // drs -> Ethi
+    {0x86430000u, 46u}, // dsb -> Latn
+    {0xB2630000u, 46u}, // dtm -> Latn
+    {0xBE630000u, 46u}, // dtp -> Latn
+    {0xCA630000u, 46u}, // dts -> Latn
+    {0xE2630000u, 18u}, // dty -> Deva
+    {0x82830000u, 46u}, // dua -> Latn
+    {0x8A830000u, 46u}, // duc -> Latn
+    {0x8E830000u, 46u}, // dud -> Latn
+    {0x9A830000u, 46u}, // dug -> Latn
+    {0x64760000u, 88u}, // dv -> Thaa
+    {0x82A30000u, 46u}, // dva -> Latn
+    {0xDAC30000u, 46u}, // dww -> Latn
+    {0xBB030000u, 46u}, // dyo -> Latn
+    {0xD3030000u, 46u}, // dyu -> Latn
+    {0x647A0000u, 90u}, // dz -> Tibt
+    {0x9B230000u, 46u}, // dzg -> Latn
+    {0xD0240000u, 46u}, // ebu -> Latn
+    {0x65650000u, 46u}, // ee -> Latn
+    {0xA0A40000u, 46u}, // efi -> Latn
+    {0xACC40000u, 46u}, // egl -> Latn
+    {0xE0C40000u, 19u}, // egy -> Egyp
+    {0x81440000u, 46u}, // eka -> Latn
+    {0xE1440000u, 37u}, // eky -> Kali
+    {0x656C0000u, 25u}, // el -> Grek
+    {0x81840000u, 46u}, // ema -> Latn
+    {0xA1840000u, 46u}, // emi -> Latn
+    {0x656E0000u, 46u}, // en -> Latn
+    {0x656E5841u, 97u}, // en-XA -> ~~~A
+    {0xB5A40000u, 46u}, // enn -> Latn
+    {0xC1A40000u, 46u}, // enq -> Latn
+    {0x656F0000u, 46u}, // eo -> Latn
+    {0xA2240000u, 46u}, // eri -> Latn
+    {0x65730000u, 46u}, // es -> Latn
+    {0x9A440000u, 23u}, // esg -> Gonm
+    {0xD2440000u, 46u}, // esu -> Latn
+    {0x65740000u, 46u}, // et -> Latn
+    {0xC6640000u, 46u}, // etr -> Latn
+    {0xCE640000u, 35u}, // ett -> Ital
+    {0xD2640000u, 46u}, // etu -> Latn
+    {0xDE640000u, 46u}, // etx -> Latn
+    {0x65750000u, 46u}, // eu -> Latn
+    {0xBAC40000u, 46u}, // ewo -> Latn
+    {0xCEE40000u, 46u}, // ext -> Latn
     {0x66610000u,  1u}, // fa -> Arab
-    {0x80050000u, 44u}, // faa -> Latn
-    {0x84050000u, 44u}, // fab -> Latn
-    {0x98050000u, 44u}, // fag -> Latn
-    {0xA0050000u, 44u}, // fai -> Latn
-    {0xB4050000u, 44u}, // fan -> Latn
-    {0x66660000u, 44u}, // ff -> Latn
-    {0xA0A50000u, 44u}, // ffi -> Latn
-    {0xB0A50000u, 44u}, // ffm -> Latn
-    {0x66690000u, 44u}, // fi -> Latn
+    {0x80050000u, 46u}, // faa -> Latn
+    {0x84050000u, 46u}, // fab -> Latn
+    {0x98050000u, 46u}, // fag -> Latn
+    {0xA0050000u, 46u}, // fai -> Latn
+    {0xB4050000u, 46u}, // fan -> Latn
+    {0x66660000u, 46u}, // ff -> Latn
+    {0xA0A50000u, 46u}, // ffi -> Latn
+    {0xB0A50000u, 46u}, // ffm -> Latn
+    {0x66690000u, 46u}, // fi -> Latn
     {0x81050000u,  1u}, // fia -> Arab
-    {0xAD050000u, 44u}, // fil -> Latn
-    {0xCD050000u, 44u}, // fit -> Latn
-    {0x666A0000u, 44u}, // fj -> Latn
-    {0xC5650000u, 44u}, // flr -> Latn
-    {0xBD850000u, 44u}, // fmp -> Latn
-    {0x666F0000u, 44u}, // fo -> Latn
-    {0x8DC50000u, 44u}, // fod -> Latn
-    {0xB5C50000u, 44u}, // fon -> Latn
-    {0xC5C50000u, 44u}, // for -> Latn
-    {0x91E50000u, 44u}, // fpe -> Latn
-    {0xCA050000u, 44u}, // fqs -> Latn
-    {0x66720000u, 44u}, // fr -> Latn
-    {0x8A250000u, 44u}, // frc -> Latn
-    {0xBE250000u, 44u}, // frp -> Latn
-    {0xC6250000u, 44u}, // frr -> Latn
-    {0xCA250000u, 44u}, // frs -> Latn
+    {0xAD050000u, 46u}, // fil -> Latn
+    {0xCD050000u, 46u}, // fit -> Latn
+    {0x666A0000u, 46u}, // fj -> Latn
+    {0xC5650000u, 46u}, // flr -> Latn
+    {0xBD850000u, 46u}, // fmp -> Latn
+    {0x666F0000u, 46u}, // fo -> Latn
+    {0x8DC50000u, 46u}, // fod -> Latn
+    {0xB5C50000u, 46u}, // fon -> Latn
+    {0xC5C50000u, 46u}, // for -> Latn
+    {0x91E50000u, 46u}, // fpe -> Latn
+    {0xCA050000u, 46u}, // fqs -> Latn
+    {0x66720000u, 46u}, // fr -> Latn
+    {0x8A250000u, 46u}, // frc -> Latn
+    {0xBE250000u, 46u}, // frp -> Latn
+    {0xC6250000u, 46u}, // frr -> Latn
+    {0xCA250000u, 46u}, // frs -> Latn
     {0x86850000u,  1u}, // fub -> Arab
-    {0x8E850000u, 44u}, // fud -> Latn
-    {0x92850000u, 44u}, // fue -> Latn
-    {0x96850000u, 44u}, // fuf -> Latn
-    {0x9E850000u, 44u}, // fuh -> Latn
-    {0xC2850000u, 44u}, // fuq -> Latn
-    {0xC6850000u, 44u}, // fur -> Latn
-    {0xD6850000u, 44u}, // fuv -> Latn
-    {0xE2850000u, 44u}, // fuy -> Latn
-    {0xC6A50000u, 44u}, // fvr -> Latn
-    {0x66790000u, 44u}, // fy -> Latn
-    {0x67610000u, 44u}, // ga -> Latn
-    {0x80060000u, 44u}, // gaa -> Latn
-    {0x94060000u, 44u}, // gaf -> Latn
-    {0x98060000u, 44u}, // gag -> Latn
-    {0x9C060000u, 44u}, // gah -> Latn
-    {0xA4060000u, 44u}, // gaj -> Latn
-    {0xB0060000u, 44u}, // gam -> Latn
-    {0xB4060000u, 27u}, // gan -> Hans
-    {0xD8060000u, 44u}, // gaw -> Latn
-    {0xE0060000u, 44u}, // gay -> Latn
-    {0x80260000u, 44u}, // gba -> Latn
-    {0x94260000u, 44u}, // gbf -> Latn
-    {0xB0260000u, 17u}, // gbm -> Deva
-    {0xE0260000u, 44u}, // gby -> Latn
+    {0x8E850000u, 46u}, // fud -> Latn
+    {0x92850000u, 46u}, // fue -> Latn
+    {0x96850000u, 46u}, // fuf -> Latn
+    {0x9E850000u, 46u}, // fuh -> Latn
+    {0xC2850000u, 46u}, // fuq -> Latn
+    {0xC6850000u, 46u}, // fur -> Latn
+    {0xD6850000u, 46u}, // fuv -> Latn
+    {0xE2850000u, 46u}, // fuy -> Latn
+    {0xC6A50000u, 46u}, // fvr -> Latn
+    {0x66790000u, 46u}, // fy -> Latn
+    {0x67610000u, 46u}, // ga -> Latn
+    {0x80060000u, 46u}, // gaa -> Latn
+    {0x94060000u, 46u}, // gaf -> Latn
+    {0x98060000u, 46u}, // gag -> Latn
+    {0x9C060000u, 46u}, // gah -> Latn
+    {0xA4060000u, 46u}, // gaj -> Latn
+    {0xB0060000u, 46u}, // gam -> Latn
+    {0xB4060000u, 28u}, // gan -> Hans
+    {0xD8060000u, 46u}, // gaw -> Latn
+    {0xE0060000u, 46u}, // gay -> Latn
+    {0x80260000u, 46u}, // gba -> Latn
+    {0x94260000u, 46u}, // gbf -> Latn
+    {0xB0260000u, 18u}, // gbm -> Deva
+    {0xE0260000u, 46u}, // gby -> Latn
     {0xE4260000u,  1u}, // gbz -> Arab
-    {0xC4460000u, 44u}, // gcr -> Latn
-    {0x67640000u, 44u}, // gd -> Latn
-    {0x90660000u, 44u}, // gde -> Latn
-    {0xB4660000u, 44u}, // gdn -> Latn
-    {0xC4660000u, 44u}, // gdr -> Latn
-    {0x84860000u, 44u}, // geb -> Latn
-    {0xA4860000u, 44u}, // gej -> Latn
-    {0xAC860000u, 44u}, // gel -> Latn
-    {0xE4860000u, 19u}, // gez -> Ethi
-    {0xA8A60000u, 44u}, // gfk -> Latn
-    {0xB4C60000u, 17u}, // ggn -> Deva
-    {0xC8E60000u, 44u}, // ghs -> Latn
-    {0xAD060000u, 44u}, // gil -> Latn
-    {0xB1060000u, 44u}, // gim -> Latn
+    {0xC4460000u, 46u}, // gcr -> Latn
+    {0x67640000u, 46u}, // gd -> Latn
+    {0x90660000u, 46u}, // gde -> Latn
+    {0xB4660000u, 46u}, // gdn -> Latn
+    {0xC4660000u, 46u}, // gdr -> Latn
+    {0x84860000u, 46u}, // geb -> Latn
+    {0xA4860000u, 46u}, // gej -> Latn
+    {0xAC860000u, 46u}, // gel -> Latn
+    {0xE4860000u, 20u}, // gez -> Ethi
+    {0xA8A60000u, 46u}, // gfk -> Latn
+    {0xB4C60000u, 18u}, // ggn -> Deva
+    {0xC8E60000u, 46u}, // ghs -> Latn
+    {0xAD060000u, 46u}, // gil -> Latn
+    {0xB1060000u, 46u}, // gim -> Latn
     {0xA9260000u,  1u}, // gjk -> Arab
-    {0xB5260000u, 44u}, // gjn -> Latn
+    {0xB5260000u, 46u}, // gjn -> Latn
     {0xD1260000u,  1u}, // gju -> Arab
-    {0xB5460000u, 44u}, // gkn -> Latn
-    {0xBD460000u, 44u}, // gkp -> Latn
-    {0x676C0000u, 44u}, // gl -> Latn
+    {0xB5460000u, 46u}, // gkn -> Latn
+    {0xBD460000u, 46u}, // gkp -> Latn
+    {0x676C0000u, 46u}, // gl -> Latn
     {0xA9660000u,  1u}, // glk -> Arab
-    {0xB1860000u, 44u}, // gmm -> Latn
-    {0xD5860000u, 19u}, // gmv -> Ethi
-    {0x676E0000u, 44u}, // gn -> Latn
-    {0x8DA60000u, 44u}, // gnd -> Latn
-    {0x99A60000u, 44u}, // gng -> Latn
-    {0x8DC60000u, 44u}, // god -> Latn
-    {0x95C60000u, 19u}, // gof -> Ethi
-    {0xA1C60000u, 44u}, // goi -> Latn
-    {0xB1C60000u, 17u}, // gom -> Deva
-    {0xB5C60000u, 84u}, // gon -> Telu
-    {0xC5C60000u, 44u}, // gor -> Latn
-    {0xC9C60000u, 44u}, // gos -> Latn
-    {0xCDC60000u, 23u}, // got -> Goth
-    {0x86260000u, 44u}, // grb -> Latn
-    {0x8A260000u, 15u}, // grc -> Cprt
+    {0xB1860000u, 46u}, // gmm -> Latn
+    {0xD5860000u, 20u}, // gmv -> Ethi
+    {0x676E0000u, 46u}, // gn -> Latn
+    {0x8DA60000u, 46u}, // gnd -> Latn
+    {0x99A60000u, 46u}, // gng -> Latn
+    {0x8DC60000u, 46u}, // god -> Latn
+    {0x95C60000u, 20u}, // gof -> Ethi
+    {0xA1C60000u, 46u}, // goi -> Latn
+    {0xB1C60000u, 18u}, // gom -> Deva
+    {0xB5C60000u, 86u}, // gon -> Telu
+    {0xC5C60000u, 46u}, // gor -> Latn
+    {0xC9C60000u, 46u}, // gos -> Latn
+    {0xCDC60000u, 24u}, // got -> Goth
+    {0x86260000u, 46u}, // grb -> Latn
+    {0x8A260000u, 16u}, // grc -> Cprt
     {0xCE260000u,  7u}, // grt -> Beng
-    {0xDA260000u, 44u}, // grw -> Latn
-    {0xDA460000u, 44u}, // gsw -> Latn
-    {0x67750000u, 25u}, // gu -> Gujr
-    {0x86860000u, 44u}, // gub -> Latn
-    {0x8A860000u, 44u}, // guc -> Latn
-    {0x8E860000u, 44u}, // gud -> Latn
-    {0xC6860000u, 44u}, // gur -> Latn
-    {0xDA860000u, 44u}, // guw -> Latn
-    {0xDE860000u, 44u}, // gux -> Latn
-    {0xE6860000u, 44u}, // guz -> Latn
-    {0x67760000u, 44u}, // gv -> Latn
-    {0x96A60000u, 44u}, // gvf -> Latn
-    {0xC6A60000u, 17u}, // gvr -> Deva
-    {0xCAA60000u, 44u}, // gvs -> Latn
+    {0xDA260000u, 46u}, // grw -> Latn
+    {0xDA460000u, 46u}, // gsw -> Latn
+    {0x67750000u, 26u}, // gu -> Gujr
+    {0x86860000u, 46u}, // gub -> Latn
+    {0x8A860000u, 46u}, // guc -> Latn
+    {0x8E860000u, 46u}, // gud -> Latn
+    {0xC6860000u, 46u}, // gur -> Latn
+    {0xDA860000u, 46u}, // guw -> Latn
+    {0xDE860000u, 46u}, // gux -> Latn
+    {0xE6860000u, 46u}, // guz -> Latn
+    {0x67760000u, 46u}, // gv -> Latn
+    {0x96A60000u, 46u}, // gvf -> Latn
+    {0xC6A60000u, 18u}, // gvr -> Deva
+    {0xCAA60000u, 46u}, // gvs -> Latn
     {0x8AC60000u,  1u}, // gwc -> Arab
-    {0xA2C60000u, 44u}, // gwi -> Latn
+    {0xA2C60000u, 46u}, // gwi -> Latn
     {0xCEC60000u,  1u}, // gwt -> Arab
-    {0xA3060000u, 44u}, // gyi -> Latn
-    {0x68610000u, 44u}, // ha -> Latn
+    {0xA3060000u, 46u}, // gyi -> Latn
+    {0x68610000u, 46u}, // ha -> Latn
     {0x6861434Du,  1u}, // ha-CM -> Arab
     {0x68615344u,  1u}, // ha-SD -> Arab
-    {0x98070000u, 44u}, // hag -> Latn
-    {0xA8070000u, 27u}, // hak -> Hans
-    {0xB0070000u, 44u}, // ham -> Latn
-    {0xD8070000u, 44u}, // haw -> Latn
+    {0x98070000u, 46u}, // hag -> Latn
+    {0xA8070000u, 28u}, // hak -> Hans
+    {0xB0070000u, 46u}, // ham -> Latn
+    {0xD8070000u, 46u}, // haw -> Latn
     {0xE4070000u,  1u}, // haz -> Arab
-    {0x84270000u, 44u}, // hbb -> Latn
-    {0xE0670000u, 19u}, // hdy -> Ethi
-    {0x68650000u, 30u}, // he -> Hebr
-    {0xE0E70000u, 44u}, // hhy -> Latn
-    {0x68690000u, 17u}, // hi -> Deva
-    {0x81070000u, 44u}, // hia -> Latn
-    {0x95070000u, 44u}, // hif -> Latn
-    {0x99070000u, 44u}, // hig -> Latn
-    {0x9D070000u, 44u}, // hih -> Latn
-    {0xAD070000u, 44u}, // hil -> Latn
-    {0x81670000u, 44u}, // hla -> Latn
-    {0xD1670000u, 31u}, // hlu -> Hluw
-    {0x8D870000u, 67u}, // hmd -> Plrd
-    {0xCD870000u, 44u}, // hmt -> Latn
+    {0x84270000u, 46u}, // hbb -> Latn
+    {0xE0670000u, 20u}, // hdy -> Ethi
+    {0x68650000u, 31u}, // he -> Hebr
+    {0xE0E70000u, 46u}, // hhy -> Latn
+    {0x68690000u, 18u}, // hi -> Deva
+    {0x81070000u, 46u}, // hia -> Latn
+    {0x95070000u, 46u}, // hif -> Latn
+    {0x99070000u, 46u}, // hig -> Latn
+    {0x9D070000u, 46u}, // hih -> Latn
+    {0xAD070000u, 46u}, // hil -> Latn
+    {0x81670000u, 46u}, // hla -> Latn
+    {0xD1670000u, 32u}, // hlu -> Hluw
+    {0x8D870000u, 69u}, // hmd -> Plrd
+    {0xCD870000u, 46u}, // hmt -> Latn
     {0x8DA70000u,  1u}, // hnd -> Arab
-    {0x91A70000u, 17u}, // hne -> Deva
-    {0xA5A70000u, 32u}, // hnj -> Hmng
-    {0xB5A70000u, 44u}, // hnn -> Latn
+    {0x91A70000u, 18u}, // hne -> Deva
+    {0xA5A70000u, 33u}, // hnj -> Hmng
+    {0xB5A70000u, 46u}, // hnn -> Latn
     {0xB9A70000u,  1u}, // hno -> Arab
-    {0x686F0000u, 44u}, // ho -> Latn
-    {0x89C70000u, 17u}, // hoc -> Deva
-    {0xA5C70000u, 17u}, // hoj -> Deva
-    {0xCDC70000u, 44u}, // hot -> Latn
-    {0x68720000u, 44u}, // hr -> Latn
-    {0x86470000u, 44u}, // hsb -> Latn
-    {0xB6470000u, 27u}, // hsn -> Hans
-    {0x68740000u, 44u}, // ht -> Latn
-    {0x68750000u, 44u}, // hu -> Latn
-    {0xA2870000u, 44u}, // hui -> Latn
+    {0x686F0000u, 46u}, // ho -> Latn
+    {0x89C70000u, 18u}, // hoc -> Deva
+    {0xA5C70000u, 18u}, // hoj -> Deva
+    {0xCDC70000u, 46u}, // hot -> Latn
+    {0x68720000u, 46u}, // hr -> Latn
+    {0x86470000u, 46u}, // hsb -> Latn
+    {0xB6470000u, 28u}, // hsn -> Hans
+    {0x68740000u, 46u}, // ht -> Latn
+    {0x68750000u, 46u}, // hu -> Latn
+    {0xA2870000u, 46u}, // hui -> Latn
     {0x68790000u,  3u}, // hy -> Armn
-    {0x687A0000u, 44u}, // hz -> Latn
-    {0x69610000u, 44u}, // ia -> Latn
-    {0xB4080000u, 44u}, // ian -> Latn
-    {0xC4080000u, 44u}, // iar -> Latn
-    {0x80280000u, 44u}, // iba -> Latn
-    {0x84280000u, 44u}, // ibb -> Latn
-    {0xE0280000u, 44u}, // iby -> Latn
-    {0x80480000u, 44u}, // ica -> Latn
-    {0x9C480000u, 44u}, // ich -> Latn
-    {0x69640000u, 44u}, // id -> Latn
-    {0x8C680000u, 44u}, // idd -> Latn
-    {0xA0680000u, 44u}, // idi -> Latn
-    {0xD0680000u, 44u}, // idu -> Latn
-    {0x90A80000u, 44u}, // ife -> Latn
-    {0x69670000u, 44u}, // ig -> Latn
-    {0x84C80000u, 44u}, // igb -> Latn
-    {0x90C80000u, 44u}, // ige -> Latn
-    {0x69690000u, 94u}, // ii -> Yiii
-    {0xA5280000u, 44u}, // ijj -> Latn
-    {0x696B0000u, 44u}, // ik -> Latn
-    {0xA9480000u, 44u}, // ikk -> Latn
-    {0xCD480000u, 44u}, // ikt -> Latn
-    {0xD9480000u, 44u}, // ikw -> Latn
-    {0xDD480000u, 44u}, // ikx -> Latn
-    {0xB9680000u, 44u}, // ilo -> Latn
-    {0xB9880000u, 44u}, // imo -> Latn
-    {0x696E0000u, 44u}, // in -> Latn
-    {0x9DA80000u, 16u}, // inh -> Cyrl
-    {0x696F0000u, 44u}, // io -> Latn
-    {0xD1C80000u, 44u}, // iou -> Latn
-    {0xA2280000u, 44u}, // iri -> Latn
-    {0x69730000u, 44u}, // is -> Latn
-    {0x69740000u, 44u}, // it -> Latn
+    {0x687A0000u, 46u}, // hz -> Latn
+    {0x69610000u, 46u}, // ia -> Latn
+    {0xB4080000u, 46u}, // ian -> Latn
+    {0xC4080000u, 46u}, // iar -> Latn
+    {0x80280000u, 46u}, // iba -> Latn
+    {0x84280000u, 46u}, // ibb -> Latn
+    {0xE0280000u, 46u}, // iby -> Latn
+    {0x80480000u, 46u}, // ica -> Latn
+    {0x9C480000u, 46u}, // ich -> Latn
+    {0x69640000u, 46u}, // id -> Latn
+    {0x8C680000u, 46u}, // idd -> Latn
+    {0xA0680000u, 46u}, // idi -> Latn
+    {0xD0680000u, 46u}, // idu -> Latn
+    {0x90A80000u, 46u}, // ife -> Latn
+    {0x69670000u, 46u}, // ig -> Latn
+    {0x84C80000u, 46u}, // igb -> Latn
+    {0x90C80000u, 46u}, // ige -> Latn
+    {0x69690000u, 96u}, // ii -> Yiii
+    {0xA5280000u, 46u}, // ijj -> Latn
+    {0x696B0000u, 46u}, // ik -> Latn
+    {0xA9480000u, 46u}, // ikk -> Latn
+    {0xCD480000u, 46u}, // ikt -> Latn
+    {0xD9480000u, 46u}, // ikw -> Latn
+    {0xDD480000u, 46u}, // ikx -> Latn
+    {0xB9680000u, 46u}, // ilo -> Latn
+    {0xB9880000u, 46u}, // imo -> Latn
+    {0x696E0000u, 46u}, // in -> Latn
+    {0x9DA80000u, 17u}, // inh -> Cyrl
+    {0x696F0000u, 46u}, // io -> Latn
+    {0xD1C80000u, 46u}, // iou -> Latn
+    {0xA2280000u, 46u}, // iri -> Latn
+    {0x69730000u, 46u}, // is -> Latn
+    {0x69740000u, 46u}, // it -> Latn
     {0x69750000u, 10u}, // iu -> Cans
-    {0x69770000u, 30u}, // iw -> Hebr
-    {0xB2C80000u, 44u}, // iwm -> Latn
-    {0xCAC80000u, 44u}, // iws -> Latn
-    {0x9F280000u, 44u}, // izh -> Latn
-    {0xA3280000u, 44u}, // izi -> Latn
-    {0x6A610000u, 35u}, // ja -> Jpan
-    {0x84090000u, 44u}, // jab -> Latn
-    {0xB0090000u, 44u}, // jam -> Latn
-    {0xB8290000u, 44u}, // jbo -> Latn
-    {0xD0290000u, 44u}, // jbu -> Latn
-    {0xB4890000u, 44u}, // jen -> Latn
-    {0xA8C90000u, 44u}, // jgk -> Latn
-    {0xB8C90000u, 44u}, // jgo -> Latn
-    {0x6A690000u, 30u}, // ji -> Hebr
-    {0x85090000u, 44u}, // jib -> Latn
-    {0x89890000u, 44u}, // jmc -> Latn
-    {0xAD890000u, 17u}, // jml -> Deva
-    {0x82290000u, 44u}, // jra -> Latn
-    {0xCE890000u, 44u}, // jut -> Latn
-    {0x6A760000u, 44u}, // jv -> Latn
-    {0x6A770000u, 44u}, // jw -> Latn
-    {0x6B610000u, 20u}, // ka -> Geor
-    {0x800A0000u, 16u}, // kaa -> Cyrl
-    {0x840A0000u, 44u}, // kab -> Latn
-    {0x880A0000u, 44u}, // kac -> Latn
-    {0x8C0A0000u, 44u}, // kad -> Latn
-    {0xA00A0000u, 44u}, // kai -> Latn
-    {0xA40A0000u, 44u}, // kaj -> Latn
-    {0xB00A0000u, 44u}, // kam -> Latn
-    {0xB80A0000u, 44u}, // kao -> Latn
-    {0x8C2A0000u, 16u}, // kbd -> Cyrl
-    {0xB02A0000u, 44u}, // kbm -> Latn
-    {0xBC2A0000u, 44u}, // kbp -> Latn
-    {0xC02A0000u, 44u}, // kbq -> Latn
-    {0xDC2A0000u, 44u}, // kbx -> Latn
+    {0x69770000u, 31u}, // iw -> Hebr
+    {0xB2C80000u, 46u}, // iwm -> Latn
+    {0xCAC80000u, 46u}, // iws -> Latn
+    {0x9F280000u, 46u}, // izh -> Latn
+    {0xA3280000u, 46u}, // izi -> Latn
+    {0x6A610000u, 36u}, // ja -> Jpan
+    {0x84090000u, 46u}, // jab -> Latn
+    {0xB0090000u, 46u}, // jam -> Latn
+    {0xB8290000u, 46u}, // jbo -> Latn
+    {0xD0290000u, 46u}, // jbu -> Latn
+    {0xB4890000u, 46u}, // jen -> Latn
+    {0xA8C90000u, 46u}, // jgk -> Latn
+    {0xB8C90000u, 46u}, // jgo -> Latn
+    {0x6A690000u, 31u}, // ji -> Hebr
+    {0x85090000u, 46u}, // jib -> Latn
+    {0x89890000u, 46u}, // jmc -> Latn
+    {0xAD890000u, 18u}, // jml -> Deva
+    {0x82290000u, 46u}, // jra -> Latn
+    {0xCE890000u, 46u}, // jut -> Latn
+    {0x6A760000u, 46u}, // jv -> Latn
+    {0x6A770000u, 46u}, // jw -> Latn
+    {0x6B610000u, 21u}, // ka -> Geor
+    {0x800A0000u, 17u}, // kaa -> Cyrl
+    {0x840A0000u, 46u}, // kab -> Latn
+    {0x880A0000u, 46u}, // kac -> Latn
+    {0x8C0A0000u, 46u}, // kad -> Latn
+    {0xA00A0000u, 46u}, // kai -> Latn
+    {0xA40A0000u, 46u}, // kaj -> Latn
+    {0xB00A0000u, 46u}, // kam -> Latn
+    {0xB80A0000u, 46u}, // kao -> Latn
+    {0x8C2A0000u, 17u}, // kbd -> Cyrl
+    {0xB02A0000u, 46u}, // kbm -> Latn
+    {0xBC2A0000u, 46u}, // kbp -> Latn
+    {0xC02A0000u, 46u}, // kbq -> Latn
+    {0xDC2A0000u, 46u}, // kbx -> Latn
     {0xE02A0000u,  1u}, // kby -> Arab
-    {0x984A0000u, 44u}, // kcg -> Latn
-    {0xA84A0000u, 44u}, // kck -> Latn
-    {0xAC4A0000u, 44u}, // kcl -> Latn
-    {0xCC4A0000u, 44u}, // kct -> Latn
-    {0x906A0000u, 44u}, // kde -> Latn
+    {0x984A0000u, 46u}, // kcg -> Latn
+    {0xA84A0000u, 46u}, // kck -> Latn
+    {0xAC4A0000u, 46u}, // kcl -> Latn
+    {0xCC4A0000u, 46u}, // kct -> Latn
+    {0x906A0000u, 46u}, // kde -> Latn
     {0x9C6A0000u,  1u}, // kdh -> Arab
-    {0xAC6A0000u, 44u}, // kdl -> Latn
-    {0xCC6A0000u, 87u}, // kdt -> Thai
-    {0x808A0000u, 44u}, // kea -> Latn
-    {0xB48A0000u, 44u}, // ken -> Latn
-    {0xE48A0000u, 44u}, // kez -> Latn
-    {0xB8AA0000u, 44u}, // kfo -> Latn
-    {0xC4AA0000u, 17u}, // kfr -> Deva
-    {0xE0AA0000u, 17u}, // kfy -> Deva
-    {0x6B670000u, 44u}, // kg -> Latn
-    {0x90CA0000u, 44u}, // kge -> Latn
-    {0x94CA0000u, 44u}, // kgf -> Latn
-    {0xBCCA0000u, 44u}, // kgp -> Latn
-    {0x80EA0000u, 44u}, // kha -> Latn
-    {0x84EA0000u, 80u}, // khb -> Talu
-    {0xB4EA0000u, 17u}, // khn -> Deva
-    {0xC0EA0000u, 44u}, // khq -> Latn
-    {0xC8EA0000u, 44u}, // khs -> Latn
-    {0xCCEA0000u, 56u}, // kht -> Mymr
+    {0xAC6A0000u, 46u}, // kdl -> Latn
+    {0xCC6A0000u, 89u}, // kdt -> Thai
+    {0x808A0000u, 46u}, // kea -> Latn
+    {0xB48A0000u, 46u}, // ken -> Latn
+    {0xE48A0000u, 46u}, // kez -> Latn
+    {0xB8AA0000u, 46u}, // kfo -> Latn
+    {0xC4AA0000u, 18u}, // kfr -> Deva
+    {0xE0AA0000u, 18u}, // kfy -> Deva
+    {0x6B670000u, 46u}, // kg -> Latn
+    {0x90CA0000u, 46u}, // kge -> Latn
+    {0x94CA0000u, 46u}, // kgf -> Latn
+    {0xBCCA0000u, 46u}, // kgp -> Latn
+    {0x80EA0000u, 46u}, // kha -> Latn
+    {0x84EA0000u, 82u}, // khb -> Talu
+    {0xB4EA0000u, 18u}, // khn -> Deva
+    {0xC0EA0000u, 46u}, // khq -> Latn
+    {0xC8EA0000u, 46u}, // khs -> Latn
+    {0xCCEA0000u, 58u}, // kht -> Mymr
     {0xD8EA0000u,  1u}, // khw -> Arab
-    {0xE4EA0000u, 44u}, // khz -> Latn
-    {0x6B690000u, 44u}, // ki -> Latn
-    {0xA50A0000u, 44u}, // kij -> Latn
-    {0xD10A0000u, 44u}, // kiu -> Latn
-    {0xD90A0000u, 44u}, // kiw -> Latn
-    {0x6B6A0000u, 44u}, // kj -> Latn
-    {0x8D2A0000u, 44u}, // kjd -> Latn
-    {0x992A0000u, 43u}, // kjg -> Laoo
-    {0xC92A0000u, 44u}, // kjs -> Latn
-    {0xE12A0000u, 44u}, // kjy -> Latn
-    {0x6B6B0000u, 16u}, // kk -> Cyrl
+    {0xE4EA0000u, 46u}, // khz -> Latn
+    {0x6B690000u, 46u}, // ki -> Latn
+    {0xA50A0000u, 46u}, // kij -> Latn
+    {0xD10A0000u, 46u}, // kiu -> Latn
+    {0xD90A0000u, 46u}, // kiw -> Latn
+    {0x6B6A0000u, 46u}, // kj -> Latn
+    {0x8D2A0000u, 46u}, // kjd -> Latn
+    {0x992A0000u, 45u}, // kjg -> Laoo
+    {0xC92A0000u, 46u}, // kjs -> Latn
+    {0xE12A0000u, 46u}, // kjy -> Latn
+    {0x6B6B0000u, 17u}, // kk -> Cyrl
     {0x6B6B4146u,  1u}, // kk-AF -> Arab
     {0x6B6B434Eu,  1u}, // kk-CN -> Arab
     {0x6B6B4952u,  1u}, // kk-IR -> Arab
     {0x6B6B4D4Eu,  1u}, // kk-MN -> Arab
-    {0x894A0000u, 44u}, // kkc -> Latn
-    {0xA54A0000u, 44u}, // kkj -> Latn
-    {0x6B6C0000u, 44u}, // kl -> Latn
-    {0xB56A0000u, 44u}, // kln -> Latn
-    {0xC16A0000u, 44u}, // klq -> Latn
-    {0xCD6A0000u, 44u}, // klt -> Latn
-    {0xDD6A0000u, 44u}, // klx -> Latn
-    {0x6B6D0000u, 39u}, // km -> Khmr
-    {0x858A0000u, 44u}, // kmb -> Latn
-    {0x9D8A0000u, 44u}, // kmh -> Latn
-    {0xB98A0000u, 44u}, // kmo -> Latn
-    {0xC98A0000u, 44u}, // kms -> Latn
-    {0xD18A0000u, 44u}, // kmu -> Latn
-    {0xD98A0000u, 44u}, // kmw -> Latn
-    {0x6B6E0000u, 40u}, // kn -> Knda
-    {0x95AA0000u, 44u}, // knf -> Latn
-    {0xBDAA0000u, 44u}, // knp -> Latn
-    {0x6B6F0000u, 41u}, // ko -> Kore
-    {0xA1CA0000u, 16u}, // koi -> Cyrl
-    {0xA9CA0000u, 17u}, // kok -> Deva
-    {0xADCA0000u, 44u}, // kol -> Latn
-    {0xC9CA0000u, 44u}, // kos -> Latn
-    {0xE5CA0000u, 44u}, // koz -> Latn
-    {0x91EA0000u, 44u}, // kpe -> Latn
-    {0x95EA0000u, 44u}, // kpf -> Latn
-    {0xB9EA0000u, 44u}, // kpo -> Latn
-    {0xC5EA0000u, 44u}, // kpr -> Latn
-    {0xDDEA0000u, 44u}, // kpx -> Latn
-    {0x860A0000u, 44u}, // kqb -> Latn
-    {0x960A0000u, 44u}, // kqf -> Latn
-    {0xCA0A0000u, 44u}, // kqs -> Latn
-    {0xE20A0000u, 19u}, // kqy -> Ethi
-    {0x6B720000u, 44u}, // kr -> Latn
-    {0x8A2A0000u, 16u}, // krc -> Cyrl
-    {0xA22A0000u, 44u}, // kri -> Latn
-    {0xA62A0000u, 44u}, // krj -> Latn
-    {0xAE2A0000u, 44u}, // krl -> Latn
-    {0xCA2A0000u, 44u}, // krs -> Latn
-    {0xD22A0000u, 17u}, // kru -> Deva
+    {0x894A0000u, 46u}, // kkc -> Latn
+    {0xA54A0000u, 46u}, // kkj -> Latn
+    {0x6B6C0000u, 46u}, // kl -> Latn
+    {0xB56A0000u, 46u}, // kln -> Latn
+    {0xC16A0000u, 46u}, // klq -> Latn
+    {0xCD6A0000u, 46u}, // klt -> Latn
+    {0xDD6A0000u, 46u}, // klx -> Latn
+    {0x6B6D0000u, 40u}, // km -> Khmr
+    {0x858A0000u, 46u}, // kmb -> Latn
+    {0x9D8A0000u, 46u}, // kmh -> Latn
+    {0xB98A0000u, 46u}, // kmo -> Latn
+    {0xC98A0000u, 46u}, // kms -> Latn
+    {0xD18A0000u, 46u}, // kmu -> Latn
+    {0xD98A0000u, 46u}, // kmw -> Latn
+    {0x6B6E0000u, 42u}, // kn -> Knda
+    {0x95AA0000u, 46u}, // knf -> Latn
+    {0xBDAA0000u, 46u}, // knp -> Latn
+    {0x6B6F0000u, 43u}, // ko -> Kore
+    {0xA1CA0000u, 17u}, // koi -> Cyrl
+    {0xA9CA0000u, 18u}, // kok -> Deva
+    {0xADCA0000u, 46u}, // kol -> Latn
+    {0xC9CA0000u, 46u}, // kos -> Latn
+    {0xE5CA0000u, 46u}, // koz -> Latn
+    {0x91EA0000u, 46u}, // kpe -> Latn
+    {0x95EA0000u, 46u}, // kpf -> Latn
+    {0xB9EA0000u, 46u}, // kpo -> Latn
+    {0xC5EA0000u, 46u}, // kpr -> Latn
+    {0xDDEA0000u, 46u}, // kpx -> Latn
+    {0x860A0000u, 46u}, // kqb -> Latn
+    {0x960A0000u, 46u}, // kqf -> Latn
+    {0xCA0A0000u, 46u}, // kqs -> Latn
+    {0xE20A0000u, 20u}, // kqy -> Ethi
+    {0x6B720000u, 46u}, // kr -> Latn
+    {0x8A2A0000u, 17u}, // krc -> Cyrl
+    {0xA22A0000u, 46u}, // kri -> Latn
+    {0xA62A0000u, 46u}, // krj -> Latn
+    {0xAE2A0000u, 46u}, // krl -> Latn
+    {0xCA2A0000u, 46u}, // krs -> Latn
+    {0xD22A0000u, 18u}, // kru -> Deva
     {0x6B730000u,  1u}, // ks -> Arab
-    {0x864A0000u, 44u}, // ksb -> Latn
-    {0x8E4A0000u, 44u}, // ksd -> Latn
-    {0x964A0000u, 44u}, // ksf -> Latn
-    {0x9E4A0000u, 44u}, // ksh -> Latn
-    {0xA64A0000u, 44u}, // ksj -> Latn
-    {0xC64A0000u, 44u}, // ksr -> Latn
-    {0x866A0000u, 19u}, // ktb -> Ethi
-    {0xB26A0000u, 44u}, // ktm -> Latn
-    {0xBA6A0000u, 44u}, // kto -> Latn
-    {0xC66A0000u, 44u}, // ktr -> Latn
-    {0x6B750000u, 44u}, // ku -> Latn
+    {0x864A0000u, 46u}, // ksb -> Latn
+    {0x8E4A0000u, 46u}, // ksd -> Latn
+    {0x964A0000u, 46u}, // ksf -> Latn
+    {0x9E4A0000u, 46u}, // ksh -> Latn
+    {0xA64A0000u, 46u}, // ksj -> Latn
+    {0xC64A0000u, 46u}, // ksr -> Latn
+    {0x866A0000u, 20u}, // ktb -> Ethi
+    {0xB26A0000u, 46u}, // ktm -> Latn
+    {0xBA6A0000u, 46u}, // kto -> Latn
+    {0xC66A0000u, 46u}, // ktr -> Latn
+    {0x6B750000u, 46u}, // ku -> Latn
     {0x6B754952u,  1u}, // ku-IR -> Arab
     {0x6B754C42u,  1u}, // ku-LB -> Arab
-    {0x868A0000u, 44u}, // kub -> Latn
-    {0x8E8A0000u, 44u}, // kud -> Latn
-    {0x928A0000u, 44u}, // kue -> Latn
-    {0xA68A0000u, 44u}, // kuj -> Latn
-    {0xB28A0000u, 16u}, // kum -> Cyrl
-    {0xB68A0000u, 44u}, // kun -> Latn
-    {0xBE8A0000u, 44u}, // kup -> Latn
-    {0xCA8A0000u, 44u}, // kus -> Latn
-    {0x6B760000u, 16u}, // kv -> Cyrl
-    {0x9AAA0000u, 44u}, // kvg -> Latn
-    {0xC6AA0000u, 44u}, // kvr -> Latn
+    {0x868A0000u, 46u}, // kub -> Latn
+    {0x8E8A0000u, 46u}, // kud -> Latn
+    {0x928A0000u, 46u}, // kue -> Latn
+    {0xA68A0000u, 46u}, // kuj -> Latn
+    {0xB28A0000u, 17u}, // kum -> Cyrl
+    {0xB68A0000u, 46u}, // kun -> Latn
+    {0xBE8A0000u, 46u}, // kup -> Latn
+    {0xCA8A0000u, 46u}, // kus -> Latn
+    {0x6B760000u, 17u}, // kv -> Cyrl
+    {0x9AAA0000u, 46u}, // kvg -> Latn
+    {0xC6AA0000u, 46u}, // kvr -> Latn
     {0xDEAA0000u,  1u}, // kvx -> Arab
-    {0x6B770000u, 44u}, // kw -> Latn
-    {0xA6CA0000u, 44u}, // kwj -> Latn
-    {0xBACA0000u, 44u}, // kwo -> Latn
-    {0xC2CA0000u, 44u}, // kwq -> Latn
-    {0x82EA0000u, 44u}, // kxa -> Latn
-    {0x8AEA0000u, 19u}, // kxc -> Ethi
-    {0x92EA0000u, 44u}, // kxe -> Latn
-    {0xB2EA0000u, 87u}, // kxm -> Thai
+    {0x6B770000u, 46u}, // kw -> Latn
+    {0xA6CA0000u, 46u}, // kwj -> Latn
+    {0xBACA0000u, 46u}, // kwo -> Latn
+    {0xC2CA0000u, 46u}, // kwq -> Latn
+    {0x82EA0000u, 46u}, // kxa -> Latn
+    {0x8AEA0000u, 20u}, // kxc -> Ethi
+    {0x92EA0000u, 46u}, // kxe -> Latn
+    {0xB2EA0000u, 89u}, // kxm -> Thai
     {0xBEEA0000u,  1u}, // kxp -> Arab
-    {0xDAEA0000u, 44u}, // kxw -> Latn
-    {0xE6EA0000u, 44u}, // kxz -> Latn
-    {0x6B790000u, 16u}, // ky -> Cyrl
+    {0xDAEA0000u, 46u}, // kxw -> Latn
+    {0xE6EA0000u, 46u}, // kxz -> Latn
+    {0x6B790000u, 17u}, // ky -> Cyrl
     {0x6B79434Eu,  1u}, // ky-CN -> Arab
-    {0x6B795452u, 44u}, // ky-TR -> Latn
-    {0x930A0000u, 44u}, // kye -> Latn
-    {0xDF0A0000u, 44u}, // kyx -> Latn
-    {0xA72A0000u, 44u}, // kzj -> Latn
-    {0xC72A0000u, 44u}, // kzr -> Latn
-    {0xCF2A0000u, 44u}, // kzt -> Latn
-    {0x6C610000u, 44u}, // la -> Latn
-    {0x840B0000u, 46u}, // lab -> Lina
-    {0x8C0B0000u, 30u}, // lad -> Hebr
-    {0x980B0000u, 44u}, // lag -> Latn
+    {0x6B795452u, 46u}, // ky-TR -> Latn
+    {0x930A0000u, 46u}, // kye -> Latn
+    {0xDF0A0000u, 46u}, // kyx -> Latn
+    {0xA72A0000u, 46u}, // kzj -> Latn
+    {0xC72A0000u, 46u}, // kzr -> Latn
+    {0xCF2A0000u, 46u}, // kzt -> Latn
+    {0x6C610000u, 46u}, // la -> Latn
+    {0x840B0000u, 48u}, // lab -> Lina
+    {0x8C0B0000u, 31u}, // lad -> Hebr
+    {0x980B0000u, 46u}, // lag -> Latn
     {0x9C0B0000u,  1u}, // lah -> Arab
-    {0xA40B0000u, 44u}, // laj -> Latn
-    {0xC80B0000u, 44u}, // las -> Latn
-    {0x6C620000u, 44u}, // lb -> Latn
-    {0x902B0000u, 16u}, // lbe -> Cyrl
-    {0xD02B0000u, 44u}, // lbu -> Latn
-    {0xD82B0000u, 44u}, // lbw -> Latn
-    {0xB04B0000u, 44u}, // lcm -> Latn
-    {0xBC4B0000u, 87u}, // lcp -> Thai
-    {0x846B0000u, 44u}, // ldb -> Latn
-    {0x8C8B0000u, 44u}, // led -> Latn
-    {0x908B0000u, 44u}, // lee -> Latn
-    {0xB08B0000u, 44u}, // lem -> Latn
-    {0xBC8B0000u, 45u}, // lep -> Lepc
-    {0xC08B0000u, 44u}, // leq -> Latn
-    {0xD08B0000u, 44u}, // leu -> Latn
-    {0xE48B0000u, 16u}, // lez -> Cyrl
-    {0x6C670000u, 44u}, // lg -> Latn
-    {0x98CB0000u, 44u}, // lgg -> Latn
-    {0x6C690000u, 44u}, // li -> Latn
-    {0x810B0000u, 44u}, // lia -> Latn
-    {0x8D0B0000u, 44u}, // lid -> Latn
-    {0x950B0000u, 17u}, // lif -> Deva
-    {0x990B0000u, 44u}, // lig -> Latn
-    {0x9D0B0000u, 44u}, // lih -> Latn
-    {0xA50B0000u, 44u}, // lij -> Latn
-    {0xC90B0000u, 47u}, // lis -> Lisu
-    {0xBD2B0000u, 44u}, // ljp -> Latn
+    {0xA40B0000u, 46u}, // laj -> Latn
+    {0xC80B0000u, 46u}, // las -> Latn
+    {0x6C620000u, 46u}, // lb -> Latn
+    {0x902B0000u, 17u}, // lbe -> Cyrl
+    {0xD02B0000u, 46u}, // lbu -> Latn
+    {0xD82B0000u, 46u}, // lbw -> Latn
+    {0xB04B0000u, 46u}, // lcm -> Latn
+    {0xBC4B0000u, 89u}, // lcp -> Thai
+    {0x846B0000u, 46u}, // ldb -> Latn
+    {0x8C8B0000u, 46u}, // led -> Latn
+    {0x908B0000u, 46u}, // lee -> Latn
+    {0xB08B0000u, 46u}, // lem -> Latn
+    {0xBC8B0000u, 47u}, // lep -> Lepc
+    {0xC08B0000u, 46u}, // leq -> Latn
+    {0xD08B0000u, 46u}, // leu -> Latn
+    {0xE48B0000u, 17u}, // lez -> Cyrl
+    {0x6C670000u, 46u}, // lg -> Latn
+    {0x98CB0000u, 46u}, // lgg -> Latn
+    {0x6C690000u, 46u}, // li -> Latn
+    {0x810B0000u, 46u}, // lia -> Latn
+    {0x8D0B0000u, 46u}, // lid -> Latn
+    {0x950B0000u, 18u}, // lif -> Deva
+    {0x990B0000u, 46u}, // lig -> Latn
+    {0x9D0B0000u, 46u}, // lih -> Latn
+    {0xA50B0000u, 46u}, // lij -> Latn
+    {0xC90B0000u, 49u}, // lis -> Lisu
+    {0xBD2B0000u, 46u}, // ljp -> Latn
     {0xA14B0000u,  1u}, // lki -> Arab
-    {0xCD4B0000u, 44u}, // lkt -> Latn
-    {0x916B0000u, 44u}, // lle -> Latn
-    {0xB56B0000u, 44u}, // lln -> Latn
-    {0xB58B0000u, 84u}, // lmn -> Telu
-    {0xB98B0000u, 44u}, // lmo -> Latn
-    {0xBD8B0000u, 44u}, // lmp -> Latn
-    {0x6C6E0000u, 44u}, // ln -> Latn
-    {0xC9AB0000u, 44u}, // lns -> Latn
-    {0xD1AB0000u, 44u}, // lnu -> Latn
-    {0x6C6F0000u, 43u}, // lo -> Laoo
-    {0xA5CB0000u, 44u}, // loj -> Latn
-    {0xA9CB0000u, 44u}, // lok -> Latn
-    {0xADCB0000u, 44u}, // lol -> Latn
-    {0xC5CB0000u, 44u}, // lor -> Latn
-    {0xC9CB0000u, 44u}, // los -> Latn
-    {0xE5CB0000u, 44u}, // loz -> Latn
+    {0xCD4B0000u, 46u}, // lkt -> Latn
+    {0x916B0000u, 46u}, // lle -> Latn
+    {0xB56B0000u, 46u}, // lln -> Latn
+    {0xB58B0000u, 86u}, // lmn -> Telu
+    {0xB98B0000u, 46u}, // lmo -> Latn
+    {0xBD8B0000u, 46u}, // lmp -> Latn
+    {0x6C6E0000u, 46u}, // ln -> Latn
+    {0xC9AB0000u, 46u}, // lns -> Latn
+    {0xD1AB0000u, 46u}, // lnu -> Latn
+    {0x6C6F0000u, 45u}, // lo -> Laoo
+    {0xA5CB0000u, 46u}, // loj -> Latn
+    {0xA9CB0000u, 46u}, // lok -> Latn
+    {0xADCB0000u, 46u}, // lol -> Latn
+    {0xC5CB0000u, 46u}, // lor -> Latn
+    {0xC9CB0000u, 46u}, // los -> Latn
+    {0xE5CB0000u, 46u}, // loz -> Latn
     {0x8A2B0000u,  1u}, // lrc -> Arab
-    {0x6C740000u, 44u}, // lt -> Latn
-    {0x9A6B0000u, 44u}, // ltg -> Latn
-    {0x6C750000u, 44u}, // lu -> Latn
-    {0x828B0000u, 44u}, // lua -> Latn
-    {0xBA8B0000u, 44u}, // luo -> Latn
-    {0xE28B0000u, 44u}, // luy -> Latn
+    {0x6C740000u, 46u}, // lt -> Latn
+    {0x9A6B0000u, 46u}, // ltg -> Latn
+    {0x6C750000u, 46u}, // lu -> Latn
+    {0x828B0000u, 46u}, // lua -> Latn
+    {0xBA8B0000u, 46u}, // luo -> Latn
+    {0xE28B0000u, 46u}, // luy -> Latn
     {0xE68B0000u,  1u}, // luz -> Arab
-    {0x6C760000u, 44u}, // lv -> Latn
-    {0xAECB0000u, 87u}, // lwl -> Thai
-    {0x9F2B0000u, 27u}, // lzh -> Hans
-    {0xE72B0000u, 44u}, // lzz -> Latn
-    {0x8C0C0000u, 44u}, // mad -> Latn
-    {0x940C0000u, 44u}, // maf -> Latn
-    {0x980C0000u, 17u}, // mag -> Deva
-    {0xA00C0000u, 17u}, // mai -> Deva
-    {0xA80C0000u, 44u}, // mak -> Latn
-    {0xB40C0000u, 44u}, // man -> Latn
-    {0xB40C474Eu, 58u}, // man-GN -> Nkoo
-    {0xC80C0000u, 44u}, // mas -> Latn
-    {0xD80C0000u, 44u}, // maw -> Latn
-    {0xE40C0000u, 44u}, // maz -> Latn
-    {0x9C2C0000u, 44u}, // mbh -> Latn
-    {0xB82C0000u, 44u}, // mbo -> Latn
-    {0xC02C0000u, 44u}, // mbq -> Latn
-    {0xD02C0000u, 44u}, // mbu -> Latn
-    {0xD82C0000u, 44u}, // mbw -> Latn
-    {0xA04C0000u, 44u}, // mci -> Latn
-    {0xBC4C0000u, 44u}, // mcp -> Latn
-    {0xC04C0000u, 44u}, // mcq -> Latn
-    {0xC44C0000u, 44u}, // mcr -> Latn
-    {0xD04C0000u, 44u}, // mcu -> Latn
-    {0x806C0000u, 44u}, // mda -> Latn
+    {0x6C760000u, 46u}, // lv -> Latn
+    {0xAECB0000u, 89u}, // lwl -> Thai
+    {0x9F2B0000u, 28u}, // lzh -> Hans
+    {0xE72B0000u, 46u}, // lzz -> Latn
+    {0x8C0C0000u, 46u}, // mad -> Latn
+    {0x940C0000u, 46u}, // maf -> Latn
+    {0x980C0000u, 18u}, // mag -> Deva
+    {0xA00C0000u, 18u}, // mai -> Deva
+    {0xA80C0000u, 46u}, // mak -> Latn
+    {0xB40C0000u, 46u}, // man -> Latn
+    {0xB40C474Eu, 60u}, // man-GN -> Nkoo
+    {0xC80C0000u, 46u}, // mas -> Latn
+    {0xD80C0000u, 46u}, // maw -> Latn
+    {0xE40C0000u, 46u}, // maz -> Latn
+    {0x9C2C0000u, 46u}, // mbh -> Latn
+    {0xB82C0000u, 46u}, // mbo -> Latn
+    {0xC02C0000u, 46u}, // mbq -> Latn
+    {0xD02C0000u, 46u}, // mbu -> Latn
+    {0xD82C0000u, 46u}, // mbw -> Latn
+    {0xA04C0000u, 46u}, // mci -> Latn
+    {0xBC4C0000u, 46u}, // mcp -> Latn
+    {0xC04C0000u, 46u}, // mcq -> Latn
+    {0xC44C0000u, 46u}, // mcr -> Latn
+    {0xD04C0000u, 46u}, // mcu -> Latn
+    {0x806C0000u, 46u}, // mda -> Latn
     {0x906C0000u,  1u}, // mde -> Arab
-    {0x946C0000u, 16u}, // mdf -> Cyrl
-    {0x9C6C0000u, 44u}, // mdh -> Latn
-    {0xA46C0000u, 44u}, // mdj -> Latn
-    {0xC46C0000u, 44u}, // mdr -> Latn
-    {0xDC6C0000u, 19u}, // mdx -> Ethi
-    {0x8C8C0000u, 44u}, // med -> Latn
-    {0x908C0000u, 44u}, // mee -> Latn
-    {0xA88C0000u, 44u}, // mek -> Latn
-    {0xB48C0000u, 44u}, // men -> Latn
-    {0xC48C0000u, 44u}, // mer -> Latn
-    {0xCC8C0000u, 44u}, // met -> Latn
-    {0xD08C0000u, 44u}, // meu -> Latn
+    {0x946C0000u, 17u}, // mdf -> Cyrl
+    {0x9C6C0000u, 46u}, // mdh -> Latn
+    {0xA46C0000u, 46u}, // mdj -> Latn
+    {0xC46C0000u, 46u}, // mdr -> Latn
+    {0xDC6C0000u, 20u}, // mdx -> Ethi
+    {0x8C8C0000u, 46u}, // med -> Latn
+    {0x908C0000u, 46u}, // mee -> Latn
+    {0xA88C0000u, 46u}, // mek -> Latn
+    {0xB48C0000u, 46u}, // men -> Latn
+    {0xC48C0000u, 46u}, // mer -> Latn
+    {0xCC8C0000u, 46u}, // met -> Latn
+    {0xD08C0000u, 46u}, // meu -> Latn
     {0x80AC0000u,  1u}, // mfa -> Arab
-    {0x90AC0000u, 44u}, // mfe -> Latn
-    {0xB4AC0000u, 44u}, // mfn -> Latn
-    {0xB8AC0000u, 44u}, // mfo -> Latn
-    {0xC0AC0000u, 44u}, // mfq -> Latn
-    {0x6D670000u, 44u}, // mg -> Latn
-    {0x9CCC0000u, 44u}, // mgh -> Latn
-    {0xACCC0000u, 44u}, // mgl -> Latn
-    {0xB8CC0000u, 44u}, // mgo -> Latn
-    {0xBCCC0000u, 17u}, // mgp -> Deva
-    {0xE0CC0000u, 44u}, // mgy -> Latn
-    {0x6D680000u, 44u}, // mh -> Latn
-    {0xA0EC0000u, 44u}, // mhi -> Latn
-    {0xACEC0000u, 44u}, // mhl -> Latn
-    {0x6D690000u, 44u}, // mi -> Latn
-    {0x950C0000u, 44u}, // mif -> Latn
-    {0xB50C0000u, 44u}, // min -> Latn
-    {0xC90C0000u, 29u}, // mis -> Hatr
-    {0xD90C0000u, 44u}, // miw -> Latn
-    {0x6D6B0000u, 16u}, // mk -> Cyrl
+    {0x90AC0000u, 46u}, // mfe -> Latn
+    {0xB4AC0000u, 46u}, // mfn -> Latn
+    {0xB8AC0000u, 46u}, // mfo -> Latn
+    {0xC0AC0000u, 46u}, // mfq -> Latn
+    {0x6D670000u, 46u}, // mg -> Latn
+    {0x9CCC0000u, 46u}, // mgh -> Latn
+    {0xACCC0000u, 46u}, // mgl -> Latn
+    {0xB8CC0000u, 46u}, // mgo -> Latn
+    {0xBCCC0000u, 18u}, // mgp -> Deva
+    {0xE0CC0000u, 46u}, // mgy -> Latn
+    {0x6D680000u, 46u}, // mh -> Latn
+    {0xA0EC0000u, 46u}, // mhi -> Latn
+    {0xACEC0000u, 46u}, // mhl -> Latn
+    {0x6D690000u, 46u}, // mi -> Latn
+    {0x950C0000u, 46u}, // mif -> Latn
+    {0xB50C0000u, 46u}, // min -> Latn
+    {0xC90C0000u, 30u}, // mis -> Hatr
+    {0xD90C0000u, 46u}, // miw -> Latn
+    {0x6D6B0000u, 17u}, // mk -> Cyrl
     {0xA14C0000u,  1u}, // mki -> Arab
-    {0xAD4C0000u, 44u}, // mkl -> Latn
-    {0xBD4C0000u, 44u}, // mkp -> Latn
-    {0xD94C0000u, 44u}, // mkw -> Latn
-    {0x6D6C0000u, 53u}, // ml -> Mlym
-    {0x916C0000u, 44u}, // mle -> Latn
-    {0xBD6C0000u, 44u}, // mlp -> Latn
-    {0xC96C0000u, 44u}, // mls -> Latn
-    {0xB98C0000u, 44u}, // mmo -> Latn
-    {0xD18C0000u, 44u}, // mmu -> Latn
-    {0xDD8C0000u, 44u}, // mmx -> Latn
-    {0x6D6E0000u, 16u}, // mn -> Cyrl
-    {0x6D6E434Eu, 54u}, // mn-CN -> Mong
-    {0x81AC0000u, 44u}, // mna -> Latn
-    {0x95AC0000u, 44u}, // mnf -> Latn
+    {0xAD4C0000u, 46u}, // mkl -> Latn
+    {0xBD4C0000u, 46u}, // mkp -> Latn
+    {0xD94C0000u, 46u}, // mkw -> Latn
+    {0x6D6C0000u, 55u}, // ml -> Mlym
+    {0x916C0000u, 46u}, // mle -> Latn
+    {0xBD6C0000u, 46u}, // mlp -> Latn
+    {0xC96C0000u, 46u}, // mls -> Latn
+    {0xB98C0000u, 46u}, // mmo -> Latn
+    {0xD18C0000u, 46u}, // mmu -> Latn
+    {0xDD8C0000u, 46u}, // mmx -> Latn
+    {0x6D6E0000u, 17u}, // mn -> Cyrl
+    {0x6D6E434Eu, 56u}, // mn-CN -> Mong
+    {0x81AC0000u, 46u}, // mna -> Latn
+    {0x95AC0000u, 46u}, // mnf -> Latn
     {0xA1AC0000u,  7u}, // mni -> Beng
-    {0xD9AC0000u, 56u}, // mnw -> Mymr
-    {0x6D6F0000u, 44u}, // mo -> Latn
-    {0x81CC0000u, 44u}, // moa -> Latn
-    {0x91CC0000u, 44u}, // moe -> Latn
-    {0x9DCC0000u, 44u}, // moh -> Latn
-    {0xC9CC0000u, 44u}, // mos -> Latn
-    {0xDDCC0000u, 44u}, // mox -> Latn
-    {0xBDEC0000u, 44u}, // mpp -> Latn
-    {0xC9EC0000u, 44u}, // mps -> Latn
-    {0xCDEC0000u, 44u}, // mpt -> Latn
-    {0xDDEC0000u, 44u}, // mpx -> Latn
-    {0xAE0C0000u, 44u}, // mql -> Latn
-    {0x6D720000u, 17u}, // mr -> Deva
-    {0x8E2C0000u, 17u}, // mrd -> Deva
-    {0xA62C0000u, 16u}, // mrj -> Cyrl
-    {0xBA2C0000u, 55u}, // mro -> Mroo
-    {0x6D730000u, 44u}, // ms -> Latn
+    {0xD9AC0000u, 58u}, // mnw -> Mymr
+    {0x6D6F0000u, 46u}, // mo -> Latn
+    {0x81CC0000u, 46u}, // moa -> Latn
+    {0x91CC0000u, 46u}, // moe -> Latn
+    {0x9DCC0000u, 46u}, // moh -> Latn
+    {0xC9CC0000u, 46u}, // mos -> Latn
+    {0xDDCC0000u, 46u}, // mox -> Latn
+    {0xBDEC0000u, 46u}, // mpp -> Latn
+    {0xC9EC0000u, 46u}, // mps -> Latn
+    {0xCDEC0000u, 46u}, // mpt -> Latn
+    {0xDDEC0000u, 46u}, // mpx -> Latn
+    {0xAE0C0000u, 46u}, // mql -> Latn
+    {0x6D720000u, 18u}, // mr -> Deva
+    {0x8E2C0000u, 18u}, // mrd -> Deva
+    {0xA62C0000u, 17u}, // mrj -> Cyrl
+    {0xBA2C0000u, 57u}, // mro -> Mroo
+    {0x6D730000u, 46u}, // ms -> Latn
     {0x6D734343u,  1u}, // ms-CC -> Arab
     {0x6D734944u,  1u}, // ms-ID -> Arab
-    {0x6D740000u, 44u}, // mt -> Latn
-    {0x8A6C0000u, 44u}, // mtc -> Latn
-    {0x966C0000u, 44u}, // mtf -> Latn
-    {0xA26C0000u, 44u}, // mti -> Latn
-    {0xC66C0000u, 17u}, // mtr -> Deva
-    {0x828C0000u, 44u}, // mua -> Latn
-    {0xC68C0000u, 44u}, // mur -> Latn
-    {0xCA8C0000u, 44u}, // mus -> Latn
-    {0x82AC0000u, 44u}, // mva -> Latn
-    {0xB6AC0000u, 44u}, // mvn -> Latn
+    {0x6D740000u, 46u}, // mt -> Latn
+    {0x8A6C0000u, 46u}, // mtc -> Latn
+    {0x966C0000u, 46u}, // mtf -> Latn
+    {0xA26C0000u, 46u}, // mti -> Latn
+    {0xC66C0000u, 18u}, // mtr -> Deva
+    {0x828C0000u, 46u}, // mua -> Latn
+    {0xC68C0000u, 46u}, // mur -> Latn
+    {0xCA8C0000u, 46u}, // mus -> Latn
+    {0x82AC0000u, 46u}, // mva -> Latn
+    {0xB6AC0000u, 46u}, // mvn -> Latn
     {0xE2AC0000u,  1u}, // mvy -> Arab
-    {0xAACC0000u, 44u}, // mwk -> Latn
-    {0xC6CC0000u, 17u}, // mwr -> Deva
-    {0xD6CC0000u, 44u}, // mwv -> Latn
-    {0xDACC0000u, 33u}, // mww -> Hmnp
-    {0x8AEC0000u, 44u}, // mxc -> Latn
-    {0xB2EC0000u, 44u}, // mxm -> Latn
-    {0x6D790000u, 56u}, // my -> Mymr
-    {0xAB0C0000u, 44u}, // myk -> Latn
-    {0xB30C0000u, 19u}, // mym -> Ethi
-    {0xD70C0000u, 16u}, // myv -> Cyrl
-    {0xDB0C0000u, 44u}, // myw -> Latn
-    {0xDF0C0000u, 44u}, // myx -> Latn
-    {0xE70C0000u, 50u}, // myz -> Mand
-    {0xAB2C0000u, 44u}, // mzk -> Latn
-    {0xB32C0000u, 44u}, // mzm -> Latn
+    {0xAACC0000u, 46u}, // mwk -> Latn
+    {0xC6CC0000u, 18u}, // mwr -> Deva
+    {0xD6CC0000u, 46u}, // mwv -> Latn
+    {0xDACC0000u, 34u}, // mww -> Hmnp
+    {0x8AEC0000u, 46u}, // mxc -> Latn
+    {0xB2EC0000u, 46u}, // mxm -> Latn
+    {0x6D790000u, 58u}, // my -> Mymr
+    {0xAB0C0000u, 46u}, // myk -> Latn
+    {0xB30C0000u, 20u}, // mym -> Ethi
+    {0xD70C0000u, 17u}, // myv -> Cyrl
+    {0xDB0C0000u, 46u}, // myw -> Latn
+    {0xDF0C0000u, 46u}, // myx -> Latn
+    {0xE70C0000u, 52u}, // myz -> Mand
+    {0xAB2C0000u, 46u}, // mzk -> Latn
+    {0xB32C0000u, 46u}, // mzm -> Latn
     {0xB72C0000u,  1u}, // mzn -> Arab
-    {0xBF2C0000u, 44u}, // mzp -> Latn
-    {0xDB2C0000u, 44u}, // mzw -> Latn
-    {0xE72C0000u, 44u}, // mzz -> Latn
-    {0x6E610000u, 44u}, // na -> Latn
-    {0x880D0000u, 44u}, // nac -> Latn
-    {0x940D0000u, 44u}, // naf -> Latn
-    {0xA80D0000u, 44u}, // nak -> Latn
-    {0xB40D0000u, 27u}, // nan -> Hans
-    {0xBC0D0000u, 44u}, // nap -> Latn
-    {0xC00D0000u, 44u}, // naq -> Latn
-    {0xC80D0000u, 44u}, // nas -> Latn
-    {0x6E620000u, 44u}, // nb -> Latn
-    {0x804D0000u, 44u}, // nca -> Latn
-    {0x904D0000u, 44u}, // nce -> Latn
-    {0x944D0000u, 44u}, // ncf -> Latn
-    {0x9C4D0000u, 44u}, // nch -> Latn
-    {0xB84D0000u, 44u}, // nco -> Latn
-    {0xD04D0000u, 44u}, // ncu -> Latn
-    {0x6E640000u, 44u}, // nd -> Latn
-    {0x886D0000u, 44u}, // ndc -> Latn
-    {0xC86D0000u, 44u}, // nds -> Latn
-    {0x6E650000u, 17u}, // ne -> Deva
-    {0x848D0000u, 44u}, // neb -> Latn
-    {0xD88D0000u, 17u}, // new -> Deva
-    {0xDC8D0000u, 44u}, // nex -> Latn
-    {0xC4AD0000u, 44u}, // nfr -> Latn
-    {0x6E670000u, 44u}, // ng -> Latn
-    {0x80CD0000u, 44u}, // nga -> Latn
-    {0x84CD0000u, 44u}, // ngb -> Latn
-    {0xACCD0000u, 44u}, // ngl -> Latn
-    {0x84ED0000u, 44u}, // nhb -> Latn
-    {0x90ED0000u, 44u}, // nhe -> Latn
-    {0xD8ED0000u, 44u}, // nhw -> Latn
-    {0x950D0000u, 44u}, // nif -> Latn
-    {0xA10D0000u, 44u}, // nii -> Latn
-    {0xA50D0000u, 44u}, // nij -> Latn
-    {0xB50D0000u, 44u}, // nin -> Latn
-    {0xD10D0000u, 44u}, // niu -> Latn
-    {0xE10D0000u, 44u}, // niy -> Latn
-    {0xE50D0000u, 44u}, // niz -> Latn
-    {0xB92D0000u, 44u}, // njo -> Latn
-    {0x994D0000u, 44u}, // nkg -> Latn
-    {0xB94D0000u, 44u}, // nko -> Latn
-    {0x6E6C0000u, 44u}, // nl -> Latn
-    {0x998D0000u, 44u}, // nmg -> Latn
-    {0xE58D0000u, 44u}, // nmz -> Latn
-    {0x6E6E0000u, 44u}, // nn -> Latn
-    {0x95AD0000u, 44u}, // nnf -> Latn
-    {0x9DAD0000u, 44u}, // nnh -> Latn
-    {0xA9AD0000u, 44u}, // nnk -> Latn
-    {0xB1AD0000u, 44u}, // nnm -> Latn
-    {0xBDAD0000u, 91u}, // nnp -> Wcho
-    {0x6E6F0000u, 44u}, // no -> Latn
-    {0x8DCD0000u, 42u}, // nod -> Lana
-    {0x91CD0000u, 17u}, // noe -> Deva
-    {0xB5CD0000u, 69u}, // non -> Runr
-    {0xBDCD0000u, 44u}, // nop -> Latn
-    {0xD1CD0000u, 44u}, // nou -> Latn
-    {0xBA0D0000u, 58u}, // nqo -> Nkoo
-    {0x6E720000u, 44u}, // nr -> Latn
-    {0x862D0000u, 44u}, // nrb -> Latn
+    {0xBF2C0000u, 46u}, // mzp -> Latn
+    {0xDB2C0000u, 46u}, // mzw -> Latn
+    {0xE72C0000u, 46u}, // mzz -> Latn
+    {0x6E610000u, 46u}, // na -> Latn
+    {0x880D0000u, 46u}, // nac -> Latn
+    {0x940D0000u, 46u}, // naf -> Latn
+    {0xA80D0000u, 46u}, // nak -> Latn
+    {0xB40D0000u, 28u}, // nan -> Hans
+    {0xBC0D0000u, 46u}, // nap -> Latn
+    {0xC00D0000u, 46u}, // naq -> Latn
+    {0xC80D0000u, 46u}, // nas -> Latn
+    {0x6E620000u, 46u}, // nb -> Latn
+    {0x804D0000u, 46u}, // nca -> Latn
+    {0x904D0000u, 46u}, // nce -> Latn
+    {0x944D0000u, 46u}, // ncf -> Latn
+    {0x9C4D0000u, 46u}, // nch -> Latn
+    {0xB84D0000u, 46u}, // nco -> Latn
+    {0xD04D0000u, 46u}, // ncu -> Latn
+    {0x6E640000u, 46u}, // nd -> Latn
+    {0x886D0000u, 46u}, // ndc -> Latn
+    {0xC86D0000u, 46u}, // nds -> Latn
+    {0x6E650000u, 18u}, // ne -> Deva
+    {0x848D0000u, 46u}, // neb -> Latn
+    {0xD88D0000u, 18u}, // new -> Deva
+    {0xDC8D0000u, 46u}, // nex -> Latn
+    {0xC4AD0000u, 46u}, // nfr -> Latn
+    {0x6E670000u, 46u}, // ng -> Latn
+    {0x80CD0000u, 46u}, // nga -> Latn
+    {0x84CD0000u, 46u}, // ngb -> Latn
+    {0xACCD0000u, 46u}, // ngl -> Latn
+    {0x84ED0000u, 46u}, // nhb -> Latn
+    {0x90ED0000u, 46u}, // nhe -> Latn
+    {0xD8ED0000u, 46u}, // nhw -> Latn
+    {0x950D0000u, 46u}, // nif -> Latn
+    {0xA10D0000u, 46u}, // nii -> Latn
+    {0xA50D0000u, 46u}, // nij -> Latn
+    {0xB50D0000u, 46u}, // nin -> Latn
+    {0xD10D0000u, 46u}, // niu -> Latn
+    {0xE10D0000u, 46u}, // niy -> Latn
+    {0xE50D0000u, 46u}, // niz -> Latn
+    {0xB92D0000u, 46u}, // njo -> Latn
+    {0x994D0000u, 46u}, // nkg -> Latn
+    {0xB94D0000u, 46u}, // nko -> Latn
+    {0x6E6C0000u, 46u}, // nl -> Latn
+    {0x998D0000u, 46u}, // nmg -> Latn
+    {0xE58D0000u, 46u}, // nmz -> Latn
+    {0x6E6E0000u, 46u}, // nn -> Latn
+    {0x95AD0000u, 46u}, // nnf -> Latn
+    {0x9DAD0000u, 46u}, // nnh -> Latn
+    {0xA9AD0000u, 46u}, // nnk -> Latn
+    {0xB1AD0000u, 46u}, // nnm -> Latn
+    {0xBDAD0000u, 93u}, // nnp -> Wcho
+    {0x6E6F0000u, 46u}, // no -> Latn
+    {0x8DCD0000u, 44u}, // nod -> Lana
+    {0x91CD0000u, 18u}, // noe -> Deva
+    {0xB5CD0000u, 71u}, // non -> Runr
+    {0xBDCD0000u, 46u}, // nop -> Latn
+    {0xD1CD0000u, 46u}, // nou -> Latn
+    {0xBA0D0000u, 60u}, // nqo -> Nkoo
+    {0x6E720000u, 46u}, // nr -> Latn
+    {0x862D0000u, 46u}, // nrb -> Latn
     {0xAA4D0000u, 10u}, // nsk -> Cans
-    {0xB64D0000u, 44u}, // nsn -> Latn
-    {0xBA4D0000u, 44u}, // nso -> Latn
-    {0xCA4D0000u, 44u}, // nss -> Latn
-    {0xB26D0000u, 44u}, // ntm -> Latn
-    {0xC66D0000u, 44u}, // ntr -> Latn
-    {0xA28D0000u, 44u}, // nui -> Latn
-    {0xBE8D0000u, 44u}, // nup -> Latn
-    {0xCA8D0000u, 44u}, // nus -> Latn
-    {0xD68D0000u, 44u}, // nuv -> Latn
-    {0xDE8D0000u, 44u}, // nux -> Latn
-    {0x6E760000u, 44u}, // nv -> Latn
-    {0x86CD0000u, 44u}, // nwb -> Latn
-    {0xC2ED0000u, 44u}, // nxq -> Latn
-    {0xC6ED0000u, 44u}, // nxr -> Latn
-    {0x6E790000u, 44u}, // ny -> Latn
-    {0xB30D0000u, 44u}, // nym -> Latn
-    {0xB70D0000u, 44u}, // nyn -> Latn
-    {0xA32D0000u, 44u}, // nzi -> Latn
-    {0x6F630000u, 44u}, // oc -> Latn
-    {0x88CE0000u, 44u}, // ogc -> Latn
-    {0xC54E0000u, 44u}, // okr -> Latn
-    {0xD54E0000u, 44u}, // okv -> Latn
-    {0x6F6D0000u, 44u}, // om -> Latn
-    {0x99AE0000u, 44u}, // ong -> Latn
-    {0xB5AE0000u, 44u}, // onn -> Latn
-    {0xC9AE0000u, 44u}, // ons -> Latn
-    {0xB1EE0000u, 44u}, // opm -> Latn
-    {0x6F720000u, 62u}, // or -> Orya
-    {0xBA2E0000u, 44u}, // oro -> Latn
+    {0xB64D0000u, 46u}, // nsn -> Latn
+    {0xBA4D0000u, 46u}, // nso -> Latn
+    {0xCA4D0000u, 46u}, // nss -> Latn
+    {0xB26D0000u, 46u}, // ntm -> Latn
+    {0xC66D0000u, 46u}, // ntr -> Latn
+    {0xA28D0000u, 46u}, // nui -> Latn
+    {0xBE8D0000u, 46u}, // nup -> Latn
+    {0xCA8D0000u, 46u}, // nus -> Latn
+    {0xD68D0000u, 46u}, // nuv -> Latn
+    {0xDE8D0000u, 46u}, // nux -> Latn
+    {0x6E760000u, 46u}, // nv -> Latn
+    {0x86CD0000u, 46u}, // nwb -> Latn
+    {0xC2ED0000u, 46u}, // nxq -> Latn
+    {0xC6ED0000u, 46u}, // nxr -> Latn
+    {0x6E790000u, 46u}, // ny -> Latn
+    {0xB30D0000u, 46u}, // nym -> Latn
+    {0xB70D0000u, 46u}, // nyn -> Latn
+    {0xA32D0000u, 46u}, // nzi -> Latn
+    {0x6F630000u, 46u}, // oc -> Latn
+    {0x88CE0000u, 46u}, // ogc -> Latn
+    {0xC54E0000u, 46u}, // okr -> Latn
+    {0xD54E0000u, 46u}, // okv -> Latn
+    {0x6F6D0000u, 46u}, // om -> Latn
+    {0x99AE0000u, 46u}, // ong -> Latn
+    {0xB5AE0000u, 46u}, // onn -> Latn
+    {0xC9AE0000u, 46u}, // ons -> Latn
+    {0xB1EE0000u, 46u}, // opm -> Latn
+    {0x6F720000u, 64u}, // or -> Orya
+    {0xBA2E0000u, 46u}, // oro -> Latn
     {0xD22E0000u,  1u}, // oru -> Arab
-    {0x6F730000u, 16u}, // os -> Cyrl
-    {0x824E0000u, 63u}, // osa -> Osge
+    {0x6F730000u, 17u}, // os -> Cyrl
+    {0x824E0000u, 65u}, // osa -> Osge
     {0x826E0000u,  1u}, // ota -> Arab
-    {0xAA6E0000u, 61u}, // otk -> Orkh
-    {0xB32E0000u, 44u}, // ozm -> Latn
-    {0x70610000u, 26u}, // pa -> Guru
+    {0xAA6E0000u, 63u}, // otk -> Orkh
+    {0xB32E0000u, 46u}, // ozm -> Latn
+    {0x70610000u, 27u}, // pa -> Guru
     {0x7061504Bu,  1u}, // pa-PK -> Arab
-    {0x980F0000u, 44u}, // pag -> Latn
-    {0xAC0F0000u, 65u}, // pal -> Phli
-    {0xB00F0000u, 44u}, // pam -> Latn
-    {0xBC0F0000u, 44u}, // pap -> Latn
-    {0xD00F0000u, 44u}, // pau -> Latn
-    {0xA02F0000u, 44u}, // pbi -> Latn
-    {0x8C4F0000u, 44u}, // pcd -> Latn
-    {0xB04F0000u, 44u}, // pcm -> Latn
-    {0x886F0000u, 44u}, // pdc -> Latn
-    {0xCC6F0000u, 44u}, // pdt -> Latn
-    {0x8C8F0000u, 44u}, // ped -> Latn
-    {0xB88F0000u, 92u}, // peo -> Xpeo
-    {0xDC8F0000u, 44u}, // pex -> Latn
-    {0xACAF0000u, 44u}, // pfl -> Latn
+    {0x980F0000u, 46u}, // pag -> Latn
+    {0xAC0F0000u, 67u}, // pal -> Phli
+    {0xB00F0000u, 46u}, // pam -> Latn
+    {0xBC0F0000u, 46u}, // pap -> Latn
+    {0xD00F0000u, 46u}, // pau -> Latn
+    {0xA02F0000u, 46u}, // pbi -> Latn
+    {0x8C4F0000u, 46u}, // pcd -> Latn
+    {0xB04F0000u, 46u}, // pcm -> Latn
+    {0x886F0000u, 46u}, // pdc -> Latn
+    {0xCC6F0000u, 46u}, // pdt -> Latn
+    {0x8C8F0000u, 46u}, // ped -> Latn
+    {0xB88F0000u, 94u}, // peo -> Xpeo
+    {0xDC8F0000u, 46u}, // pex -> Latn
+    {0xACAF0000u, 46u}, // pfl -> Latn
     {0xACEF0000u,  1u}, // phl -> Arab
-    {0xB4EF0000u, 66u}, // phn -> Phnx
-    {0xAD0F0000u, 44u}, // pil -> Latn
-    {0xBD0F0000u, 44u}, // pip -> Latn
+    {0xB4EF0000u, 68u}, // phn -> Phnx
+    {0xAD0F0000u, 46u}, // pil -> Latn
+    {0xBD0F0000u, 46u}, // pip -> Latn
     {0x814F0000u,  8u}, // pka -> Brah
-    {0xB94F0000u, 44u}, // pko -> Latn
-    {0x706C0000u, 44u}, // pl -> Latn
-    {0x816F0000u, 44u}, // pla -> Latn
-    {0xC98F0000u, 44u}, // pms -> Latn
-    {0x99AF0000u, 44u}, // png -> Latn
-    {0xB5AF0000u, 44u}, // pnn -> Latn
-    {0xCDAF0000u, 24u}, // pnt -> Grek
-    {0xB5CF0000u, 44u}, // pon -> Latn
-    {0x81EF0000u, 17u}, // ppa -> Deva
-    {0xB9EF0000u, 44u}, // ppo -> Latn
-    {0x822F0000u, 38u}, // pra -> Khar
+    {0xB94F0000u, 46u}, // pko -> Latn
+    {0x706C0000u, 46u}, // pl -> Latn
+    {0x816F0000u, 46u}, // pla -> Latn
+    {0xC98F0000u, 46u}, // pms -> Latn
+    {0x99AF0000u, 46u}, // png -> Latn
+    {0xB5AF0000u, 46u}, // pnn -> Latn
+    {0xCDAF0000u, 25u}, // pnt -> Grek
+    {0xB5CF0000u, 46u}, // pon -> Latn
+    {0x81EF0000u, 18u}, // ppa -> Deva
+    {0xB9EF0000u, 46u}, // ppo -> Latn
+    {0x822F0000u, 39u}, // pra -> Khar
     {0x8E2F0000u,  1u}, // prd -> Arab
-    {0x9A2F0000u, 44u}, // prg -> Latn
+    {0x9A2F0000u, 46u}, // prg -> Latn
     {0x70730000u,  1u}, // ps -> Arab
-    {0xCA4F0000u, 44u}, // pss -> Latn
-    {0x70740000u, 44u}, // pt -> Latn
-    {0xBE6F0000u, 44u}, // ptp -> Latn
-    {0xD28F0000u, 44u}, // puu -> Latn
-    {0x82CF0000u, 44u}, // pwa -> Latn
-    {0x71750000u, 44u}, // qu -> Latn
-    {0x8A900000u, 44u}, // quc -> Latn
-    {0x9A900000u, 44u}, // qug -> Latn
-    {0xA0110000u, 44u}, // rai -> Latn
-    {0xA4110000u, 17u}, // raj -> Deva
-    {0xB8110000u, 44u}, // rao -> Latn
-    {0x94510000u, 44u}, // rcf -> Latn
-    {0xA4910000u, 44u}, // rej -> Latn
-    {0xAC910000u, 44u}, // rel -> Latn
-    {0xC8910000u, 44u}, // res -> Latn
-    {0xB4D10000u, 44u}, // rgn -> Latn
+    {0xCA4F0000u, 46u}, // pss -> Latn
+    {0x70740000u, 46u}, // pt -> Latn
+    {0xBE6F0000u, 46u}, // ptp -> Latn
+    {0xD28F0000u, 46u}, // puu -> Latn
+    {0x82CF0000u, 46u}, // pwa -> Latn
+    {0x71750000u, 46u}, // qu -> Latn
+    {0x8A900000u, 46u}, // quc -> Latn
+    {0x9A900000u, 46u}, // qug -> Latn
+    {0xA0110000u, 46u}, // rai -> Latn
+    {0xA4110000u, 18u}, // raj -> Deva
+    {0xB8110000u, 46u}, // rao -> Latn
+    {0x94510000u, 46u}, // rcf -> Latn
+    {0xA4910000u, 46u}, // rej -> Latn
+    {0xAC910000u, 46u}, // rel -> Latn
+    {0xC8910000u, 46u}, // res -> Latn
+    {0xB4D10000u, 46u}, // rgn -> Latn
     {0x98F10000u,  1u}, // rhg -> Arab
-    {0x81110000u, 44u}, // ria -> Latn
-    {0x95110000u, 85u}, // rif -> Tfng
-    {0x95114E4Cu, 44u}, // rif-NL -> Latn
-    {0xC9310000u, 17u}, // rjs -> Deva
+    {0x81110000u, 46u}, // ria -> Latn
+    {0x95110000u, 87u}, // rif -> Tfng
+    {0x95114E4Cu, 46u}, // rif-NL -> Latn
+    {0xC9310000u, 18u}, // rjs -> Deva
     {0xCD510000u,  7u}, // rkt -> Beng
-    {0x726D0000u, 44u}, // rm -> Latn
-    {0x95910000u, 44u}, // rmf -> Latn
-    {0xB9910000u, 44u}, // rmo -> Latn
+    {0x726D0000u, 46u}, // rm -> Latn
+    {0x95910000u, 46u}, // rmf -> Latn
+    {0xB9910000u, 46u}, // rmo -> Latn
     {0xCD910000u,  1u}, // rmt -> Arab
-    {0xD1910000u, 44u}, // rmu -> Latn
-    {0x726E0000u, 44u}, // rn -> Latn
-    {0x81B10000u, 44u}, // rna -> Latn
-    {0x99B10000u, 44u}, // rng -> Latn
-    {0x726F0000u, 44u}, // ro -> Latn
-    {0x85D10000u, 44u}, // rob -> Latn
-    {0x95D10000u, 44u}, // rof -> Latn
-    {0xB9D10000u, 44u}, // roo -> Latn
-    {0xBA310000u, 44u}, // rro -> Latn
-    {0xB2710000u, 44u}, // rtm -> Latn
-    {0x72750000u, 16u}, // ru -> Cyrl
-    {0x92910000u, 16u}, // rue -> Cyrl
-    {0x9A910000u, 44u}, // rug -> Latn
-    {0x72770000u, 44u}, // rw -> Latn
-    {0xAAD10000u, 44u}, // rwk -> Latn
-    {0xBAD10000u, 44u}, // rwo -> Latn
-    {0xD3110000u, 37u}, // ryu -> Kana
-    {0x73610000u, 17u}, // sa -> Deva
-    {0x94120000u, 44u}, // saf -> Latn
-    {0x9C120000u, 16u}, // sah -> Cyrl
-    {0xC0120000u, 44u}, // saq -> Latn
-    {0xC8120000u, 44u}, // sas -> Latn
-    {0xCC120000u, 44u}, // sat -> Latn
-    {0xD4120000u, 44u}, // sav -> Latn
-    {0xE4120000u, 72u}, // saz -> Saur
-    {0x80320000u, 44u}, // sba -> Latn
-    {0x90320000u, 44u}, // sbe -> Latn
-    {0xBC320000u, 44u}, // sbp -> Latn
-    {0x73630000u, 44u}, // sc -> Latn
-    {0xA8520000u, 17u}, // sck -> Deva
+    {0xD1910000u, 46u}, // rmu -> Latn
+    {0x726E0000u, 46u}, // rn -> Latn
+    {0x81B10000u, 46u}, // rna -> Latn
+    {0x99B10000u, 46u}, // rng -> Latn
+    {0x726F0000u, 46u}, // ro -> Latn
+    {0x85D10000u, 46u}, // rob -> Latn
+    {0x95D10000u, 46u}, // rof -> Latn
+    {0xB9D10000u, 46u}, // roo -> Latn
+    {0xBA310000u, 46u}, // rro -> Latn
+    {0xB2710000u, 46u}, // rtm -> Latn
+    {0x72750000u, 17u}, // ru -> Cyrl
+    {0x92910000u, 17u}, // rue -> Cyrl
+    {0x9A910000u, 46u}, // rug -> Latn
+    {0x72770000u, 46u}, // rw -> Latn
+    {0xAAD10000u, 46u}, // rwk -> Latn
+    {0xBAD10000u, 46u}, // rwo -> Latn
+    {0xD3110000u, 38u}, // ryu -> Kana
+    {0x73610000u, 18u}, // sa -> Deva
+    {0x94120000u, 46u}, // saf -> Latn
+    {0x9C120000u, 17u}, // sah -> Cyrl
+    {0xC0120000u, 46u}, // saq -> Latn
+    {0xC8120000u, 46u}, // sas -> Latn
+    {0xCC120000u, 46u}, // sat -> Latn
+    {0xD4120000u, 46u}, // sav -> Latn
+    {0xE4120000u, 74u}, // saz -> Saur
+    {0x80320000u, 46u}, // sba -> Latn
+    {0x90320000u, 46u}, // sbe -> Latn
+    {0xBC320000u, 46u}, // sbp -> Latn
+    {0x73630000u, 46u}, // sc -> Latn
+    {0xA8520000u, 18u}, // sck -> Deva
     {0xAC520000u,  1u}, // scl -> Arab
-    {0xB4520000u, 44u}, // scn -> Latn
-    {0xB8520000u, 44u}, // sco -> Latn
-    {0xC8520000u, 44u}, // scs -> Latn
+    {0xB4520000u, 46u}, // scn -> Latn
+    {0xB8520000u, 46u}, // sco -> Latn
+    {0xC8520000u, 46u}, // scs -> Latn
     {0x73640000u,  1u}, // sd -> Arab
-    {0x88720000u, 44u}, // sdc -> Latn
+    {0x88720000u, 46u}, // sdc -> Latn
     {0x9C720000u,  1u}, // sdh -> Arab
-    {0x73650000u, 44u}, // se -> Latn
-    {0x94920000u, 44u}, // sef -> Latn
-    {0x9C920000u, 44u}, // seh -> Latn
-    {0xA0920000u, 44u}, // sei -> Latn
-    {0xC8920000u, 44u}, // ses -> Latn
-    {0x73670000u, 44u}, // sg -> Latn
-    {0x80D20000u, 60u}, // sga -> Ogam
-    {0xC8D20000u, 44u}, // sgs -> Latn
-    {0xD8D20000u, 19u}, // sgw -> Ethi
-    {0xE4D20000u, 44u}, // sgz -> Latn
-    {0x73680000u, 44u}, // sh -> Latn
-    {0xA0F20000u, 85u}, // shi -> Tfng
-    {0xA8F20000u, 44u}, // shk -> Latn
-    {0xB4F20000u, 56u}, // shn -> Mymr
+    {0x73650000u, 46u}, // se -> Latn
+    {0x94920000u, 46u}, // sef -> Latn
+    {0x9C920000u, 46u}, // seh -> Latn
+    {0xA0920000u, 46u}, // sei -> Latn
+    {0xC8920000u, 46u}, // ses -> Latn
+    {0x73670000u, 46u}, // sg -> Latn
+    {0x80D20000u, 62u}, // sga -> Ogam
+    {0xC8D20000u, 46u}, // sgs -> Latn
+    {0xD8D20000u, 20u}, // sgw -> Ethi
+    {0xE4D20000u, 46u}, // sgz -> Latn
+    {0x73680000u, 46u}, // sh -> Latn
+    {0xA0F20000u, 87u}, // shi -> Tfng
+    {0xA8F20000u, 46u}, // shk -> Latn
+    {0xB4F20000u, 58u}, // shn -> Mymr
     {0xD0F20000u,  1u}, // shu -> Arab
-    {0x73690000u, 74u}, // si -> Sinh
-    {0x8D120000u, 44u}, // sid -> Latn
-    {0x99120000u, 44u}, // sig -> Latn
-    {0xAD120000u, 44u}, // sil -> Latn
-    {0xB1120000u, 44u}, // sim -> Latn
-    {0xC5320000u, 44u}, // sjr -> Latn
-    {0x736B0000u, 44u}, // sk -> Latn
-    {0x89520000u, 44u}, // skc -> Latn
+    {0x73690000u, 76u}, // si -> Sinh
+    {0x8D120000u, 46u}, // sid -> Latn
+    {0x99120000u, 46u}, // sig -> Latn
+    {0xAD120000u, 46u}, // sil -> Latn
+    {0xB1120000u, 46u}, // sim -> Latn
+    {0xC5320000u, 46u}, // sjr -> Latn
+    {0x736B0000u, 46u}, // sk -> Latn
+    {0x89520000u, 46u}, // skc -> Latn
     {0xC5520000u,  1u}, // skr -> Arab
-    {0xC9520000u, 44u}, // sks -> Latn
-    {0x736C0000u, 44u}, // sl -> Latn
-    {0x8D720000u, 44u}, // sld -> Latn
-    {0xA1720000u, 44u}, // sli -> Latn
-    {0xAD720000u, 44u}, // sll -> Latn
-    {0xE1720000u, 44u}, // sly -> Latn
-    {0x736D0000u, 44u}, // sm -> Latn
-    {0x81920000u, 44u}, // sma -> Latn
-    {0xA5920000u, 44u}, // smj -> Latn
-    {0xB5920000u, 44u}, // smn -> Latn
-    {0xBD920000u, 70u}, // smp -> Samr
-    {0xC1920000u, 44u}, // smq -> Latn
-    {0xC9920000u, 44u}, // sms -> Latn
-    {0x736E0000u, 44u}, // sn -> Latn
-    {0x89B20000u, 44u}, // snc -> Latn
-    {0xA9B20000u, 44u}, // snk -> Latn
-    {0xBDB20000u, 44u}, // snp -> Latn
-    {0xDDB20000u, 44u}, // snx -> Latn
-    {0xE1B20000u, 44u}, // sny -> Latn
-    {0x736F0000u, 44u}, // so -> Latn
-    {0x99D20000u, 75u}, // sog -> Sogd
-    {0xA9D20000u, 44u}, // sok -> Latn
-    {0xC1D20000u, 44u}, // soq -> Latn
-    {0xD1D20000u, 87u}, // sou -> Thai
-    {0xE1D20000u, 44u}, // soy -> Latn
-    {0x8DF20000u, 44u}, // spd -> Latn
-    {0xADF20000u, 44u}, // spl -> Latn
-    {0xC9F20000u, 44u}, // sps -> Latn
-    {0x73710000u, 44u}, // sq -> Latn
-    {0x73720000u, 16u}, // sr -> Cyrl
-    {0x73724D45u, 44u}, // sr-ME -> Latn
-    {0x7372524Fu, 44u}, // sr-RO -> Latn
-    {0x73725255u, 44u}, // sr-RU -> Latn
-    {0x73725452u, 44u}, // sr-TR -> Latn
-    {0x86320000u, 76u}, // srb -> Sora
-    {0xB6320000u, 44u}, // srn -> Latn
-    {0xC6320000u, 44u}, // srr -> Latn
-    {0xDE320000u, 17u}, // srx -> Deva
-    {0x73730000u, 44u}, // ss -> Latn
-    {0x8E520000u, 44u}, // ssd -> Latn
-    {0x9A520000u, 44u}, // ssg -> Latn
-    {0xE2520000u, 44u}, // ssy -> Latn
-    {0x73740000u, 44u}, // st -> Latn
-    {0xAA720000u, 44u}, // stk -> Latn
-    {0xC2720000u, 44u}, // stq -> Latn
-    {0x73750000u, 44u}, // su -> Latn
-    {0x82920000u, 44u}, // sua -> Latn
-    {0x92920000u, 44u}, // sue -> Latn
-    {0xAA920000u, 44u}, // suk -> Latn
-    {0xC6920000u, 44u}, // sur -> Latn
-    {0xCA920000u, 44u}, // sus -> Latn
-    {0x73760000u, 44u}, // sv -> Latn
-    {0x73770000u, 44u}, // sw -> Latn
+    {0xC9520000u, 46u}, // sks -> Latn
+    {0x736C0000u, 46u}, // sl -> Latn
+    {0x8D720000u, 46u}, // sld -> Latn
+    {0xA1720000u, 46u}, // sli -> Latn
+    {0xAD720000u, 46u}, // sll -> Latn
+    {0xE1720000u, 46u}, // sly -> Latn
+    {0x736D0000u, 46u}, // sm -> Latn
+    {0x81920000u, 46u}, // sma -> Latn
+    {0xA5920000u, 46u}, // smj -> Latn
+    {0xB5920000u, 46u}, // smn -> Latn
+    {0xBD920000u, 72u}, // smp -> Samr
+    {0xC1920000u, 46u}, // smq -> Latn
+    {0xC9920000u, 46u}, // sms -> Latn
+    {0x736E0000u, 46u}, // sn -> Latn
+    {0x89B20000u, 46u}, // snc -> Latn
+    {0xA9B20000u, 46u}, // snk -> Latn
+    {0xBDB20000u, 46u}, // snp -> Latn
+    {0xDDB20000u, 46u}, // snx -> Latn
+    {0xE1B20000u, 46u}, // sny -> Latn
+    {0x736F0000u, 46u}, // so -> Latn
+    {0x99D20000u, 77u}, // sog -> Sogd
+    {0xA9D20000u, 46u}, // sok -> Latn
+    {0xC1D20000u, 46u}, // soq -> Latn
+    {0xD1D20000u, 89u}, // sou -> Thai
+    {0xE1D20000u, 46u}, // soy -> Latn
+    {0x8DF20000u, 46u}, // spd -> Latn
+    {0xADF20000u, 46u}, // spl -> Latn
+    {0xC9F20000u, 46u}, // sps -> Latn
+    {0x73710000u, 46u}, // sq -> Latn
+    {0x73720000u, 17u}, // sr -> Cyrl
+    {0x73724D45u, 46u}, // sr-ME -> Latn
+    {0x7372524Fu, 46u}, // sr-RO -> Latn
+    {0x73725255u, 46u}, // sr-RU -> Latn
+    {0x73725452u, 46u}, // sr-TR -> Latn
+    {0x86320000u, 78u}, // srb -> Sora
+    {0xB6320000u, 46u}, // srn -> Latn
+    {0xC6320000u, 46u}, // srr -> Latn
+    {0xDE320000u, 18u}, // srx -> Deva
+    {0x73730000u, 46u}, // ss -> Latn
+    {0x8E520000u, 46u}, // ssd -> Latn
+    {0x9A520000u, 46u}, // ssg -> Latn
+    {0xE2520000u, 46u}, // ssy -> Latn
+    {0x73740000u, 46u}, // st -> Latn
+    {0xAA720000u, 46u}, // stk -> Latn
+    {0xC2720000u, 46u}, // stq -> Latn
+    {0x73750000u, 46u}, // su -> Latn
+    {0x82920000u, 46u}, // sua -> Latn
+    {0x92920000u, 46u}, // sue -> Latn
+    {0xAA920000u, 46u}, // suk -> Latn
+    {0xC6920000u, 46u}, // sur -> Latn
+    {0xCA920000u, 46u}, // sus -> Latn
+    {0x73760000u, 46u}, // sv -> Latn
+    {0x73770000u, 46u}, // sw -> Latn
     {0x86D20000u,  1u}, // swb -> Arab
-    {0x8AD20000u, 44u}, // swc -> Latn
-    {0x9AD20000u, 44u}, // swg -> Latn
-    {0xBED20000u, 44u}, // swp -> Latn
-    {0xD6D20000u, 17u}, // swv -> Deva
-    {0xB6F20000u, 44u}, // sxn -> Latn
-    {0xDAF20000u, 44u}, // sxw -> Latn
+    {0x8AD20000u, 46u}, // swc -> Latn
+    {0x9AD20000u, 46u}, // swg -> Latn
+    {0xBED20000u, 46u}, // swp -> Latn
+    {0xD6D20000u, 18u}, // swv -> Deva
+    {0xB6F20000u, 46u}, // sxn -> Latn
+    {0xDAF20000u, 46u}, // sxw -> Latn
     {0xAF120000u,  7u}, // syl -> Beng
-    {0xC7120000u, 78u}, // syr -> Syrc
-    {0xAF320000u, 44u}, // szl -> Latn
-    {0x74610000u, 81u}, // ta -> Taml
-    {0xA4130000u, 17u}, // taj -> Deva
-    {0xAC130000u, 44u}, // tal -> Latn
-    {0xB4130000u, 44u}, // tan -> Latn
-    {0xC0130000u, 44u}, // taq -> Latn
-    {0x88330000u, 44u}, // tbc -> Latn
-    {0x8C330000u, 44u}, // tbd -> Latn
-    {0x94330000u, 44u}, // tbf -> Latn
-    {0x98330000u, 44u}, // tbg -> Latn
-    {0xB8330000u, 44u}, // tbo -> Latn
-    {0xD8330000u, 44u}, // tbw -> Latn
-    {0xE4330000u, 44u}, // tbz -> Latn
-    {0xA0530000u, 44u}, // tci -> Latn
-    {0xE0530000u, 40u}, // tcy -> Knda
-    {0x8C730000u, 79u}, // tdd -> Tale
-    {0x98730000u, 17u}, // tdg -> Deva
-    {0x9C730000u, 17u}, // tdh -> Deva
-    {0xD0730000u, 44u}, // tdu -> Latn
-    {0x74650000u, 84u}, // te -> Telu
-    {0x8C930000u, 44u}, // ted -> Latn
-    {0xB0930000u, 44u}, // tem -> Latn
-    {0xB8930000u, 44u}, // teo -> Latn
-    {0xCC930000u, 44u}, // tet -> Latn
-    {0xA0B30000u, 44u}, // tfi -> Latn
-    {0x74670000u, 16u}, // tg -> Cyrl
+    {0xC7120000u, 80u}, // syr -> Syrc
+    {0xAF320000u, 46u}, // szl -> Latn
+    {0x74610000u, 83u}, // ta -> Taml
+    {0xA4130000u, 18u}, // taj -> Deva
+    {0xAC130000u, 46u}, // tal -> Latn
+    {0xB4130000u, 46u}, // tan -> Latn
+    {0xC0130000u, 46u}, // taq -> Latn
+    {0x88330000u, 46u}, // tbc -> Latn
+    {0x8C330000u, 46u}, // tbd -> Latn
+    {0x94330000u, 46u}, // tbf -> Latn
+    {0x98330000u, 46u}, // tbg -> Latn
+    {0xB8330000u, 46u}, // tbo -> Latn
+    {0xD8330000u, 46u}, // tbw -> Latn
+    {0xE4330000u, 46u}, // tbz -> Latn
+    {0xA0530000u, 46u}, // tci -> Latn
+    {0xE0530000u, 42u}, // tcy -> Knda
+    {0x8C730000u, 81u}, // tdd -> Tale
+    {0x98730000u, 18u}, // tdg -> Deva
+    {0x9C730000u, 18u}, // tdh -> Deva
+    {0xD0730000u, 46u}, // tdu -> Latn
+    {0x74650000u, 86u}, // te -> Telu
+    {0x8C930000u, 46u}, // ted -> Latn
+    {0xB0930000u, 46u}, // tem -> Latn
+    {0xB8930000u, 46u}, // teo -> Latn
+    {0xCC930000u, 46u}, // tet -> Latn
+    {0xA0B30000u, 46u}, // tfi -> Latn
+    {0x74670000u, 17u}, // tg -> Cyrl
     {0x7467504Bu,  1u}, // tg-PK -> Arab
-    {0x88D30000u, 44u}, // tgc -> Latn
-    {0xB8D30000u, 44u}, // tgo -> Latn
-    {0xD0D30000u, 44u}, // tgu -> Latn
-    {0x74680000u, 87u}, // th -> Thai
-    {0xACF30000u, 17u}, // thl -> Deva
-    {0xC0F30000u, 17u}, // thq -> Deva
-    {0xC4F30000u, 17u}, // thr -> Deva
-    {0x74690000u, 19u}, // ti -> Ethi
-    {0x95130000u, 44u}, // tif -> Latn
-    {0x99130000u, 19u}, // tig -> Ethi
-    {0xA9130000u, 44u}, // tik -> Latn
-    {0xB1130000u, 44u}, // tim -> Latn
-    {0xB9130000u, 44u}, // tio -> Latn
-    {0xD5130000u, 44u}, // tiv -> Latn
-    {0x746B0000u, 44u}, // tk -> Latn
-    {0xAD530000u, 44u}, // tkl -> Latn
-    {0xC5530000u, 44u}, // tkr -> Latn
-    {0xCD530000u, 17u}, // tkt -> Deva
-    {0x746C0000u, 44u}, // tl -> Latn
-    {0x95730000u, 44u}, // tlf -> Latn
-    {0xDD730000u, 44u}, // tlx -> Latn
-    {0xE1730000u, 44u}, // tly -> Latn
-    {0x9D930000u, 44u}, // tmh -> Latn
-    {0xE1930000u, 44u}, // tmy -> Latn
-    {0x746E0000u, 44u}, // tn -> Latn
-    {0x9DB30000u, 44u}, // tnh -> Latn
-    {0x746F0000u, 44u}, // to -> Latn
-    {0x95D30000u, 44u}, // tof -> Latn
-    {0x99D30000u, 44u}, // tog -> Latn
-    {0xC1D30000u, 44u}, // toq -> Latn
-    {0xA1F30000u, 44u}, // tpi -> Latn
-    {0xB1F30000u, 44u}, // tpm -> Latn
-    {0xE5F30000u, 44u}, // tpz -> Latn
-    {0xBA130000u, 44u}, // tqo -> Latn
-    {0x74720000u, 44u}, // tr -> Latn
-    {0xD2330000u, 44u}, // tru -> Latn
-    {0xD6330000u, 44u}, // trv -> Latn
+    {0x88D30000u, 46u}, // tgc -> Latn
+    {0xB8D30000u, 46u}, // tgo -> Latn
+    {0xD0D30000u, 46u}, // tgu -> Latn
+    {0x74680000u, 89u}, // th -> Thai
+    {0xACF30000u, 18u}, // thl -> Deva
+    {0xC0F30000u, 18u}, // thq -> Deva
+    {0xC4F30000u, 18u}, // thr -> Deva
+    {0x74690000u, 20u}, // ti -> Ethi
+    {0x95130000u, 46u}, // tif -> Latn
+    {0x99130000u, 20u}, // tig -> Ethi
+    {0xA9130000u, 46u}, // tik -> Latn
+    {0xB1130000u, 46u}, // tim -> Latn
+    {0xB9130000u, 46u}, // tio -> Latn
+    {0xD5130000u, 46u}, // tiv -> Latn
+    {0x746B0000u, 46u}, // tk -> Latn
+    {0xAD530000u, 46u}, // tkl -> Latn
+    {0xC5530000u, 46u}, // tkr -> Latn
+    {0xCD530000u, 18u}, // tkt -> Deva
+    {0x746C0000u, 46u}, // tl -> Latn
+    {0x95730000u, 46u}, // tlf -> Latn
+    {0xDD730000u, 46u}, // tlx -> Latn
+    {0xE1730000u, 46u}, // tly -> Latn
+    {0x9D930000u, 46u}, // tmh -> Latn
+    {0xE1930000u, 46u}, // tmy -> Latn
+    {0x746E0000u, 46u}, // tn -> Latn
+    {0x9DB30000u, 46u}, // tnh -> Latn
+    {0x746F0000u, 46u}, // to -> Latn
+    {0x95D30000u, 46u}, // tof -> Latn
+    {0x99D30000u, 46u}, // tog -> Latn
+    {0xC1D30000u, 46u}, // toq -> Latn
+    {0xA1F30000u, 46u}, // tpi -> Latn
+    {0xB1F30000u, 46u}, // tpm -> Latn
+    {0xE5F30000u, 46u}, // tpz -> Latn
+    {0xBA130000u, 46u}, // tqo -> Latn
+    {0x74720000u, 46u}, // tr -> Latn
+    {0xD2330000u, 46u}, // tru -> Latn
+    {0xD6330000u, 46u}, // trv -> Latn
     {0xDA330000u,  1u}, // trw -> Arab
-    {0x74730000u, 44u}, // ts -> Latn
-    {0x8E530000u, 24u}, // tsd -> Grek
-    {0x96530000u, 17u}, // tsf -> Deva
-    {0x9A530000u, 44u}, // tsg -> Latn
-    {0xA6530000u, 88u}, // tsj -> Tibt
-    {0xDA530000u, 44u}, // tsw -> Latn
-    {0x74740000u, 16u}, // tt -> Cyrl
-    {0x8E730000u, 44u}, // ttd -> Latn
-    {0x92730000u, 44u}, // tte -> Latn
-    {0xA6730000u, 44u}, // ttj -> Latn
-    {0xC6730000u, 44u}, // ttr -> Latn
-    {0xCA730000u, 87u}, // tts -> Thai
-    {0xCE730000u, 44u}, // ttt -> Latn
-    {0x9E930000u, 44u}, // tuh -> Latn
-    {0xAE930000u, 44u}, // tul -> Latn
-    {0xB2930000u, 44u}, // tum -> Latn
-    {0xC2930000u, 44u}, // tuq -> Latn
-    {0x8EB30000u, 44u}, // tvd -> Latn
-    {0xAEB30000u, 44u}, // tvl -> Latn
-    {0xD2B30000u, 44u}, // tvu -> Latn
-    {0x9ED30000u, 44u}, // twh -> Latn
-    {0xC2D30000u, 44u}, // twq -> Latn
-    {0x9AF30000u, 82u}, // txg -> Tang
-    {0x74790000u, 44u}, // ty -> Latn
-    {0x83130000u, 44u}, // tya -> Latn
-    {0xD7130000u, 16u}, // tyv -> Cyrl
-    {0xB3330000u, 44u}, // tzm -> Latn
-    {0xD0340000u, 44u}, // ubu -> Latn
-    {0xB0740000u, 16u}, // udm -> Cyrl
+    {0x74730000u, 46u}, // ts -> Latn
+    {0x8E530000u, 25u}, // tsd -> Grek
+    {0x96530000u, 18u}, // tsf -> Deva
+    {0x9A530000u, 46u}, // tsg -> Latn
+    {0xA6530000u, 90u}, // tsj -> Tibt
+    {0xDA530000u, 46u}, // tsw -> Latn
+    {0x74740000u, 17u}, // tt -> Cyrl
+    {0x8E730000u, 46u}, // ttd -> Latn
+    {0x92730000u, 46u}, // tte -> Latn
+    {0xA6730000u, 46u}, // ttj -> Latn
+    {0xC6730000u, 46u}, // ttr -> Latn
+    {0xCA730000u, 89u}, // tts -> Thai
+    {0xCE730000u, 46u}, // ttt -> Latn
+    {0x9E930000u, 46u}, // tuh -> Latn
+    {0xAE930000u, 46u}, // tul -> Latn
+    {0xB2930000u, 46u}, // tum -> Latn
+    {0xC2930000u, 46u}, // tuq -> Latn
+    {0x8EB30000u, 46u}, // tvd -> Latn
+    {0xAEB30000u, 46u}, // tvl -> Latn
+    {0xD2B30000u, 46u}, // tvu -> Latn
+    {0x9ED30000u, 46u}, // twh -> Latn
+    {0xC2D30000u, 46u}, // twq -> Latn
+    {0x9AF30000u, 84u}, // txg -> Tang
+    {0x74790000u, 46u}, // ty -> Latn
+    {0x83130000u, 46u}, // tya -> Latn
+    {0xD7130000u, 17u}, // tyv -> Cyrl
+    {0xB3330000u, 46u}, // tzm -> Latn
+    {0xD0340000u, 46u}, // ubu -> Latn
+    {0xB0740000u, 17u}, // udm -> Cyrl
     {0x75670000u,  1u}, // ug -> Arab
-    {0x75674B5Au, 16u}, // ug-KZ -> Cyrl
-    {0x75674D4Eu, 16u}, // ug-MN -> Cyrl
-    {0x80D40000u, 89u}, // uga -> Ugar
-    {0x756B0000u, 16u}, // uk -> Cyrl
-    {0xA1740000u, 44u}, // uli -> Latn
-    {0x85940000u, 44u}, // umb -> Latn
+    {0x75674B5Au, 17u}, // ug-KZ -> Cyrl
+    {0x75674D4Eu, 17u}, // ug-MN -> Cyrl
+    {0x80D40000u, 91u}, // uga -> Ugar
+    {0x756B0000u, 17u}, // uk -> Cyrl
+    {0xA1740000u, 46u}, // uli -> Latn
+    {0x85940000u, 46u}, // umb -> Latn
     {0xC5B40000u,  7u}, // unr -> Beng
-    {0xC5B44E50u, 17u}, // unr-NP -> Deva
+    {0xC5B44E50u, 18u}, // unr-NP -> Deva
     {0xDDB40000u,  7u}, // unx -> Beng
-    {0xA9D40000u, 44u}, // uok -> Latn
+    {0xA9D40000u, 46u}, // uok -> Latn
     {0x75720000u,  1u}, // ur -> Arab
-    {0xA2340000u, 44u}, // uri -> Latn
-    {0xCE340000u, 44u}, // urt -> Latn
-    {0xDA340000u, 44u}, // urw -> Latn
-    {0x82540000u, 44u}, // usa -> Latn
-    {0xC6740000u, 44u}, // utr -> Latn
-    {0x9EB40000u, 44u}, // uvh -> Latn
-    {0xAEB40000u, 44u}, // uvl -> Latn
-    {0x757A0000u, 44u}, // uz -> Latn
+    {0xA2340000u, 46u}, // uri -> Latn
+    {0xCE340000u, 46u}, // urt -> Latn
+    {0xDA340000u, 46u}, // urw -> Latn
+    {0x82540000u, 46u}, // usa -> Latn
+    {0xC6740000u, 46u}, // utr -> Latn
+    {0x9EB40000u, 46u}, // uvh -> Latn
+    {0xAEB40000u, 46u}, // uvl -> Latn
+    {0x757A0000u, 46u}, // uz -> Latn
     {0x757A4146u,  1u}, // uz-AF -> Arab
-    {0x757A434Eu, 16u}, // uz-CN -> Cyrl
-    {0x98150000u, 44u}, // vag -> Latn
-    {0xA0150000u, 90u}, // vai -> Vaii
-    {0xB4150000u, 44u}, // van -> Latn
-    {0x76650000u, 44u}, // ve -> Latn
-    {0x88950000u, 44u}, // vec -> Latn
-    {0xBC950000u, 44u}, // vep -> Latn
-    {0x76690000u, 44u}, // vi -> Latn
-    {0x89150000u, 44u}, // vic -> Latn
-    {0xD5150000u, 44u}, // viv -> Latn
-    {0xC9750000u, 44u}, // vls -> Latn
-    {0x95950000u, 44u}, // vmf -> Latn
-    {0xD9950000u, 44u}, // vmw -> Latn
-    {0x766F0000u, 44u}, // vo -> Latn
-    {0xCDD50000u, 44u}, // vot -> Latn
-    {0xBA350000u, 44u}, // vro -> Latn
-    {0xB6950000u, 44u}, // vun -> Latn
-    {0xCE950000u, 44u}, // vut -> Latn
-    {0x77610000u, 44u}, // wa -> Latn
-    {0x90160000u, 44u}, // wae -> Latn
-    {0xA4160000u, 44u}, // waj -> Latn
-    {0xAC160000u, 19u}, // wal -> Ethi
-    {0xB4160000u, 44u}, // wan -> Latn
-    {0xC4160000u, 44u}, // war -> Latn
-    {0xBC360000u, 44u}, // wbp -> Latn
-    {0xC0360000u, 84u}, // wbq -> Telu
-    {0xC4360000u, 17u}, // wbr -> Deva
-    {0xA0560000u, 44u}, // wci -> Latn
-    {0xC4960000u, 44u}, // wer -> Latn
-    {0xA0D60000u, 44u}, // wgi -> Latn
-    {0x98F60000u, 44u}, // whg -> Latn
-    {0x85160000u, 44u}, // wib -> Latn
-    {0xD1160000u, 44u}, // wiu -> Latn
-    {0xD5160000u, 44u}, // wiv -> Latn
-    {0x81360000u, 44u}, // wja -> Latn
-    {0xA1360000u, 44u}, // wji -> Latn
-    {0xC9760000u, 44u}, // wls -> Latn
-    {0xB9960000u, 44u}, // wmo -> Latn
-    {0x89B60000u, 44u}, // wnc -> Latn
+    {0x757A434Eu, 17u}, // uz-CN -> Cyrl
+    {0x98150000u, 46u}, // vag -> Latn
+    {0xA0150000u, 92u}, // vai -> Vaii
+    {0xB4150000u, 46u}, // van -> Latn
+    {0x76650000u, 46u}, // ve -> Latn
+    {0x88950000u, 46u}, // vec -> Latn
+    {0xBC950000u, 46u}, // vep -> Latn
+    {0x76690000u, 46u}, // vi -> Latn
+    {0x89150000u, 46u}, // vic -> Latn
+    {0xD5150000u, 46u}, // viv -> Latn
+    {0xC9750000u, 46u}, // vls -> Latn
+    {0x95950000u, 46u}, // vmf -> Latn
+    {0xD9950000u, 46u}, // vmw -> Latn
+    {0x766F0000u, 46u}, // vo -> Latn
+    {0xCDD50000u, 46u}, // vot -> Latn
+    {0xBA350000u, 46u}, // vro -> Latn
+    {0xB6950000u, 46u}, // vun -> Latn
+    {0xCE950000u, 46u}, // vut -> Latn
+    {0x77610000u, 46u}, // wa -> Latn
+    {0x90160000u, 46u}, // wae -> Latn
+    {0xA4160000u, 46u}, // waj -> Latn
+    {0xAC160000u, 20u}, // wal -> Ethi
+    {0xB4160000u, 46u}, // wan -> Latn
+    {0xC4160000u, 46u}, // war -> Latn
+    {0xBC360000u, 46u}, // wbp -> Latn
+    {0xC0360000u, 86u}, // wbq -> Telu
+    {0xC4360000u, 18u}, // wbr -> Deva
+    {0xA0560000u, 46u}, // wci -> Latn
+    {0xC4960000u, 46u}, // wer -> Latn
+    {0xA0D60000u, 46u}, // wgi -> Latn
+    {0x98F60000u, 46u}, // whg -> Latn
+    {0x85160000u, 46u}, // wib -> Latn
+    {0xD1160000u, 46u}, // wiu -> Latn
+    {0xD5160000u, 46u}, // wiv -> Latn
+    {0x81360000u, 46u}, // wja -> Latn
+    {0xA1360000u, 46u}, // wji -> Latn
+    {0xC9760000u, 46u}, // wls -> Latn
+    {0xB9960000u, 46u}, // wmo -> Latn
+    {0x89B60000u, 46u}, // wnc -> Latn
     {0xA1B60000u,  1u}, // wni -> Arab
-    {0xD1B60000u, 44u}, // wnu -> Latn
-    {0x776F0000u, 44u}, // wo -> Latn
-    {0x85D60000u, 44u}, // wob -> Latn
-    {0xC9D60000u, 44u}, // wos -> Latn
-    {0xCA360000u, 44u}, // wrs -> Latn
-    {0x9A560000u, 21u}, // wsg -> Gong
-    {0xAA560000u, 44u}, // wsk -> Latn
-    {0xB2760000u, 17u}, // wtm -> Deva
-    {0xD2960000u, 27u}, // wuu -> Hans
-    {0xD6960000u, 44u}, // wuv -> Latn
-    {0x82D60000u, 44u}, // wwa -> Latn
-    {0xD4170000u, 44u}, // xav -> Latn
-    {0xA0370000u, 44u}, // xbi -> Latn
+    {0xD1B60000u, 46u}, // wnu -> Latn
+    {0x776F0000u, 46u}, // wo -> Latn
+    {0x85D60000u, 46u}, // wob -> Latn
+    {0xC9D60000u, 46u}, // wos -> Latn
+    {0xCA360000u, 46u}, // wrs -> Latn
+    {0x9A560000u, 22u}, // wsg -> Gong
+    {0xAA560000u, 46u}, // wsk -> Latn
+    {0xB2760000u, 18u}, // wtm -> Deva
+    {0xD2960000u, 28u}, // wuu -> Hans
+    {0xD6960000u, 46u}, // wuv -> Latn
+    {0x82D60000u, 46u}, // wwa -> Latn
+    {0xD4170000u, 46u}, // xav -> Latn
+    {0xA0370000u, 46u}, // xbi -> Latn
+    {0xB8570000u, 14u}, // xco -> Chrs
     {0xC4570000u, 11u}, // xcr -> Cari
-    {0xC8970000u, 44u}, // xes -> Latn
-    {0x78680000u, 44u}, // xh -> Latn
-    {0x81770000u, 44u}, // xla -> Latn
-    {0x89770000u, 48u}, // xlc -> Lyci
-    {0x8D770000u, 49u}, // xld -> Lydi
-    {0x95970000u, 20u}, // xmf -> Geor
-    {0xB5970000u, 51u}, // xmn -> Mani
-    {0xC5970000u, 52u}, // xmr -> Merc
-    {0x81B70000u, 57u}, // xna -> Narb
-    {0xC5B70000u, 17u}, // xnr -> Deva
-    {0x99D70000u, 44u}, // xog -> Latn
-    {0xB5D70000u, 44u}, // xon -> Latn
-    {0xC5F70000u, 68u}, // xpr -> Prti
-    {0x86370000u, 44u}, // xrb -> Latn
-    {0x82570000u, 71u}, // xsa -> Sarb
-    {0xA2570000u, 44u}, // xsi -> Latn
-    {0xB2570000u, 44u}, // xsm -> Latn
-    {0xC6570000u, 17u}, // xsr -> Deva
-    {0x92D70000u, 44u}, // xwe -> Latn
-    {0xB0180000u, 44u}, // yam -> Latn
-    {0xB8180000u, 44u}, // yao -> Latn
-    {0xBC180000u, 44u}, // yap -> Latn
-    {0xC8180000u, 44u}, // yas -> Latn
-    {0xCC180000u, 44u}, // yat -> Latn
-    {0xD4180000u, 44u}, // yav -> Latn
-    {0xE0180000u, 44u}, // yay -> Latn
-    {0xE4180000u, 44u}, // yaz -> Latn
-    {0x80380000u, 44u}, // yba -> Latn
-    {0x84380000u, 44u}, // ybb -> Latn
-    {0xE0380000u, 44u}, // yby -> Latn
-    {0xC4980000u, 44u}, // yer -> Latn
-    {0xC4D80000u, 44u}, // ygr -> Latn
-    {0xD8D80000u, 44u}, // ygw -> Latn
-    {0x79690000u, 30u}, // yi -> Hebr
-    {0xB9580000u, 44u}, // yko -> Latn
-    {0x91780000u, 44u}, // yle -> Latn
-    {0x99780000u, 44u}, // ylg -> Latn
-    {0xAD780000u, 44u}, // yll -> Latn
-    {0xAD980000u, 44u}, // yml -> Latn
-    {0x796F0000u, 44u}, // yo -> Latn
-    {0xB5D80000u, 44u}, // yon -> Latn
-    {0x86380000u, 44u}, // yrb -> Latn
-    {0x92380000u, 44u}, // yre -> Latn
-    {0xAE380000u, 44u}, // yrl -> Latn
-    {0xCA580000u, 44u}, // yss -> Latn
-    {0x82980000u, 44u}, // yua -> Latn
-    {0x92980000u, 28u}, // yue -> Hant
-    {0x9298434Eu, 27u}, // yue-CN -> Hans
-    {0xA6980000u, 44u}, // yuj -> Latn
-    {0xCE980000u, 44u}, // yut -> Latn
-    {0xDA980000u, 44u}, // yuw -> Latn
-    {0x7A610000u, 44u}, // za -> Latn
-    {0x98190000u, 44u}, // zag -> Latn
+    {0xC8970000u, 46u}, // xes -> Latn
+    {0x78680000u, 46u}, // xh -> Latn
+    {0x81770000u, 46u}, // xla -> Latn
+    {0x89770000u, 50u}, // xlc -> Lyci
+    {0x8D770000u, 51u}, // xld -> Lydi
+    {0x95970000u, 21u}, // xmf -> Geor
+    {0xB5970000u, 53u}, // xmn -> Mani
+    {0xC5970000u, 54u}, // xmr -> Merc
+    {0x81B70000u, 59u}, // xna -> Narb
+    {0xC5B70000u, 18u}, // xnr -> Deva
+    {0x99D70000u, 46u}, // xog -> Latn
+    {0xB5D70000u, 46u}, // xon -> Latn
+    {0xC5F70000u, 70u}, // xpr -> Prti
+    {0x86370000u, 46u}, // xrb -> Latn
+    {0x82570000u, 73u}, // xsa -> Sarb
+    {0xA2570000u, 46u}, // xsi -> Latn
+    {0xB2570000u, 46u}, // xsm -> Latn
+    {0xC6570000u, 18u}, // xsr -> Deva
+    {0x92D70000u, 46u}, // xwe -> Latn
+    {0xB0180000u, 46u}, // yam -> Latn
+    {0xB8180000u, 46u}, // yao -> Latn
+    {0xBC180000u, 46u}, // yap -> Latn
+    {0xC8180000u, 46u}, // yas -> Latn
+    {0xCC180000u, 46u}, // yat -> Latn
+    {0xD4180000u, 46u}, // yav -> Latn
+    {0xE0180000u, 46u}, // yay -> Latn
+    {0xE4180000u, 46u}, // yaz -> Latn
+    {0x80380000u, 46u}, // yba -> Latn
+    {0x84380000u, 46u}, // ybb -> Latn
+    {0xE0380000u, 46u}, // yby -> Latn
+    {0xC4980000u, 46u}, // yer -> Latn
+    {0xC4D80000u, 46u}, // ygr -> Latn
+    {0xD8D80000u, 46u}, // ygw -> Latn
+    {0x79690000u, 31u}, // yi -> Hebr
+    {0xB9580000u, 46u}, // yko -> Latn
+    {0x91780000u, 46u}, // yle -> Latn
+    {0x99780000u, 46u}, // ylg -> Latn
+    {0xAD780000u, 46u}, // yll -> Latn
+    {0xAD980000u, 46u}, // yml -> Latn
+    {0x796F0000u, 46u}, // yo -> Latn
+    {0xB5D80000u, 46u}, // yon -> Latn
+    {0x86380000u, 46u}, // yrb -> Latn
+    {0x92380000u, 46u}, // yre -> Latn
+    {0xAE380000u, 46u}, // yrl -> Latn
+    {0xCA580000u, 46u}, // yss -> Latn
+    {0x82980000u, 46u}, // yua -> Latn
+    {0x92980000u, 29u}, // yue -> Hant
+    {0x9298434Eu, 28u}, // yue-CN -> Hans
+    {0xA6980000u, 46u}, // yuj -> Latn
+    {0xCE980000u, 46u}, // yut -> Latn
+    {0xDA980000u, 46u}, // yuw -> Latn
+    {0x7A610000u, 46u}, // za -> Latn
+    {0x98190000u, 46u}, // zag -> Latn
     {0xA4790000u,  1u}, // zdj -> Arab
-    {0x80990000u, 44u}, // zea -> Latn
-    {0x9CD90000u, 85u}, // zgh -> Tfng
-    {0x7A680000u, 27u}, // zh -> Hans
-    {0x7A684155u, 28u}, // zh-AU -> Hant
-    {0x7A68424Eu, 28u}, // zh-BN -> Hant
-    {0x7A684742u, 28u}, // zh-GB -> Hant
-    {0x7A684746u, 28u}, // zh-GF -> Hant
-    {0x7A68484Bu, 28u}, // zh-HK -> Hant
-    {0x7A684944u, 28u}, // zh-ID -> Hant
-    {0x7A684D4Fu, 28u}, // zh-MO -> Hant
-    {0x7A684D59u, 28u}, // zh-MY -> Hant
-    {0x7A685041u, 28u}, // zh-PA -> Hant
-    {0x7A685046u, 28u}, // zh-PF -> Hant
-    {0x7A685048u, 28u}, // zh-PH -> Hant
-    {0x7A685352u, 28u}, // zh-SR -> Hant
-    {0x7A685448u, 28u}, // zh-TH -> Hant
-    {0x7A685457u, 28u}, // zh-TW -> Hant
-    {0x7A685553u, 28u}, // zh-US -> Hant
-    {0x7A68564Eu, 28u}, // zh-VN -> Hant
-    {0xDCF90000u, 59u}, // zhx -> Nshu
-    {0x81190000u, 44u}, // zia -> Latn
-    {0xB1790000u, 44u}, // zlm -> Latn
-    {0xA1990000u, 44u}, // zmi -> Latn
-    {0x91B90000u, 44u}, // zne -> Latn
-    {0x7A750000u, 44u}, // zu -> Latn
-    {0x83390000u, 44u}, // zza -> Latn
+    {0x80990000u, 46u}, // zea -> Latn
+    {0x9CD90000u, 87u}, // zgh -> Tfng
+    {0x7A680000u, 28u}, // zh -> Hans
+    {0x7A684155u, 29u}, // zh-AU -> Hant
+    {0x7A68424Eu, 29u}, // zh-BN -> Hant
+    {0x7A684742u, 29u}, // zh-GB -> Hant
+    {0x7A684746u, 29u}, // zh-GF -> Hant
+    {0x7A68484Bu, 29u}, // zh-HK -> Hant
+    {0x7A684944u, 29u}, // zh-ID -> Hant
+    {0x7A684D4Fu, 29u}, // zh-MO -> Hant
+    {0x7A684D59u, 29u}, // zh-MY -> Hant
+    {0x7A685041u, 29u}, // zh-PA -> Hant
+    {0x7A685046u, 29u}, // zh-PF -> Hant
+    {0x7A685048u, 29u}, // zh-PH -> Hant
+    {0x7A685352u, 29u}, // zh-SR -> Hant
+    {0x7A685448u, 29u}, // zh-TH -> Hant
+    {0x7A685457u, 29u}, // zh-TW -> Hant
+    {0x7A685553u, 29u}, // zh-US -> Hant
+    {0x7A68564Eu, 29u}, // zh-VN -> Hant
+    {0xDCF90000u, 61u}, // zhx -> Nshu
+    {0x81190000u, 46u}, // zia -> Latn
+    {0xCD590000u, 41u}, // zkt -> Kits
+    {0xB1790000u, 46u}, // zlm -> Latn
+    {0xA1990000u, 46u}, // zmi -> Latn
+    {0x91B90000u, 46u}, // zne -> Latn
+    {0x7A750000u, 46u}, // zu -> Latn
+    {0x83390000u, 46u}, // zza -> Latn
 });
 
 std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
@@ -1829,6 +1833,7 @@
     0xC66A4D594C61746ELLU, // ktr_Latn_MY
     0x6B75495141726162LLU, // ku_Arab_IQ
     0x6B7554524C61746ELLU, // ku_Latn_TR
+    0x6B75474559657A69LLU, // ku_Yezi_GE
     0xB28A52554379726CLLU, // kum_Cyrl_RU
     0x6B7652554379726CLLU, // kv_Cyrl_RU
     0xC6AA49444C61746ELLU, // kvr_Latn_ID
@@ -2199,6 +2204,7 @@
     0xB276494E44657661LLU, // wtm_Deva_IN
     0xD296434E48616E73LLU, // wuu_Hans_CN
     0xD41742524C61746ELLU, // xav_Latn_BR
+    0xB857555A43687273LLU, // xco_Chrs_UZ
     0xC457545243617269LLU, // xcr_Cari_TR
     0x78685A414C61746ELLU, // xh_Latn_ZA
     0x897754524C796369LLU, // xlc_Lyci_TR
@@ -2231,6 +2237,7 @@
     0x7A68434E48616E73LLU, // zh_Hans_CN
     0x7A68545748616E74LLU, // zh_Hant_TW
     0xDCF9434E4E736875LLU, // zhx_Nshu_CN
+    0xCD59434E4B697473LLU, // zkt_Kits_CN
     0xB17954474C61746ELLU, // zlm_Latn_TG
     0xA1994D594C61746ELLU, // zmi_Latn_MY
     0x7A755A414C61746ELLU, // zu_Latn_ZA
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 3a771bb..ad9486c 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -870,16 +870,17 @@
     private int getSessionResourceId() throws MediaCasException {
         validateInternalStates();
 
-        int[] sessionResourceId = new int[1];
-        sessionResourceId[0] = -1;
+        int[] sessionResourceHandle = new int[1];
+        sessionResourceHandle[0] = -1;
         if (mTunerResourceManager != null) {
             CasSessionRequest casSessionRequest = new CasSessionRequest(mClientId, mCasSystemId);
-            if (!mTunerResourceManager.requestCasSession(casSessionRequest, sessionResourceId)) {
-                throw new MediaCasException.ResourceBusyException(
+            if (!mTunerResourceManager
+                    .requestCasSession(casSessionRequest, sessionResourceHandle)) {
+                throw new MediaCasException.InsufficientResourceException(
                     "insufficient resource to Open Session");
             }
         }
-        return  sessionResourceId[0];
+        return  sessionResourceHandle[0];
     }
 
     private void addSessionToResourceMap(Session session, int sessionResourceId) {
@@ -905,6 +906,10 @@
      * Open a session to descramble one or more streams scrambled by the
      * conditional access system.
      *
+     * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+     * to get cas session resource if cas session resources is limited. If the client can't get the
+     * resource, this call returns {@link MediaCasException.InsufficientResourceException }.
+     *
      * @return session the newly opened session.
      *
      * @throws IllegalStateException if the MediaCas instance is not valid.
@@ -930,6 +935,10 @@
      * Open a session with usage and scrambling information, so that descrambler can be configured
      * to descramble one or more streams scrambled by the conditional access system.
      *
+     * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+     * to get cas session resource if cas session resources is limited. If the client can't get the
+     * resource, this call returns {@link MediaCasException.InsufficientResourceException}.
+     *
      * @param sessionUsage used for the created session.
      * @param scramblingMode used for the created session.
      *
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 8e39dfa..fb95e98 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -466,10 +466,6 @@
             return;
         }
 
-        List<RoutingSessionInfo> sessionInfos;
-        synchronized (mSessionLock) {
-            sessionInfos = new ArrayList<>(mSessionInfo.values());
-        }
         try {
             mRemoteCallback.updateState(mProviderInfo);
         } catch (RemoteException ex) {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 37e2ab5..bde45d7 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -58,7 +58,7 @@
 // TODO: Add method names at the beginning of log messages. (e.g. updateControllerOnHandler)
 //       Not only MediaRouter2, but also to service / manager / provider.
 // TODO: ensure thread-safe and document it
-public class MediaRouter2 {
+public final class MediaRouter2 {
     private static final String TAG = "MR2";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final Object sRouterLock = new Object();
@@ -93,9 +93,9 @@
     MediaRouter2Stub mStub;
 
     @GuardedBy("sRouterLock")
-    private Map<String, RoutingController> mRoutingControllers = new ArrayMap<>();
+    private final Map<String, RoutingController> mRoutingControllers = new ArrayMap<>();
 
-    private AtomicInteger mControllerCreationRequestCnt = new AtomicInteger(1);
+    private final AtomicInteger mControllerCreationRequestCnt = new AtomicInteger(1);
 
     final Handler mHandler;
     @GuardedBy("sRouterLock")
@@ -419,8 +419,7 @@
 
         controller.release();
 
-        final int requestId;
-        requestId = mControllerCreationRequestCnt.getAndIncrement();
+        final int requestId = mControllerCreationRequestCnt.getAndIncrement();
 
         ControllerCreationRequest request =
                 new ControllerCreationRequest(requestId, controller, route);
@@ -610,10 +609,16 @@
         }
 
         if (sessionInfo != null) {
-            RoutingController newController = new RoutingController(sessionInfo);
-            synchronized (sRouterLock) {
-                mRoutingControllers.put(newController.getId(), newController);
+            RoutingController newController;
+            if (sessionInfo.isSystemSession()) {
+                newController = getSystemController();
+            } else {
+                newController = new RoutingController(sessionInfo);
+                synchronized (sRouterLock) {
+                    mRoutingControllers.put(newController.getId(), newController);
+                }
             }
+            //TODO: Determine oldController properly when transfer is launched by Output Switcher.
             notifyTransferred(matchingRequest != null ? matchingRequest.mController :
                     getSystemController(), newController);
         }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 11c9fe1..88bcd6a 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -50,7 +50,7 @@
  * A class that monitors and controls media routing of other apps.
  * @hide
  */
-public class MediaRouter2Manager {
+public final class MediaRouter2Manager {
     private static final String TAG = "MR2Manager";
     private static final Object sLock = new Object();
 
@@ -61,7 +61,7 @@
 
     final String mPackageName;
 
-    private Context mContext;
+    private final Context mContext;
     @GuardedBy("sLock")
     private Client mClient;
     private final IMediaRouterService mMediaRouterService;
@@ -74,7 +74,7 @@
     @NonNull
     final ConcurrentMap<String, List<String>> mPreferredFeaturesMap = new ConcurrentHashMap<>();
 
-    private AtomicInteger mNextRequestId = new AtomicInteger(1);
+    private final AtomicInteger mNextRequestId = new AtomicInteger(1);
 
     /**
      * Gets an instance of media router manager that controls media route of other applications.
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
index 40add56..975604c 100644
--- a/media/java/android/media/tv/tuner/Descrambler.java
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
 import android.media.tv.tuner.filter.Filter;
 
 import java.lang.annotation.Retention;
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index ea06632..525ee4d 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -23,7 +23,7 @@
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 08a33f1..d24d752 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -26,7 +26,6 @@
 import android.content.Context;
 import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.TvInputService;
-import android.media.tv.tuner.TunerConstants.Result;
 import android.media.tv.tuner.dvr.DvrPlayback;
 import android.media.tv.tuner.dvr.DvrRecorder;
 import android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener;
@@ -44,12 +43,14 @@
 import android.media.tv.tuner.frontend.OnTuneEventListener;
 import android.media.tv.tuner.frontend.ScanCallback;
 import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
 import android.media.tv.tunerresourcemanager.TunerLnbRequest;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Message;
+import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -68,6 +69,96 @@
  */
 @SystemApi
 public class Tuner implements AutoCloseable  {
+    /**
+     * Invalid TS packet ID.
+     */
+    public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
+    /**
+     * Invalid stream ID.
+     */
+    public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
+    /**
+     * Invalid filter ID.
+     */
+    public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
+    /**
+     * Invalid AV Sync ID.
+     */
+    public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
+    /**
+     * Invalid timestamp.
+     *
+     * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
+     * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, or
+     * {@link Tuner#getAvSyncTime(int)} when the requested timestamp is not available.
+     *
+     * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
+     * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
+     * @see Tuner#getAvSyncTime(int)
+     */
+    public static final long INVALID_TIMESTAMP = -1L;
+
+
+    /** @hide */
+    @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ScanType {}
+    /**
+     * Scan type undefined.
+     */
+    public static final int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
+    /**
+     * Scan type auto.
+     *
+     * <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked}
+     */
+    public static final int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO;
+    /**
+     * Blind scan.
+     *
+     * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
+     * implementation specific range.
+     */
+    public static final int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND;
+
+
+    /** @hide */
+    @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
+            RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Result {}
+
+    /**
+     * Operation succeeded.
+     */
+    public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
+    /**
+     * Operation failed because the corresponding resources are not available.
+     */
+    public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
+    /**
+     * Operation failed because the corresponding resources are not initialized.
+     */
+    public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
+    /**
+     * Operation failed because it's not in a valid state.
+     */
+    public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
+    /**
+     * Operation failed because there are invalid arguments.
+     */
+    public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
+    /**
+     * Memory allocation failed.
+     */
+    public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
+    /**
+     * Operation failed due to unknown errors.
+     */
+    public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
+
+
+
     private static final String TAG = "MediaTvTuner";
     private static final boolean DEBUG = false;
 
@@ -93,23 +184,27 @@
     public static final int DVR_TYPE_PLAYBACK = Constants.DvrType.PLAYBACK;
 
     static {
-        System.loadLibrary("media_tv_tuner");
-        nativeInit();
+        try {
+            System.loadLibrary("media_tv_tuner");
+            nativeInit();
+        } catch (UnsatisfiedLinkError e) {
+            Log.d(TAG, "tuner JNI library not found!");
+        }
     }
 
     private final Context mContext;
     private final TunerResourceManager mTunerResourceManager;
     private final int mClientId;
 
-    private List<Integer> mFrontendIds;
     private Frontend mFrontend;
     private EventHandler mHandler;
     @Nullable
     private FrontendInfo mFrontendInfo;
+    private Integer mFrontendHandle;
+    private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
 
-    private List<Integer> mLnbIds;
     private Lnb mLnb;
-    private Integer mLnbId;
+    private Integer mLnbHandle;
     @Nullable
     private OnTuneEventListener mOnTuneEventListener;
     @Nullable
@@ -233,7 +328,7 @@
     /**
      * Native method to open frontend of the given ID.
      */
-    private native Frontend nativeOpenFrontendById(int id);
+    private native Frontend nativeOpenFrontendByHandle(int handle);
     private native int nativeTune(int type, FrontendSettings settings);
     private native int nativeStopTune();
     private native int nativeScan(int settingsType, FrontendSettings settings, int scanType);
@@ -250,7 +345,7 @@
     private native TimeFilter nativeOpenTimeFilter();
 
     private native List<Integer> nativeGetLnbIds();
-    private native Lnb nativeOpenLnbById(int id);
+    private native Lnb nativeOpenLnbByHandle(int handle);
     private native Lnb nativeOpenLnbByName(String name);
 
     private native Descrambler nativeOpenDescrambler();
@@ -359,6 +454,10 @@
     /**
      * Tunes the frontend to using the settings given.
      *
+     * <p>Tuner resource manager (TRM) uses the client priority value to decide whether it is able
+     * to get frontend resource. If the client can't get the resource, this call returns {@link
+     * Result#RESULT_UNAVAILABLE}.
+     *
      * <p>
      * This locks the frontend to a frequency by providing signal
      * delivery information. If previous tuning isn't completed, this stop the previous tuning, and
@@ -378,7 +477,8 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
     public int tune(@NonNull FrontendSettings settings) {
-        TunerUtils.checkTunerPermission(mContext);
+        mFrontendType = settings.getType();
+        checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
         mFrontendInfo = null;
         return nativeTune(settings.getType(), settings);
     }
@@ -411,14 +511,15 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
-    public int scan(@NonNull FrontendSettings settings, @TunerConstants.ScanType int scanType,
+    public int scan(@NonNull FrontendSettings settings, @ScanType int scanType,
             @NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
-        TunerUtils.checkTunerPermission(mContext);
         if (mScanCallback != null || mScanCallbackExecutor != null) {
             throw new IllegalStateException(
                     "Scan already in progress.  stopScan must be called before a new scan can be "
                             + "started.");
         }
+        mFrontendType = settings.getType();
+        checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
         mScanCallback = scanCallback;
         mScanCallbackExecutor = executor;
         mFrontendInfo = null;
@@ -446,6 +547,16 @@
         return retVal;
     }
 
+    private boolean requestFrontend() {
+        int[] feId = new int[1];
+        TunerFrontendRequest request = new TunerFrontendRequest(mClientId, mFrontendType);
+        boolean granted = mTunerResourceManager.requestFrontend(request, feId);
+        if (granted) {
+            mFrontendHandle = feId[0];
+        }
+        return granted;
+    }
+
     /**
      * Sets Low-Noise Block downconverter (LNB) for satellite frontend.
      *
@@ -498,7 +609,7 @@
     public int getAvSyncHwId(@NonNull Filter filter) {
         TunerUtils.checkTunerPermission(mContext);
         Integer id = nativeGetAvSyncHwId(filter);
-        return id == null ? TunerConstants.INVALID_AV_SYNC_ID : id;
+        return id == null ? INVALID_AV_SYNC_ID : id;
     }
 
     /**
@@ -514,7 +625,7 @@
     public long getAvSyncTime(int avSyncHwId) {
         TunerUtils.checkTunerPermission(mContext);
         Long time = nativeGetAvSyncTime(avSyncHwId);
-        return time == null ? TunerConstants.TIMESTAMP_UNAVAILABLE : time;
+        return time == null ? INVALID_TIMESTAMP : time;
     }
 
     /**
@@ -579,22 +690,6 @@
         return nativeGetDemuxCapabilities();
     }
 
-    private List<Integer> getFrontendIds() {
-        mFrontendIds = nativeGetFrontendIds();
-        return mFrontendIds;
-    }
-
-    private Frontend openFrontendById(int id) {
-        if (mFrontendIds == null) {
-            mFrontendIds = getFrontendIds();
-        }
-        if (!mFrontendIds.contains(id)) {
-            return null;
-        }
-        mFrontend = nativeOpenFrontendById(id);
-        return mFrontend;
-    }
-
     private void onFrontendEvent(int eventType) {
         if (mOnTunerEventExecutor != null && mOnTuneEventListener != null) {
             mOnTunerEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType));
@@ -731,11 +826,9 @@
     public Lnb openLnb(@CallbackExecutor @NonNull Executor executor, @NonNull LnbCallback cb) {
         Objects.requireNonNull(executor, "executor must not be null");
         Objects.requireNonNull(cb, "LnbCallback must not be null");
-        TunerUtils.checkTunerPermission(mContext);
-        if (mLnbId == null && !requestLnb()) {
-            return null;
-        }
-        return nativeOpenLnbById(mLnbId);
+        checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB);
+        // TODO: update JNI code for LNB handle,
+        return nativeOpenLnbByHandle(mLnbHandle);
     }
 
     /**
@@ -763,7 +856,7 @@
         TunerLnbRequest request = new TunerLnbRequest(mClientId);
         boolean granted = mTunerResourceManager.requestLnb(request, lnbId);
         if (granted) {
-            mLnbId = lnbId[0];
+            mLnbHandle = lnbId[0];
         }
         return granted;
     }
@@ -778,22 +871,6 @@
         return nativeOpenTimeFilter();
     }
 
-    private List<Integer> getLnbIds() {
-        mLnbIds = nativeGetLnbIds();
-        return mLnbIds;
-    }
-
-    private Lnb openLnbById(int id) {
-        if (mLnbIds == null) {
-            mLnbIds = getLnbIds();
-        }
-        if (!mLnbIds.contains(id)) {
-            return null;
-        }
-        mLnb = nativeOpenLnbById(id);
-        return mLnb;
-    }
-
     private void onLnbEvent(int eventType) {
         if (mHandler != null) {
             mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_LNB_EVENT, eventType, 0));
@@ -857,4 +934,23 @@
         DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize);
         return dvr;
     }
+
+    private boolean checkResource(int resourceType)  {
+        // TODO: demux and descrambler
+        switch (resourceType) {
+            case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
+                if (mFrontendHandle == null && !requestFrontend()) {
+                    return false;
+                }
+                break;
+            }
+            case TunerResourceManager.TUNER_RESOURCE_TYPE_LNB: {
+                if (mLnbHandle == null && !requestLnb()) {
+                    return false;
+                }
+                break;
+            }
+        }
+        return true;
+    }
 }
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
deleted file mode 100644
index 6d89962..0000000
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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 android.media.tv.tuner;
-
-import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.hardware.tv.tuner.V1_0.Constants;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Constants for tuner framework.
- *
- * @hide
- */
-@SystemApi
-public final class TunerConstants {
-    /**
-     * Invalid TS packet ID.
-     */
-    public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
-    /**
-     * Invalid stream ID.
-     */
-    public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
-    /**
-     * Invalid filter ID.
-     */
-    public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
-    /**
-     * Invalid AV Sync ID.
-     */
-    public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
-    /**
-     * Timestamp is unavailable.
-     *
-     * <p>Returned by {@link android.media.tv.tuner.filter.TimeFilter#getSourceTime()},
-     * {@link android.media.tv.tuner.filter.TimeFilter#getTimeStamp()}, or
-     * {@link Tuner#getAvSyncTime(int)} when the requested timestamp is not available.
-     *
-     * @see android.media.tv.tuner.filter.TimeFilter#getSourceTime()
-     * @see android.media.tv.tuner.filter.TimeFilter#getTimeStamp()
-     * @see Tuner#getAvSyncTime(int)
-     * @hide
-     */
-    public static final long TIMESTAMP_UNAVAILABLE = -1L;
-
-    /** @hide */
-    @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ScanType {}
-    /**
-     * Scan type undefined.
-     */
-    public static final int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
-    /**
-     * Scan type auto.
-     *
-     * <p> Tuner will send {@link android.media.tv.tuner.frontend.ScanCallback#onLocked}
-     */
-    public static final int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO;
-    /**
-     * Blind scan.
-     *
-     * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
-     * implementation specific range.
-     */
-    public static final int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND;
-
-    /** @hide */
-    @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
-            RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Result {}
-
-    /**
-     * Operation succeeded.
-     */
-    public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
-    /**
-     * Operation failed because the corresponding resources are not available.
-     */
-    public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
-    /**
-     * Operation failed because the corresponding resources are not initialized.
-     */
-    public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
-    /**
-     * Operation failed because it's not in a valid state.
-     */
-    public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
-    /**
-     * Operation failed because there are invalid arguments.
-     */
-    public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
-    /**
-     * Memory allocation failed.
-     */
-    public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
-    /**
-     * Operation failed due to unknown errors.
-     */
-    public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
-
-    private TunerConstants() {
-    }
-}
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 2258ee5..c3be12a 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -171,22 +171,22 @@
      */
     @Nullable
     public static void throwExceptionForResult(
-            @TunerConstants.Result int r, @Nullable String msg) {
+            @Tuner.Result int r, @Nullable String msg) {
         if (msg == null) {
             msg = "";
         }
         switch (r) {
-            case TunerConstants.RESULT_INVALID_ARGUMENT:
+            case Tuner.RESULT_INVALID_ARGUMENT:
                 throw new IllegalArgumentException(msg);
-            case TunerConstants.RESULT_INVALID_STATE:
+            case Tuner.RESULT_INVALID_STATE:
                 throw new IllegalStateException(msg);
-            case TunerConstants.RESULT_NOT_INITIALIZED:
+            case Tuner.RESULT_NOT_INITIALIZED:
                 throw new IllegalStateException("Invalid state: not initialized. " + msg);
-            case TunerConstants.RESULT_OUT_OF_MEMORY:
+            case Tuner.RESULT_OUT_OF_MEMORY:
                 throw new OutOfMemoryError(msg);
-            case TunerConstants.RESULT_UNAVAILABLE:
+            case Tuner.RESULT_UNAVAILABLE:
                 throw new IllegalStateException("Invalid state: resource unavailable. " + msg);
-            case TunerConstants.RESULT_UNKNOWN_ERROR:
+            case Tuner.RESULT_UNKNOWN_ERROR:
                 throw new RuntimeException("Unknown error" + msg);
             default:
                 break;
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index 37a016e..0d10d94 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -21,7 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
 import android.media.tv.tuner.filter.Filter;
 import android.os.ParcelFileDescriptor;
 
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index d06356c..dbda7bb 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -19,7 +19,7 @@
 import android.annotation.BytesLong;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
 import android.media.tv.tuner.filter.Filter;
 import android.os.ParcelFileDescriptor;
 
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 4777fe8..8dc0622 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -22,7 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner.Result;
 import android.media.tv.tuner.TunerUtils;
 
 import java.lang.annotation.Retention;
diff --git a/media/java/android/media/tv/tuner/filter/TimeFilter.java b/media/java/android/media/tv/tuner/filter/TimeFilter.java
index 371ccc4..be0a055 100644
--- a/media/java/android/media/tv/tuner/filter/TimeFilter.java
+++ b/media/java/android/media/tv/tuner/filter/TimeFilter.java
@@ -17,8 +17,8 @@
 package android.media.tv.tuner.filter;
 
 import android.annotation.SystemApi;
-import android.media.tv.tuner.TunerConstants;
-import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.Tuner;
+import android.media.tv.tuner.Tuner.Result;
 import android.media.tv.tuner.TunerUtils;
 
 /**
@@ -35,17 +35,6 @@
 @SystemApi
 public class TimeFilter implements AutoCloseable {
 
-    /**
-     * Timestamp is unavailable.
-     *
-     * <p>Returned by {@link #getSourceTime()} or {@link #getTimeStamp()} when the requested
-     * timestamp is not available.
-     *
-     * @see #getSourceTime()
-     * @see #getTimeStamp()
-     */
-    public static final long TIMESTAMP_UNAVAILABLE = -1L;
-
 
     private native int nativeSetTimestamp(long timestamp);
     private native int nativeClearTimestamp();
@@ -108,12 +97,12 @@
      *
      * @return current timestamp in the time filter. It's based on the 90KHz counter, and it's
      * the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019. The
-     * timestamps may or may not be related to PTS or DTS. Returns {@link #TIMESTAMP_UNAVAILABLE}
-     * if the timestamp is never set.
+     * timestamps may or may not be related to PTS or DTS. Returns
+     * {@link Tuner#INVALID_TIMESTAMP} if the timestamp is never set.
      */
     public long getTimeStamp() {
         if (!mEnable) {
-            return TIMESTAMP_UNAVAILABLE;
+            return Tuner.INVALID_TIMESTAMP;
         }
         return nativeGetTimestamp();
     }
@@ -126,11 +115,11 @@
      * @return first timestamp of incoming data stream. It's based on the 90KHz counter, and
      * it's the same format as PTS (Presentation Time Stamp) defined in ISO/IEC 13818-1:2019.
      * The timestamps may or may not be related to PTS or DTS. Returns
-     * {@link #TIMESTAMP_UNAVAILABLE} if the timestamp is not available.
+     * {@link Tuner#INVALID_TIMESTAMP} if the timestamp is not available.
      */
     public long getSourceTime() {
         if (!mEnable) {
-            return TIMESTAMP_UNAVAILABLE;
+            return Tuner.INVALID_TIMESTAMP;
         }
         return nativeGetSourceTime();
     }
@@ -144,7 +133,7 @@
     @Override
     public void close() {
         int res = nativeClose();
-        if (res != TunerConstants.RESULT_SUCCESS) {
+        if (res != Tuner.RESULT_SUCCESS) {
             TunerUtils.throwExceptionForResult(res, "Failed to close time filter.");
         }
     }
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index 7b85fa8..382cc85 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -213,13 +214,24 @@
     /**
      * Builder for {@link AnalogFrontendSettings}.
      */
-    public static class Builder extends FrontendSettings.Builder<Builder> {
+    public static class Builder {
+        private int mFrequency;
         private int mSignalType;
         private int mSifStandard;
 
         private Builder() {}
 
         /**
+         * Sets frequency in Hz.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
          * Sets analog signal type.
          */
         @NonNull
@@ -244,10 +256,5 @@
         public AnalogFrontendSettings build() {
             return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 }
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index b40ab00..1394716 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -325,7 +326,8 @@
     /**
      * Builder for {@link Atsc3FrontendSettings}.
      */
-    public static class Builder extends FrontendSettings.Builder<Builder> {
+    public static class Builder {
+        private int mFrequency;
         private int mBandwidth;
         private int mDemodOutputFormat;
         private Atsc3PlpSettings[] mPlpSettings;
@@ -334,6 +336,16 @@
         }
 
         /**
+         * Sets frequency in Hz.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
          * Sets bandwidth.
          */
         @NonNull
@@ -366,11 +378,6 @@
             return new Atsc3FrontendSettings(
                 mFrequency, mBandwidth, mDemodOutputFormat, mPlpSettings);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 
     @Override
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
index fc82a1c..53352f0 100644
--- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -91,13 +92,24 @@
     /**
      * Builder for {@link AtscFrontendSettings}.
      */
-    public static class Builder extends FrontendSettings.Builder<Builder> {
+    public static class Builder {
+        private int mFrequency;
         private int mModulation;
 
         private Builder() {
         }
 
         /**
+         * Sets frequency in Hz.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
          * Sets Modulation.
          */
         @NonNull
@@ -113,11 +125,6 @@
         public AtscFrontendSettings build() {
             return new AtscFrontendSettings(mFrequency, mModulation);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 
     @Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index 197c1c5..6d58570 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -218,7 +219,8 @@
     /**
      * Builder for {@link DvbcFrontendSettings}.
      */
-    public static class Builder extends FrontendSettings.Builder<Builder> {
+    public static class Builder {
+        private int mFrequency;
         private int mModulation;
         private long mInnerFec;
         private int mSymbolRate;
@@ -230,6 +232,16 @@
         }
 
         /**
+         * Sets frequency in Hz.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
          * Sets Modulation.
          */
         @NonNull
@@ -286,11 +298,6 @@
             return new DvbcFrontendSettings(mFrequency, mModulation, mInnerFec, mSymbolRate,
                 mOuterFec, mAnnex, mSpectralInversion);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 
     @Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 4a4fed5..9c45dd1 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -303,7 +304,8 @@
     /**
      * Builder for {@link DvbsFrontendSettings}.
      */
-    public static class Builder extends FrontendSettings.Builder<Builder> {
+    public static class Builder {
+        private int mFrequency;
         private int mModulation;
         private DvbsCodeRate mCodeRate;
         private int mSymbolRate;
@@ -317,6 +319,16 @@
         }
 
         /**
+         * Sets frequency in Hz.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
          * Sets Modulation.
          */
         @NonNull
@@ -389,11 +401,6 @@
             return new DvbsFrontendSettings(mFrequency, mModulation, mCodeRate, mSymbolRate,
                     mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 
     @Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index 1510b2d..4accabb 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -506,7 +507,8 @@
     /**
      * Builder for {@link DvbtFrontendSettings}.
      */
-    public static class Builder extends FrontendSettings.Builder<Builder> {
+    public static class Builder {
+        private int mFrequency;
         private int mTransmissionMode;
         private int mBandwidth;
         private int mConstellation;
@@ -525,6 +527,16 @@
         }
 
         /**
+         * Sets frequency in Hz.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
          * Sets Transmission Mode.
          */
         @NonNull
@@ -638,11 +650,6 @@
                     mConstellation, mHierarchy, mHpCodeRate, mLpCodeRate, mGuardInterval,
                     mIsHighPriority, mStandard, mIsMiso, mPlpMode, mPlpId, mPlpGroupId);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 
     @Override
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 9071526..2f2fa97 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -17,9 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
-import android.annotation.IntRange;
 import android.annotation.LongDef;
-import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
 
@@ -265,27 +263,4 @@
     public int getFrequency() {
         return mFrequency;
     }
-
-    /**
-     * Builder for {@link FrontendSettings}.
-     *
-     * @param <T> The subclass to be built.
-     */
-    public abstract static class Builder<T extends Builder<T>> {
-        /* package */ int mFrequency;
-
-        /* package */ Builder() {}
-
-        /**
-         * Sets frequency in Hz.
-         */
-        @NonNull
-        @IntRange(from = 1)
-        public T setFrequency(int frequency) {
-            mFrequency = frequency;
-            return self();
-        }
-
-        /* package */ abstract T self();
-    }
 }
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 9b0e533..f385379 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -224,7 +225,8 @@
     /**
      * Builder for {@link Isdbs3FrontendSettings}.
      */
-    public static class Builder extends FrontendSettings.Builder<Builder> {
+    public static class Builder {
+        private int mFrequency;
         private int mStreamId;
         private int mStreamIdType;
         private int mModulation;
@@ -236,6 +238,16 @@
         }
 
         /**
+         * Sets frequency in Hz.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
          * Sets Stream ID.
          */
         @NonNull
@@ -292,11 +304,6 @@
             return new Isdbs3FrontendSettings(mFrequency, mStreamId, mStreamIdType, mModulation,
                     mCodeRate, mSymbolRate, mRolloff);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 
     @Override
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index 14c08b1..4667b9b 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -208,7 +209,8 @@
     /**
      * Builder for {@link IsdbsFrontendSettings}.
      */
-    public static class Builder extends FrontendSettings.Builder<Builder> {
+    public static class Builder {
+        private int mFrequency;
         private int mStreamId;
         private int mStreamIdType;
         private int mModulation;
@@ -220,6 +222,16 @@
         }
 
         /**
+         * Sets frequency in Hz.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
          * Sets Stream ID.
          */
         @NonNull
@@ -276,11 +288,6 @@
             return new IsdbsFrontendSettings(mFrequency, mStreamId, mStreamIdType, mModulation,
                     mCodeRate, mSymbolRate, mRolloff);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 
     @Override
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index de3c80d..b607623 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
@@ -202,7 +203,8 @@
     /**
      * Builder for {@link IsdbtFrontendSettings}.
      */
-    public static class Builder extends FrontendSettings.Builder<Builder> {
+    public static class Builder {
+        private int mFrequency;
         private int mModulation;
         private int mBandwidth;
         private int mMode;
@@ -214,6 +216,16 @@
         }
 
         /**
+         * Sets frequency in Hz.
+         */
+        @NonNull
+        @IntRange(from = 1)
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
          * Sets Modulation.
          */
         @NonNull
@@ -270,11 +282,6 @@
             return new IsdbtFrontendSettings(mFrequency, mModulation, mBandwidth, mMode, mCodeRate,
                     mGuardInterval, mServiceAreaId);
         }
-
-        @Override
-        Builder self() {
-            return this;
-        }
     }
 
     @Override
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index 524b6c2..63a71e2 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -17,6 +17,7 @@
 package android.media.tv.tunerresourcemanager;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
@@ -27,6 +28,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.Executor;
 
 /**
@@ -60,12 +63,25 @@
     private static final String TAG = "TunerResourceManager";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    public static final int INVALID_FRONTEND_ID = -1;
-    public static final int INVALID_CAS_SESSION_RESOURCE_ID = -1;
-    public static final int INVALID_LNB_ID = -1;
-    public static final int INVALID_TV_INPUT_DEVICE_ID = -1;
-    public static final int INVALID_TV_INPUT_PORT_ID = -1;
     public static final int INVALID_RESOURCE_HANDLE = -1;
+    /**
+     * Tuner resource type to help generate resource handle
+     */
+    @IntDef({
+        TUNER_RESOURCE_TYPE_FRONTEND,
+        TUNER_RESOURCE_TYPE_DEMUX,
+        TUNER_RESOURCE_TYPE_DESCRAMBLER,
+        TUNER_RESOURCE_TYPE_LNB,
+        TUNER_RESOURCE_TYPE_CAS_SESSION,
+     })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TunerResourceType {}
+
+    public static final int TUNER_RESOURCE_TYPE_FRONTEND = 0;
+    public static final int TUNER_RESOURCE_TYPE_DEMUX = 1;
+    public static final int TUNER_RESOURCE_TYPE_DESCRAMBLER = 2;
+    public static final int TUNER_RESOURCE_TYPE_LNB = 3;
+    public static final int TUNER_RESOURCE_TYPE_CAS_SESSION = 4;
 
     private final ITunerResourceManager mService;
     private final int mUserId;
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index a37c9e5..bbeb451 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1410,6 +1410,10 @@
     return (IDescrambler *)env->GetLongField(descrambler, gFields.descramblerContext);
 }
 
+static uint32_t getResourceIdFromHandle(jint handle) {
+    return (handle & 0x00ff0000) >> 16;
+}
+
 static DemuxPid getDemuxPid(int pidType, int pid) {
     DemuxPid demuxPid;
     if ((int)pidType == 1) {
@@ -1968,8 +1972,10 @@
     return tuner->getFrontendIds();
 }
 
-static jobject android_media_tv_Tuner_open_frontend_by_id(JNIEnv *env, jobject thiz, jint id) {
+static jobject android_media_tv_Tuner_open_frontend_by_handle(
+        JNIEnv *env, jobject thiz, jint handle) {
     sp<JTuner> tuner = getTuner(env, thiz);
+    uint32_t id = getResourceIdFromHandle(handle);
     return tuner->openFrontendById(id);
 }
 
@@ -2045,8 +2051,9 @@
     return tuner->getLnbIds();
 }
 
-static jobject android_media_tv_Tuner_open_lnb_by_id(JNIEnv *env, jobject thiz, jint id) {
+static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) {
     sp<JTuner> tuner = getTuner(env, thiz);
+    uint32_t id = getResourceIdFromHandle(handle);
     return tuner->openLnbById(id);
 }
 
@@ -2924,8 +2931,8 @@
     { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
     { "nativeGetFrontendIds", "()Ljava/util/List;",
             (void *)android_media_tv_Tuner_get_frontend_ids },
-    { "nativeOpenFrontendById", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
-            (void *)android_media_tv_Tuner_open_frontend_by_id },
+    { "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;",
+            (void *)android_media_tv_Tuner_open_frontend_by_handle },
     { "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
             (void *)android_media_tv_Tuner_tune },
     { "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
@@ -2950,8 +2957,8 @@
             (void *)android_media_tv_Tuner_open_time_filter },
     { "nativeGetLnbIds", "()Ljava/util/List;",
             (void *)android_media_tv_Tuner_get_lnb_ids },
-    { "nativeOpenLnbById", "(I)Landroid/media/tv/tuner/Lnb;",
-            (void *)android_media_tv_Tuner_open_lnb_by_id },
+    { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;",
+            (void *)android_media_tv_Tuner_open_lnb_by_handle },
     { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;",
             (void *)android_media_tv_Tuner_open_lnb_by_name },
     { "nativeOpenDescrambler", "()Landroid/media/tv/tuner/Descrambler;",
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index 83f3b6e..efeb335 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -120,8 +120,9 @@
     }
 
     if (mCaptureThread != 0) {
+        sp<CaptureThread> t = mCaptureThread;
         mCaptureLock.unlock();
-        mCaptureThread->requestExitAndWait();
+        t->requestExitAndWait();
         mCaptureLock.lock();
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 8292d30..14d5bd5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -27,9 +27,11 @@
 import com.android.systemui.dagger.SystemUIRootComponent;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
+import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
 import com.android.systemui.stackdivider.DividerModule;
@@ -107,6 +109,10 @@
             BatteryControllerImpl controllerImpl);
 
     @Binds
+    @Singleton
+    public abstract QSFactory provideQSFactory(QSFactoryImpl qsFactoryImpl);
+
+    @Binds
     abstract DockManager bindDockManager(DockManagerImpl dockManager);
 
     @Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index b63162b..3ee92bd7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -238,10 +238,12 @@
         }
 
         buildNavBarContent();
-        // If the UI was rebuilt (day/night change) while the keyguard was up we need to
-        // correctly respect that state.
+        // If the UI was rebuilt (day/night change or user change) while the keyguard was up we need
+        // to correctly respect that state.
         if (mKeyguardStateControllerLazy.get().isShowing()) {
             mCarNavigationBarController.showAllKeyguardButtons(isDeviceSetupForUser());
+        } else {
+            mCarNavigationBarController.hideAllKeyguardButtons(isDeviceSetupForUser());
         }
 
         // Upon restarting the Navigation Bar, CarFacetButtonController should immediately apply the
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
index c04e47f..f2748b8 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
@@ -46,8 +46,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import dagger.Lazy;
-
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
@@ -68,12 +66,8 @@
     @Mock
     private ButtonSelectionStateListener mButtonSelectionStateListener;
     @Mock
-    private Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
-    @Mock
     private KeyguardStateController mKeyguardStateController;
     @Mock
-    private Lazy<NavigationBarController> mNavigationBarControllerLazy;
-    @Mock
     private NavigationBarController mNavigationBarController;
     @Mock
     private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@@ -89,13 +83,11 @@
         mCarNavigationBar = new CarNavigationBar(mContext, mCarNavigationBarController,
                 mWindowManager, mDeviceProvisionedController, new CommandQueue(mContext),
                 mAutoHideController, mButtonSelectionStateListener, mHandler,
-                mKeyguardStateControllerLazy, mNavigationBarControllerLazy,
+                () -> mKeyguardStateController, () -> mNavigationBarController,
                 mSuperStatusBarViewFactory, mButtonSelectionStateController);
         StatusBarWindowView statusBarWindowView = (StatusBarWindowView) LayoutInflater.from(
                 mContext).inflate(R.layout.super_status_bar, /* root= */ null);
         when(mSuperStatusBarViewFactory.getStatusBarWindowView()).thenReturn(statusBarWindowView);
-        when(mNavigationBarControllerLazy.get()).thenReturn(mNavigationBarController);
-        when(mKeyguardStateControllerLazy.get()).thenReturn(mKeyguardStateController);
         when(mKeyguardStateController.isShowing()).thenReturn(false);
         mDependency.injectMockDependency(WindowManager.class);
         // Needed to inflate top navigation bar.
@@ -119,4 +111,44 @@
 
         verify(mButtonSelectionStateListener).onTaskStackChanged();
     }
+
+    @Test
+    public void restartNavBars_newUserNotSetupWithKeyguardShowing_showsKeyguardButtons() {
+        ArgumentCaptor<CarDeviceProvisionedController.DeviceProvisionedListener>
+                deviceProvisionedCallbackCaptor = ArgumentCaptor.forClass(
+                CarDeviceProvisionedController.DeviceProvisionedListener.class);
+        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+        mCarNavigationBar.start();
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        // switching the currentUserSetup value to force restart the navbars.
+        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
+        verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture());
+
+        deviceProvisionedCallbackCaptor.getValue().onUserSwitched();
+        waitForIdleSync(mHandler);
+
+        verify(mCarNavigationBarController).showAllKeyguardButtons(false);
+    }
+
+    @Test
+    public void restartNavbars_newUserIsSetupWithKeyguardHidden_hidesKeyguardButtons() {
+        ArgumentCaptor<CarDeviceProvisionedController.DeviceProvisionedListener>
+                deviceProvisionedCallbackCaptor = ArgumentCaptor.forClass(
+                CarDeviceProvisionedController.DeviceProvisionedListener.class);
+        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+        mCarNavigationBar.start();
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        // switching the currentUserSetup value to force restart the navbars.
+        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
+        verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture());
+        deviceProvisionedCallbackCaptor.getValue().onUserSwitched();
+        waitForIdleSync(mHandler);
+        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+        deviceProvisionedCallbackCaptor.getValue().onUserSetupChanged();
+        waitForIdleSync(mHandler);
+
+        verify(mCarNavigationBarController).hideAllKeyguardButtons(true);
+    }
 }
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 00f6bcb..a797066 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1039,7 +1039,7 @@
     <!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
     <string name="accessibility_display_daltonizer_preference_title">Color correction</string>
     <!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps the device display more accurate colors. Color correction may be helpful for people with colorblindness.</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle"><![CDATA[Color correction allows you to adjust how colors are displayed on your device]]></string>
     <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
     <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 18eb187..3c78560 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -218,7 +218,7 @@
     }
 
     public boolean supportsHighQualityAudio(BluetoothDevice device) {
-        BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+        BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
         if (bluetoothDevice == null) {
             return false;
         }
@@ -227,7 +227,7 @@
     }
 
     public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
-        BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+        BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
         if (bluetoothDevice == null) {
             return false;
         }
@@ -253,7 +253,7 @@
     }
 
     public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) {
-        BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+        BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
         if (bluetoothDevice == null) {
             return;
         }
@@ -272,7 +272,7 @@
     }
 
     public String getHighQualityAudioOptionLabel(BluetoothDevice device) {
-        BluetoothDevice bluetoothDevice = (device == null) ? device : mService.getActiveDevice();
+        BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
         int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec;
         if (bluetoothDevice == null || !supportsHighQualityAudio(device)
                 || getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 4a0d16c..9ae9b4a4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -49,11 +49,13 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({MediaDeviceState.STATE_CONNECTED,
             MediaDeviceState.STATE_CONNECTING,
-            MediaDeviceState.STATE_DISCONNECTED})
+            MediaDeviceState.STATE_DISCONNECTED,
+            MediaDeviceState.STATE_CONNECTING_FAILED})
     public @interface MediaDeviceState {
-        int STATE_CONNECTED = 1;
-        int STATE_CONNECTING = 2;
-        int STATE_DISCONNECTED = 3;
+        int STATE_CONNECTED = 0;
+        int STATE_CONNECTING = 1;
+        int STATE_DISCONNECTED = 2;
+        int STATE_CONNECTING_FAILED = 3;
     }
 
     private final Collection<DeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
@@ -93,6 +95,15 @@
         mCallbacks.remove(callback);
     }
 
+    /**
+     * Creates a LocalMediaManager with references to given managers.
+     *
+     * It will obtain a {@link LocalBluetoothManager} by calling
+     * {@link LocalBluetoothManager#getInstance} and create an {@link InfoMediaManager} passing
+     * that bluetooth manager.
+     *
+     * It will use {@link BluetoothAdapter#getDefaultAdapter()] for setting the bluetooth adapter.
+     */
     public LocalMediaManager(Context context, String packageName, Notification notification) {
         mContext = context;
         mPackageName = packageName;
@@ -108,12 +119,18 @@
                 new InfoMediaManager(context, packageName, notification, mLocalBluetoothManager);
     }
 
+    /**
+     * Creates a LocalMediaManager with references to given managers.
+     *
+     * It will use {@link BluetoothAdapter#getDefaultAdapter()] for setting the bluetooth adapter.
+     */
     public LocalMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
             InfoMediaManager infoMediaManager, String packageName) {
         mContext = context;
         mLocalBluetoothManager = localBluetoothManager;
         mInfoMediaManager = infoMediaManager;
         mPackageName = packageName;
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
     /**
@@ -126,6 +143,7 @@
             final CachedBluetoothDevice cachedDevice =
                     ((BluetoothMediaDevice) device).getCachedDevice();
             if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
+                device.setState(MediaDeviceState.STATE_CONNECTING);
                 cachedDevice.connect();
                 return;
             }
@@ -140,6 +158,7 @@
             mCurrentConnectedDevice.disconnect();
         }
 
+        device.setState(MediaDeviceState.STATE_CONNECTING);
         if (TextUtils.isEmpty(mPackageName)) {
             mInfoMediaManager.connectDeviceWithoutPackageName(device);
         } else {
@@ -421,6 +440,7 @@
             MediaDevice connectDevice = getMediaDeviceById(mMediaDevices, id);
             connectDevice = connectDevice != null
                     ? connectDevice : updateCurrentConnectedDevice();
+            connectDevice.setState(MediaDeviceState.STATE_CONNECTED);
 
             if (connectDevice == mCurrentConnectedDevice) {
                 Log.d(TAG, "onConnectedDeviceChanged() this device all ready connected!");
@@ -438,6 +458,11 @@
 
         @Override
         public void onRequestFailed(int reason) {
+            for (MediaDevice device : mMediaDevices) {
+                if (device.getState() == MediaDeviceState.STATE_CONNECTING) {
+                    device.setState(MediaDeviceState.STATE_CONNECTING_FAILED);
+                }
+            }
             dispatchOnRequestFailed(reason);
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 33c3d7e..39e6a12 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -51,6 +51,7 @@
     int mType;
 
     private int mConnectedRecord;
+    private int mState;
 
     protected final Context mContext;
     protected final MediaRoute2Info mRouteInfo;
@@ -200,6 +201,22 @@
     }
 
     /**
+     * Set current device's state
+     */
+    public void setState(@LocalMediaManager.MediaDeviceState int state) {
+        mState = state;
+    }
+
+    /**
+     * Get current device's state
+     *
+     * @return state of device
+     */
+    public @LocalMediaManager.MediaDeviceState int getState() {
+        return mState;
+    }
+
+    /**
      * Rules:
      * 1. If there is one of the connected devices identified as a carkit, this carkit will
      * be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 18d8f14..559187d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.media;
 
+import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -27,6 +29,8 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
 
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -53,10 +57,13 @@
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class LocalMediaManagerTest {
 
+    private static final String TEST_DEVICE_NAME_1 = "device_name_1";
+    private static final String TEST_DEVICE_NAME_2 = "device_name_2";
     private static final String TEST_DEVICE_ID_1 = "device_id_1";
     private static final String TEST_DEVICE_ID_2 = "device_id_2";
     private static final String TEST_DEVICE_ID_3 = "device_id_3";
     private static final String TEST_CURRENT_DEVICE_ID = "currentDevice_id";
+    private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
 
     @Mock
     private BluetoothMediaManager mBluetoothMediaManager;
@@ -72,10 +79,18 @@
     private A2dpProfile mA2dpProfile;
     @Mock
     private LocalBluetoothProfileManager mLocalProfileManager;
+    @Mock
+    private MediaRouter2Manager mMediaRouter2Manager;
+    @Mock
+    private MediaRoute2Info mRouteInfo1;
+    @Mock
+    private MediaRoute2Info mRouteInfo2;
 
     private Context mContext;
     private LocalMediaManager mLocalMediaManager;
     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+    private InfoMediaDevice mInfoMediaDevice1;
+    private InfoMediaDevice mInfoMediaDevice2;
 
     @Before
     public void setUp() {
@@ -84,14 +99,20 @@
         final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
-
+        when(mRouteInfo1.getName()).thenReturn(TEST_DEVICE_NAME_1);
+        when(mRouteInfo1.getId()).thenReturn(TEST_DEVICE_ID_1);
+        when(mRouteInfo2.getName()).thenReturn(TEST_DEVICE_NAME_2);
+        when(mRouteInfo2.getId()).thenReturn(TEST_DEVICE_ID_2);
         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
         when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
         when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
 
+        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
+                TEST_PACKAGE_NAME);
+        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
+                TEST_PACKAGE_NAME);
         mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
                 mInfoMediaManager, "com.test.packagename");
-        mLocalMediaManager.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
     @Test
@@ -123,6 +144,32 @@
     }
 
     @Test
+    public void connectDevice_deviceNotEqualCurrentConnectedDevice_isConnectingState() {
+        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+        mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
+
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.connectDevice(mInfoMediaDevice2);
+
+        assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+                .STATE_CONNECTING);
+    }
+
+    @Test
+    public void connectDevice_deviceEqualCurrentConnectedDevice_notConnectingState() {
+        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+        mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1;
+
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.connectDevice(mInfoMediaDevice1);
+
+        assertThat(mInfoMediaDevice1.getState()).isNotEqualTo(LocalMediaManager.MediaDeviceState
+                .STATE_CONNECTING);
+    }
+
+    @Test
     public void connectDevice_bluetoothDeviceNotConnected_connectBluetoothDevice() {
         final MediaDevice device = mock(BluetoothMediaDevice.class);
         final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
@@ -388,6 +435,21 @@
     }
 
     @Test
+    public void onConnectedDeviceChanged_isConnectedState() {
+        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+        mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+
+        assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+                .STATE_DISCONNECTED);
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1);
+
+        assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+                .STATE_CONNECTED);
+    }
+
+    @Test
     public void onDeviceAttributesChanged_shouldDispatchDeviceListUpdate() {
         mLocalMediaManager.registerCallback(mCallback);
 
@@ -397,6 +459,26 @@
     }
 
     @Test
+    public void onRequestFailed_checkDevicesState() {
+        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice1);
+        mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2);
+        mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+        mInfoMediaDevice2.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+
+        assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+                .STATE_CONNECTING);
+        assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+                .STATE_CONNECTED);
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(REASON_UNKNOWN_ERROR);
+
+        assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+                .STATE_CONNECTING_FAILED);
+        assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState
+                .STATE_CONNECTED);
+    }
+
+    @Test
     public void getActiveMediaDevice_checkList() {
         final List<MediaDevice> devices = new ArrayList<>();
         final MediaDevice device1 = mock(MediaDevice.class);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 3f29b72..4b08387 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -448,4 +448,23 @@
         assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
                 mContext.getResources().getString(R.string.unknown));
     }
+
+    @Test
+    public void setState_verifyGetState() {
+        mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+        assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+                LocalMediaManager.MediaDeviceState.STATE_CONNECTED);
+
+        mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+        assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+                LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+
+        mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+        assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+                LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+
+        mInfoMediaDevice1.setState(LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
+        assertThat(mInfoMediaDevice1.getState()).isEqualTo(
+                LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
+    }
 }
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index a76f961..d506e7e 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -58,13 +58,15 @@
         android:elevation="@dimen/screenshot_preview_elevation"
         android:visibility="gone"
         android:background="@drawable/screenshot_rounded_corners"
-        android:adjustViewBounds="true"/>
+        android:adjustViewBounds="true"
+        android:contentDescription="@string/screenshot_preview_description"/>
     <FrameLayout
         android:id="@+id/global_screenshot_dismiss_button"
         android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
         android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
         android:elevation="7dp"
-        android:visibility="gone">
+        android:visibility="gone"
+        android:contentDescription="@string/screenshot_dismiss_ui_description">
         <ImageView
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c8b0d99..06e027d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -117,7 +117,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord
+        wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -529,4 +529,7 @@
     <!--  Flag to turn on the rendering of the above path or not  -->
     <bool name="config_enableDisplayCutoutProtection">false</bool>
 
+    <!-- Respect drawable/rounded.xml intrinsic size for multiple radius corner path customization -->
+    <bool name="config_roundedCornerMultipleRadius">false</bool>
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3543073..93bafdb 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -233,6 +233,10 @@
     <!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
     <string name="screenshot_failed_to_capture_text">Taking screenshots isn\'t allowed by the app or
         your organization</string>
+    <!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] -->
+    <string name="screenshot_dismiss_ui_description">Dismiss screenshot</string>
+    <!-- Content description indicating that tapping will open an app to view/edit the screenshot. [CHAR LIMIT=NONE] -->
+    <string name="screenshot_preview_description">Open screenshot</string>
 
     <!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
     <string name="screenrecord_name">Screen Recorder</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 4885ade..07bd3a0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -78,6 +78,9 @@
     public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
     // Winscope tracing is enabled
     public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
+    // The Assistant gesture should be constrained. It is up to the launcher implementation to
+    // decide how to constrain it
+    public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -92,7 +95,8 @@
             SYSUI_STATE_OVERVIEW_DISABLED,
             SYSUI_STATE_HOME_DISABLED,
             SYSUI_STATE_SEARCH_DISABLED,
-            SYSUI_STATE_TRACING_ENABLED
+            SYSUI_STATE_TRACING_ENABLED,
+            SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED
     })
     public @interface SystemUiStateFlags {}
 
@@ -112,6 +116,8 @@
         str.add((flags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0 ? "a11y_click" : "");
         str.add((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0 ? "a11y_long_click" : "");
         str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
+        str.add((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0
+                ? "asst_gesture_constrain" : "");
         return str.toString();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 431c451..90df124 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2740,6 +2740,8 @@
 
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
         mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver);
+
+        mHandler.removeCallbacksAndMessages(null);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 537a812..a8a3cae 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -50,6 +50,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
+import android.graphics.drawable.VectorDrawable;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -126,8 +127,9 @@
     private WindowManager mWindowManager;
     private int mRotation;
     private SecureSetting mColorInversionSetting;
-    private boolean mPendingRotationChange;
     private Handler mHandler;
+    private boolean mPendingRotationChange;
+    private boolean mIsRoundedCornerMultipleRadius;
 
     private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
             new CameraAvailabilityListener.CameraTransitionCallback() {
@@ -194,6 +196,8 @@
         mRotation = mContext.getDisplay().getRotation();
         mWindowManager = mContext.getSystemService(WindowManager.class);
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        mIsRoundedCornerMultipleRadius = mContext.getResources().getBoolean(
+                R.bool.config_roundedCornerMultipleRadius);
         updateRoundedCornerRadii();
         setupDecorations();
         setupCameraListener();
@@ -572,15 +576,22 @@
                 com.android.internal.R.dimen.rounded_corner_radius_top);
         final int newRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.rounded_corner_radius_bottom);
-
         final boolean roundedCornersChanged = mRoundedDefault != newRoundedDefault
                 || mRoundedDefaultBottom != newRoundedDefaultBottom
                 || mRoundedDefaultTop != newRoundedDefaultTop;
 
         if (roundedCornersChanged) {
-            mRoundedDefault = newRoundedDefault;
-            mRoundedDefaultTop = newRoundedDefaultTop;
-            mRoundedDefaultBottom = newRoundedDefaultBottom;
+            // If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the
+            // max(width, height) size of drawable/rounded.xml instead of rounded_corner_radius
+            if (mIsRoundedCornerMultipleRadius) {
+                final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
+                mRoundedDefault = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+                mRoundedDefaultTop = mRoundedDefaultBottom = mRoundedDefault;
+            } else {
+                mRoundedDefault = newRoundedDefault;
+                mRoundedDefaultTop = newRoundedDefaultTop;
+                mRoundedDefaultBottom = newRoundedDefaultBottom;
+            }
             onTuningChanged(SIZE, null);
         }
     }
@@ -631,7 +642,8 @@
     }
 
     private boolean hasRoundedCorners() {
-        return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0;
+        return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0
+                || mIsRoundedCornerMultipleRadius;
     }
 
     private boolean shouldShowRoundedCorner(@BoundsPosition int pos) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 5077e18..cc4ee89 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -1,6 +1,9 @@
 package com.android.systemui.assist;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -43,6 +46,7 @@
 import com.android.settingslib.applications.InterestingConfigChanges;
 import com.android.systemui.R;
 import com.android.systemui.assist.ui.DefaultUiController;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -51,6 +55,8 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import dagger.Lazy;
+
 /**
  * Class to manage everything related to assist in SystemUI.
  */
@@ -98,6 +104,9 @@
     public static final String INVOCATION_TYPE_KEY = "invocation_type";
     protected static final String ACTION_KEY = "action";
     protected static final String SHOW_ASSIST_HANDLES_ACTION = "show_assist_handles";
+    protected static final String SET_ASSIST_GESTURE_CONSTRAINED_ACTION =
+            "set_assist_gesture_constrained";
+    protected static final String CONSTRAINED_KEY = "should_constrain";
 
     public static final int INVOCATION_TYPE_GESTURE = 1;
     public static final int INVOCATION_TYPE_ACTIVE_EDGE = 2;
@@ -120,6 +129,7 @@
     private final PhoneStateMonitor mPhoneStateMonitor;
     private final AssistHandleBehaviorController mHandleController;
     private final UiController mUiController;
+    protected final Lazy<SysUiState> mSysUiState;
 
     private AssistOrbContainer mView;
     private final DeviceProvisionedController mDeviceProvisionedController;
@@ -185,7 +195,8 @@
             CommandQueue commandQueue,
             PhoneStateMonitor phoneStateMonitor,
             OverviewProxyService overviewProxyService,
-            ConfigurationController configurationController) {
+            ConfigurationController configurationController,
+            Lazy<SysUiState> sysUiState) {
         mContext = context;
         mDeviceProvisionedController = controller;
         mCommandQueue = commandQueue;
@@ -206,6 +217,8 @@
 
         mUiController = new DefaultUiController(mContext);
 
+        mSysUiState = sysUiState;
+
         overviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
             @Override
             public void onAssistantProgress(float progress) {
@@ -243,8 +256,16 @@
                         if (VERBOSE) {
                             Log.v(TAG, "UI hints received");
                         }
-                        if (SHOW_ASSIST_HANDLES_ACTION.equals(hints.getString(ACTION_KEY))) {
+
+                        String action = hints.getString(ACTION_KEY);
+                        if (SHOW_ASSIST_HANDLES_ACTION.equals(action)) {
                             requestAssistHandles();
+                        } else if (SET_ASSIST_GESTURE_CONSTRAINED_ACTION.equals(action)) {
+                            mSysUiState.get()
+                                    .setFlag(
+                                            SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
+                                            hints.getBoolean(CONSTRAINED_KEY, false))
+                                    .commitUpdate(DEFAULT_DISPLAY);
                         }
                     }
                 });
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 406e7ce..b39dd1a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -52,6 +52,7 @@
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.ZenModeConfig;
 import android.util.ArraySet;
@@ -146,13 +147,15 @@
     private BubbleData mBubbleData;
     @Nullable private BubbleStackView mStackView;
     private BubbleIconFactory mBubbleIconFactory;
-    private int mMaxBubbles;
 
     // Tracks the id of the current (foreground) user.
     private int mCurrentUserId;
     // Saves notification keys of active bubbles when users are switched.
     private final SparseSetArray<String> mSavedBubbleKeysPerUser;
 
+    // Used when ranking updates occur and we check if things should bubble / unbubble
+    private NotificationListenerService.Ranking mTmpRanking;
+
     // Saves notification keys of user created "fake" bubbles so that we can allow notifications
     // like these to bubble by default. Doesn't persist across reboots, not a long-term solution.
     private final HashSet<String> mUserCreatedBubbles;
@@ -338,7 +341,6 @@
 
         configurationController.addCallback(this /* configurationListener */);
 
-        mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
         mBubbleData = data;
         mBubbleData.setListener(mBubbleDataListener);
         mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() {
@@ -939,9 +941,29 @@
         }
     }
 
+    /**
+     * Called when NotificationListener has received adjusted notification rank and reapplied
+     * filtering and sorting. This is used to dismiss or create bubbles based on changes in
+     * permissions on the notification channel or the global setting.
+     *
+     * @param rankingMap the updated ranking map from NotificationListenerService
+     */
     private void onRankingUpdated(RankingMap rankingMap) {
-        // Forward to BubbleData to block any bubbles which should no longer be shown
-        mBubbleData.notificationRankingUpdated(rankingMap);
+        if (mTmpRanking == null) {
+            mTmpRanking = new NotificationListenerService.Ranking();
+        }
+        String[] orderedKeys = rankingMap.getOrderedKeys();
+        for (int i = 0; i < orderedKeys.length; i++) {
+            String key = orderedKeys[i];
+            NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
+            rankingMap.getRanking(key, mTmpRanking);
+            if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) {
+                mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
+            } else if (entry != null && mTmpRanking.isBubble()) {
+                entry.setFlagBubble(true);
+                onEntryUpdated(entry);
+            }
+        }
     }
 
     @SuppressWarnings("FieldCanBeLocal")
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 54b83c0..ff5e13c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -26,7 +26,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.RankingMap;
 import android.util.Log;
 import android.util.Pair;
 
@@ -298,31 +297,6 @@
     }
 
     /**
-     * Called when NotificationListener has received adjusted notification rank and reapplied
-     * filtering and sorting. This is used to dismiss any bubbles which should no longer be shown
-     * due to changes in permissions on the notification channel or the global setting.
-     *
-     * @param rankingMap the updated ranking map from NotificationListenerService
-     */
-    public void notificationRankingUpdated(RankingMap rankingMap) {
-        if (mTmpRanking == null) {
-            mTmpRanking = new NotificationListenerService.Ranking();
-        }
-
-        String[] orderedKeys = rankingMap.getOrderedKeys();
-        for (int i = 0; i < orderedKeys.length; i++) {
-            String key = orderedKeys[i];
-            if (hasBubbleWithKey(key)) {
-                rankingMap.getRanking(key, mTmpRanking);
-                if (!mTmpRanking.canBubble()) {
-                    doRemove(key, BubbleController.DISMISS_BLOCKED);
-                }
-            }
-        }
-        dispatchPendingChanges();
-    }
-
-    /**
      * Adds a group key indicating that the summary for this group should be suppressed.
      *
      * @param groupKey the group key of the group whose summary should be suppressed.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 0778d5b..e666fb5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -471,7 +471,8 @@
         mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
         int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
 
-        mStackAnimationController = new StackAnimationController(floatingContentCoordinator);
+        mStackAnimationController = new StackAnimationController(
+                floatingContentCoordinator, this::getBubbleCount);
 
         mExpandedAnimationController = new ExpandedAnimationController(
                 mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 3eaa90c..9b5dc31 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -409,11 +409,8 @@
         mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
 
         // Includes overflow button.
-        // TODO(b/148675523) this is a temporary work around; change back once we have proper fix.
-//        float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
-//                - (mBubblesMaxRendered + 1) * mBubbleSizePx;
-        float totalGapWidth = getAvailableScreenWidth(true /* includeStableInsets */)
-                - (mExpandedViewPadding * 2) - (mBubblesMaxRendered + 1) * mBubbleSizePx;
+        float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
+                - (mBubblesMaxRendered + 1) * mBubbleSizePx;
         mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
 
         // Ensure that all child views are at 1x scale, and visible, in case they were animating
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 86387f1..7ee162e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -43,6 +43,7 @@
 import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.Set;
+import java.util.function.IntSupplier;
 
 /**
  * Animation controller for bubbles when they're in their stacked state. Stacked bubbles sit atop
@@ -88,6 +89,9 @@
     private static final int SPRING_AFTER_FLING_STIFFNESS = 750;
     private static final float SPRING_AFTER_FLING_DAMPING_RATIO = 0.85f;
 
+    /** Sentinel value for unset position value. */
+    private static final float UNSET = -Float.MIN_VALUE;
+
     /**
      * Minimum fling velocity required to trigger moving the stack from one side of the screen to
      * the other.
@@ -132,7 +136,7 @@
      * The Y position of the stack before the IME became visible, or {@link Float#MIN_VALUE} if the
      * IME is not visible or the user moved the stack since the IME became visible.
      */
-    private float mPreImeY = Float.MIN_VALUE;
+    private float mPreImeY = UNSET;
 
     /**
      * Animations on the stack position itself, which would have been started in
@@ -241,9 +245,15 @@
         }
     };
 
+    /** Returns the number of 'real' bubbles (excluding the overflow bubble). */
+    private IntSupplier mBubbleCountSupplier;
+
     public StackAnimationController(
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            IntSupplier bubbleCountSupplier) {
         mFloatingContentCoordinator = floatingContentCoordinator;
+        mBubbleCountSupplier = bubbleCountSupplier;
+
     }
 
     /**
@@ -256,7 +266,7 @@
 
         // If we manually move the bubbles with the IME open, clear the return point since we don't
         // want the stack to snap away from the new position.
-        mPreImeY = Float.MIN_VALUE;
+        mPreImeY = UNSET;
 
         moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_X, x);
         moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_Y, y);
@@ -505,26 +515,27 @@
      * Animates the stack either away from the newly visible IME, or back to its original position
      * due to the IME going away.
      *
-     * @return The destination Y value of the stack due to the IME movement.
+     * @return The destination Y value of the stack due to the IME movement (or the current position
+     * of the stack if it's not moving).
      */
     public float animateForImeVisibility(boolean imeVisible) {
         final float maxBubbleY = getAllowableStackPositionRegion().bottom;
-        float destinationY = Float.MIN_VALUE;
+        float destinationY = UNSET;
 
         if (imeVisible) {
             // Stack is lower than it should be and overlaps the now-visible IME.
-            if (mStackPosition.y > maxBubbleY && mPreImeY == Float.MIN_VALUE) {
+            if (mStackPosition.y > maxBubbleY && mPreImeY == UNSET) {
                 mPreImeY = mStackPosition.y;
                 destinationY = maxBubbleY;
             }
         } else {
-            if (mPreImeY > Float.MIN_VALUE) {
+            if (mPreImeY != UNSET) {
                 destinationY = mPreImeY;
-                mPreImeY = Float.MIN_VALUE;
+                mPreImeY = UNSET;
             }
         }
 
-        if (destinationY > Float.MIN_VALUE) {
+        if (destinationY != UNSET) {
             springFirstBubbleWithStackFollowing(
                     DynamicAnimation.TRANSLATION_Y,
                     getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null)
@@ -535,7 +546,7 @@
             notifyFloatingCoordinatorStackAnimatingTo(mStackPosition.x, destinationY);
         }
 
-        return destinationY;
+        return destinationY != UNSET ? destinationY : mStackPosition.y;
     }
 
     /**
@@ -588,7 +599,7 @@
                     mLayout.getHeight()
                             - mBubbleSize
                             - mBubblePaddingTop
-                            - (mImeHeight > Float.MIN_VALUE ? mImeHeight + mBubblePaddingTop : 0f)
+                            - (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f)
                             - Math.max(
                             insets.getStableInsetBottom(),
                             insets.getDisplayCutout() != null
@@ -669,6 +680,8 @@
                 new SpringAnimation(this, firstBubbleProperty)
                         .setSpring(spring)
                         .addEndListener((dynamicAnimation, b, v, v1) -> {
+                            mRestingStackPosition.set(mStackPosition);
+
                             if (after != null) {
                                 for (Runnable callback : after) {
                                     callback.run();
@@ -736,7 +749,7 @@
             return;
         }
 
-        if (mLayout.getChildCount() == 1) {
+        if (getBubbleCount() == 1) {
             // If this is the first child added, position the stack in its starting position.
             moveStackToStartPosition();
         } else if (isStackPositionSet() && mLayout.indexOfChild(child) == 0) {
@@ -758,7 +771,7 @@
                 .start();
 
         // If there are other bubbles, pull them into the correct position.
-        if (mLayout.getChildCount() > 0) {
+        if (getBubbleCount() > 0) {
             animationForChildAtIndex(0).translationX(mStackPosition.x).start();
         } else {
             // When all children are removed ensure stack position is sane
@@ -979,6 +992,11 @@
         return mMagnetizedStack;
     }
 
+    /** Returns the number of 'real' bubbles (excluding overflow). */
+    private int getBubbleCount() {
+        return mBubbleCountSupplier.getAsInt();
+    }
+
     /**
      * FloatProperty that uses {@link #moveFirstBubbleWithStackFollowing} to set the first bubble's
      * translation and animate the rest of the stack with it. A DynamicAnimation can animate this
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 956b4aa..8c572fe 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -26,9 +26,11 @@
 import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
+import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
 import com.android.systemui.stackdivider.DividerModule;
@@ -85,6 +87,10 @@
             BatteryControllerImpl controllerImpl);
 
     @Binds
+    @Singleton
+    public abstract QSFactory provideQSFactory(QSFactoryImpl qsFactoryImpl);
+
+    @Binds
     abstract DockManager bindDockManager(DockManagerImpl dockManager);
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 6d16253..b99d765 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -116,16 +116,17 @@
 import javax.inject.Inject;
 
 /**
- * Helper to show the global actions dialog.  Each item is an {@link Action} that
- * may show depending on whether the keyguard is showing, and whether the device
- * is provisioned.
+ * Helper to show the global actions dialog.  Each item is an {@link Action} that may show depending
+ * on whether the keyguard is showing, and whether the device is provisioned.
  */
 public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
-        DialogInterface.OnShowListener, ConfigurationController.ConfigurationListener {
+        DialogInterface.OnShowListener,
+        ConfigurationController.ConfigurationListener,
+        GlobalActionsPanelPlugin.Callbacks {
 
-    static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
-    static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
-    static public final String SYSTEM_DIALOG_REASON_DREAM = "dream";
+    public static final String SYSTEM_DIALOG_REASON_KEY = "reason";
+    public static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
+    public static final String SYSTEM_DIALOG_REASON_DREAM = "dream";
 
     private static final String TAG = "GlobalActionsDialog";
 
@@ -271,8 +272,9 @@
             @Override
             public void onUnlockedChanged() {
                 if (mDialog != null && mDialog.mPanelController != null) {
-                    boolean locked = !keyguardStateController.canDismissLockScreen();
-                    mDialog.mPanelController.onDeviceLockStateChanged(locked);
+                    boolean unlocked = keyguardStateController.isUnlocked()
+                            || keyguardStateController.canDismissLockScreen();
+                    mDialog.mPanelController.onDeviceLockStateChanged(unlocked);
                 }
             }
         });
@@ -386,7 +388,7 @@
                 mItems.add(getSettingsAction());
             } else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
                 if (Settings.Secure.getIntForUser(mContentResolver,
-                            Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
+                        Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, getCurrentUser().id) != 0
                         && shouldDisplayLockdown()) {
                     mItems.add(getLockdownAction());
                 }
@@ -420,26 +422,7 @@
 
         mAdapter = new MyAdapter();
 
-        GlobalActionsPanelPlugin.PanelViewController panelViewController =
-                mPanelPlugin != null
-                        ? mPanelPlugin.onPanelShown(
-                                new GlobalActionsPanelPlugin.Callbacks() {
-                                    @Override
-                                    public void dismissGlobalActionsMenu() {
-                                        dismissDialog();
-                                    }
-
-                                    @Override
-                                    public void startPendingIntentDismissingKeyguard(
-                                            PendingIntent intent) {
-                                        mActivityStarter
-                                                .startPendingIntentDismissingKeyguard(intent);
-                                    }
-                                },
-                                !mKeyguardStateController.isUnlocked())
-                        : null;
-
-        ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
+        ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, getWalletPanelViewController(),
                 mDepthController, mSysuiColorExtractor, mStatusBarService,
                 mNotificationShadeWindowController,
                 shouldShowControls() ? mControlsUiController : null, mBlurUtils);
@@ -477,6 +460,33 @@
         mConfigurationController.removeCallback(this);
     }
 
+    @Nullable
+    private GlobalActionsPanelPlugin.PanelViewController getWalletPanelViewController() {
+        if (mPanelPlugin == null) {
+            return null;
+        }
+        return mPanelPlugin.onPanelShown(this, !mKeyguardStateController.isUnlocked());
+    }
+
+    /**
+     * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
+     * called when the quick access wallet requests dismissal.
+     */
+    @Override
+    public void dismissGlobalActionsMenu() {
+        dismissDialog();
+    }
+
+    /**
+     * Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
+     * called when the quick access wallet requests that an intent be started (with lock screen
+     * shown first if needed).
+     */
+    @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent pendingIntent) {
+        mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent);
+    }
+
     private final class PowerAction extends SinglePressAction implements LongPressAction {
         private PowerAction() {
             super(R.drawable.ic_lock_power_off,
@@ -919,7 +929,9 @@
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public void onDismiss(DialogInterface dialog) {
         if (mDialog == dialog) {
             mDialog = null;
@@ -935,17 +947,19 @@
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public void onShow(DialogInterface dialog) {
         mMetricsLogger.visible(MetricsEvent.POWER_MENU);
     }
 
     /**
-     * The adapter used for the list within the global actions dialog, taking
-     * into account whether the keyguard is showing via
-     * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing} and whether
-     * the device is provisioned
-     * via {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
+     * The adapter used for the list within the global actions dialog, taking into account whether
+     * the keyguard is showing via
+     * {@link com.android.systemui.globalactions.GlobalActionsDialog#mKeyguardShowing}
+     * and whether the device is provisioned via
+     * {@link com.android.systemui.globalactions.GlobalActionsDialog#mDeviceProvisioned}.
      */
     public class MyAdapter extends MultiListAdapter {
         private int countItems(boolean separated) {
@@ -1076,8 +1090,7 @@
      */
     public interface Action {
         /**
-         * @return Text that will be announced when dialog is created.  null
-         * for none.
+         * @return Text that will be announced when dialog is created.  null for none.
          */
         CharSequence getLabelForAccessibility(Context context);
 
@@ -1086,15 +1099,13 @@
         void onPress();
 
         /**
-         * @return whether this action should appear in the dialog when the keygaurd
-         * is showing.
+         * @return whether this action should appear in the dialog when the keygaurd is showing.
          */
         boolean showDuringKeyguard();
 
         /**
-         * @return whether this action should appear in the dialog before the
-         * device is provisioned.onlongpress
-         *
+         * @return whether this action should appear in the dialog before the device is
+         * provisioned.onlongpress
          */
         boolean showBeforeProvisioning();
 
@@ -1113,8 +1124,7 @@
     }
 
     /**
-     * A single press action maintains no state, just responds to a press
-     * and takes an action.
+     * A single press action maintains no state, just responds to a press and takes an action.
      */
 
     private abstract class SinglePressAction implements Action {
@@ -1188,8 +1198,8 @@
     }
 
     /**
-     * A toggle action knows whether it is on or off, and displays an icon
-     * and status message accordingly.
+     * A toggle action knows whether it is on or off, and displays an icon and status message
+     * accordingly.
      */
     private static abstract class ToggleAction implements Action {
 
@@ -1239,8 +1249,7 @@
         }
 
         /**
-         * Override to make changes to resource IDs just before creating the
-         * View.
+         * Override to make changes to resource IDs just before creating the View.
          */
         void willCreate() {
 
@@ -1296,9 +1305,9 @@
         }
 
         /**
-         * Implementations may override this if their state can be in on of the intermediate
-         * states until some notification is received (e.g airplane mode is 'turning off' until
-         * we know the wireless connections are back online
+         * Implementations may override this if their state can be in on of the intermediate states
+         * until some notification is received (e.g airplane mode is 'turning off' until we know the
+         * wireless connections are back online
          *
          * @param buttonOn Whether the button was turned on or off
          */
@@ -1316,12 +1325,13 @@
     private class AirplaneModeAction extends ToggleAction {
         AirplaneModeAction() {
             super(
-                R.drawable.ic_lock_airplane_mode,
-                R.drawable.ic_lock_airplane_mode_off,
-                R.string.global_actions_toggle_airplane_mode,
-                R.string.global_actions_airplane_mode_on_status,
-                R.string.global_actions_airplane_mode_off_status);
+                    R.drawable.ic_lock_airplane_mode,
+                    R.drawable.ic_lock_airplane_mode_off,
+                    R.string.global_actions_toggle_airplane_mode,
+                    R.string.global_actions_airplane_mode_on_status,
+                    R.string.global_actions_airplane_mode_off_status);
         }
+
         void onToggle(boolean on) {
             if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {
                 mIsWaitingForEcmExit = true;
@@ -1607,11 +1617,11 @@
             window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
             window.addFlags(
                     WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
-                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                    | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                            | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+                            | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                            | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
             window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
             window.getAttributes().setFitInsetsTypes(0 /* types */);
             setTitle(R.string.global_actions);
@@ -1755,7 +1765,8 @@
 
         /**
          * Updates background and system bars according to current GradientColors.
-         * @param colors Colors and hints to use.
+         *
+         * @param colors  Colors and hints to use.
          * @param animate Interpolates gradient if true, just sets otherwise.
          */
         private void updateColors(GradientColors colors, boolean animate) {
@@ -1921,8 +1932,8 @@
     }
 
     /**
-     * Determines whether or not the Global Actions menu should be forced to
-     * use the newer grid-style layout.
+     * Determines whether or not the Global Actions menu should be forced to use the newer
+     * grid-style layout.
      */
     private static boolean isForceGridEnabled(Context context) {
         return isPanelDebugModeEnabled(context);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3d708a9..226ac16c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -91,8 +91,10 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NavigationModeController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -374,6 +376,7 @@
 
     private boolean mLockLater;
     private boolean mShowHomeOverLockscreen;
+    private boolean mInGestureNavigationMode;
 
     private boolean mWakeAndUnlocking;
     private IKeyguardDrawnCallback mDrawnCallback;
@@ -720,7 +723,8 @@
             KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager,
             @UiBackground Executor uiBgExecutor, PowerManager powerManager,
             TrustManager trustManager,
-            DeviceConfigProxy deviceConfig) {
+            DeviceConfigProxy deviceConfig,
+            NavigationModeController navigationModeController) {
         super(context);
         mFalsingManager = falsingManager;
         mLockPatternUtils = lockPatternUtils;
@@ -742,6 +746,10 @@
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 mHandler::post,
                 mOnPropertiesChangedListener);
+        mInGestureNavigationMode =
+                QuickStepContract.isGesturalMode(navigationModeController.addListener(mode -> {
+                    mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
+                }));
     }
 
     public void userActivity() {
@@ -2013,7 +2021,7 @@
             // windows that appear on top, ever
             int flags = StatusBarManager.DISABLE_NONE;
             if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
-                if (!mShowHomeOverLockscreen) {
+                if (!mShowHomeOverLockscreen || !mInGestureNavigationMode) {
                     flags |= StatusBarManager.DISABLE_HOME;
                 }
                 flags |= StatusBarManager.DISABLE_RECENT;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 9be4786..7a63a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.NavigationModeController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.DeviceConfigProxy;
@@ -64,7 +65,8 @@
             PowerManager powerManager,
             TrustManager trustManager,
             @UiBackground Executor uiBgExecutor,
-            DeviceConfigProxy deviceConfig) {
+            DeviceConfigProxy deviceConfig,
+            NavigationModeController navigationModeController) {
         return new KeyguardViewMediator(
                 context,
                 falsingManager,
@@ -78,6 +80,7 @@
                 uiBgExecutor,
                 powerManager,
                 trustManager,
-                deviceConfig);
+                deviceConfig,
+                navigationModeController);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index a161d03..e208ee2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -33,7 +33,6 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
-import android.os.Handler;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -64,6 +63,7 @@
 public class MediaControlPanel implements NotificationMediaManager.MediaListener {
     private static final String TAG = "MediaControlPanel";
     private final NotificationMediaManager mMediaManager;
+    private final Executor mForegroundExecutor;
     private final Executor mBackgroundExecutor;
 
     private Context mContext;
@@ -102,15 +102,18 @@
      * @param manager
      * @param layoutId layout resource to use for this control panel
      * @param actionIds resource IDs for action buttons in the layout
+     * @param foregroundExecutor foreground executor
      * @param backgroundExecutor background executor, used for processing artwork
      */
     public MediaControlPanel(Context context, ViewGroup parent, NotificationMediaManager manager,
-            @LayoutRes int layoutId, int[] actionIds, Executor backgroundExecutor) {
+            @LayoutRes int layoutId, int[] actionIds, Executor foregroundExecutor,
+            Executor backgroundExecutor) {
         mContext = context;
         LayoutInflater inflater = LayoutInflater.from(mContext);
         mMediaNotifView = (LinearLayout) inflater.inflate(layoutId, parent, false);
         mMediaManager = manager;
         mActionIds = actionIds;
+        mForegroundExecutor = foregroundExecutor;
         mBackgroundExecutor = backgroundExecutor;
     }
 
@@ -176,15 +179,17 @@
         mMediaNotifView.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
 
         // Click action
-        mMediaNotifView.setOnClickListener(v -> {
-            try {
-                contentIntent.send();
-                // Also close shade
-                mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
-            } catch (PendingIntent.CanceledException e) {
-                Log.e(TAG, "Pending intent was canceled", e);
-            }
-        });
+        if (contentIntent != null) {
+            mMediaNotifView.setOnClickListener(v -> {
+                try {
+                    contentIntent.send();
+                    // Also close shade
+                    mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+                } catch (PendingIntent.CanceledException e) {
+                    Log.e(TAG, "Pending intent was canceled", e);
+                }
+            });
+        }
 
         // App icon
         ImageView appIcon = mMediaNotifView.findViewById(R.id.icon);
@@ -316,7 +321,7 @@
 
         // Now that it's resized, update the UI
         final RoundedBitmapDrawable result = roundedDrawable;
-        albumView.getHandler().post(() -> {
+        mForegroundExecutor.execute(() -> {
             if (result != null) {
                 albumView.setImageDrawable(result);
                 albumView.setVisibility(View.VISIBLE);
@@ -335,8 +340,7 @@
         if (mSeamless == null) {
             return;
         }
-        Handler handler = mSeamless.getHandler();
-        handler.post(() -> {
+        mForegroundExecutor.execute(() -> {
             updateChipInternal(device);
         });
     }
@@ -401,12 +405,15 @@
                         new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
                 mContext.sendBroadcast(intent);
             } else {
-                Log.d(TAG, "No receiver to restart");
                 // If we don't have a receiver, try relaunching the activity instead
-                try {
-                    mController.getSessionActivity().send();
-                } catch (PendingIntent.CanceledException e) {
-                    Log.e(TAG, "Pending intent was canceled", e);
+                if (mController.getSessionActivity() != null) {
+                    try {
+                        mController.getSessionActivity().send();
+                    } catch (PendingIntent.CanceledException e) {
+                        Log.e(TAG, "Pending intent was canceled", e);
+                    }
+                } else {
+                    Log.e(TAG, "No receiver or activity to restart");
                 }
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index fb348f4..8be2502 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -67,7 +67,7 @@
 
     private ComponentName mLastPipComponentName;
     private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
-    private Size mReentrySize = null;
+    private Size mReentrySize;
 
     private float mDefaultAspectRatio;
     private float mMinAspectRatio;
@@ -77,6 +77,7 @@
     private int mDefaultMinSize;
     private Point mScreenEdgeInsets;
     private int mCurrentMinSize;
+    private Size mOverrideMinimalSize;
 
     private boolean mIsImeShowing;
     private int mImeHeight;
@@ -226,11 +227,14 @@
     /**
      * @return {@link Rect} of the destination PiP window bounds.
      */
-    Rect getDestinationBounds(float aspectRatio, Rect bounds) {
+    Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) {
         final Rect destinationBounds;
-        final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
         if (bounds == null) {
+            final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
             destinationBounds = new Rect(defaultBounds);
+            if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) {
+                mOverrideMinimalSize = minimalSize;
+            }
         } else {
             destinationBounds = new Rect(bounds);
         }
@@ -335,7 +339,6 @@
      */
     private void transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
             boolean useCurrentMinEdgeSize) {
-
         // Save the snap fraction and adjust the size based on the new aspect ratio.
         final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
                 getMovementBounds(stackBounds));
@@ -354,10 +357,38 @@
         final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
         final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
         stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
+        // apply the override minimal size if applicable, this minimal size is specified by app
+        if (mOverrideMinimalSize != null) {
+            transformBoundsToMinimalSize(stackBounds, aspectRatio, mOverrideMinimalSize);
+        }
         mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
     }
 
     /**
+     * Transforms a given bounds to meet the minimal size constraints.
+     * This function assumes the given {@param stackBounds} qualifies {@param aspectRatio}.
+     */
+    private void transformBoundsToMinimalSize(Rect stackBounds, float aspectRatio,
+            Size minimalSize) {
+        if (minimalSize == null) return;
+        final Size adjustedMinimalSize;
+        final float minimalSizeAspectRatio =
+                minimalSize.getWidth() / (float) minimalSize.getHeight();
+        if (minimalSizeAspectRatio > aspectRatio) {
+            // minimal size is wider, fixed the width and increase the height
+            adjustedMinimalSize = new Size(
+                    minimalSize.getWidth(), (int) (minimalSize.getWidth() / aspectRatio));
+        } else {
+            adjustedMinimalSize = new Size(
+                    (int) (minimalSize.getHeight() * aspectRatio), minimalSize.getHeight());
+        }
+        final Rect containerBounds = new Rect(stackBounds);
+        Gravity.apply(mDefaultStackGravity,
+                adjustedMinimalSize.getWidth(), adjustedMinimalSize.getHeight(),
+                containerBounds, stackBounds);
+    }
+
+    /**
      * @return the default bounds to show the PIP, if a {@param snapFraction} and {@param size} are
      * provided, then it will apply the default bounds to the provided snap fraction and size.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 4a4a638..dc1b5d7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -30,11 +30,14 @@
 import android.app.ITaskOrganizerController;
 import android.app.PictureInPictureParams;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Size;
 import android.view.ITaskOrganizer;
 import android.view.IWindowContainer;
 import android.view.SurfaceControl;
@@ -45,7 +48,9 @@
 import com.android.systemui.pip.phone.PipUpdateThread;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.function.Consumer;
 
@@ -77,6 +82,7 @@
     private final Rect mLastReportedBounds = new Rect();
     private final int mEnterExitAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+    private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>();
 
     // These callbacks are called on the update thread
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -204,26 +210,12 @@
         mOneShotAnimationType = animationType;
     }
 
-    /**
-     * Callback to issue the final {@link WindowContainerTransaction} on end of movements.
-     * @param destinationBounds the final bounds.
-     */
-    public void onMotionMovementEnd(Rect destinationBounds) {
-        try {
-            mLastReportedBounds.set(destinationBounds);
-            final WindowContainerTransaction wct = new WindowContainerTransaction();
-            wct.setBounds(mToken, destinationBounds);
-            mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to apply window container transaction", e);
-        }
-    }
-
     @Override
     public void taskAppeared(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(info, "Requires RunningTaskInfo");
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
+                getAspectRatioOrDefault(info.pictureInPictureParams),
+                null /* bounds */, getMinimalSize(info.topActivityInfo));
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         mTaskInfo = info;
         mToken = mTaskInfo.token;
@@ -234,6 +226,7 @@
             throw new RuntimeException("Unable to get leash", e);
         }
         final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
+        mBoundsToRestore.put(mToken.asBinder(), currentBounds);
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
             scheduleAnimateResizePip(currentBounds, destinationBounds,
                     TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null);
@@ -258,8 +251,8 @@
             Log.wtf(TAG, "Unrecognized token: " + token);
             return;
         }
-        scheduleAnimateResizePip(mLastReportedBounds,
-                info.configuration.windowConfiguration.getBounds(),
+        final Rect boundsToRestore = mBoundsToRestore.remove(token.asBinder());
+        scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore,
                 TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, null);
         mInPip = false;
     }
@@ -276,7 +269,8 @@
             return;
         }
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                getAspectRatioOrDefault(newParams), null /* bounds */);
+                getAspectRatioOrDefault(newParams),
+                null /* bounds */, getMinimalSize(info.topActivityInfo));
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration, null);
     }
@@ -446,6 +440,14 @@
                 .start());
     }
 
+    private Size getMinimalSize(ActivityInfo activityInfo) {
+        if (activityInfo == null || activityInfo.windowLayout == null) {
+            return null;
+        }
+        final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
+        return new Size(windowLayout.minWidth, windowLayout.minHeight);
+    }
+
     private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
         return params == null
                 ? mPipBoundsHandler.getDefaultAspectRatio()
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 15a0088..449a2bc 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -426,7 +426,7 @@
         cancelAnimations();
 
         mAnimatedBoundsPhysicsAnimator
-                .withEndActions(() ->  mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds))
+                .withEndActions(() ->  mPipTaskOrganizer.scheduleFinishResizePip(mAnimatedBounds))
                 .addUpdateListener(mResizePipUpdateListener)
                 .start();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 8fff419..25acce6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -65,6 +65,7 @@
     private final PointF mDownPoint = new PointF();
     private final Point mMaxSize = new Point();
     private final Point mMinSize = new Point();
+    private final Rect mLastResizeBounds = new Rect();
     private final Rect mTmpBounds = new Rect();
     private final int mDelta;
 
@@ -187,17 +188,13 @@
     private void onMotionEvent(MotionEvent ev) {
         int action = ev.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
+            mLastResizeBounds.setEmpty();
             mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
             if (mAllowGesture) {
                 mDownPoint.set(ev.getX(), ev.getY());
             }
 
         } else if (mAllowGesture) {
-            final Rect currentPipBounds = mMotionHelper.getBounds();
-            Rect newSize = TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(), mDownPoint.x,
-                    mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, mMinSize.y, mMaxSize,
-                    true, true);
-            mPipBoundsHandler.transformBoundsToAspectRatio(newSize);
             switch (action) {
                 case MotionEvent.ACTION_POINTER_DOWN:
                     // We do not support multi touch for resizing via drag
@@ -206,11 +203,16 @@
                 case MotionEvent.ACTION_MOVE:
                     // Capture inputs
                     mInputMonitor.pilferPointers();
-                    //TODO: Actually do resize here.
+                    final Rect currentPipBounds = mMotionHelper.getBounds();
+                    mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
+                            mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
+                            mMinSize.y, mMaxSize, true, true));
+                    mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
+                    mPipTaskOrganizer.scheduleResizePip(mLastResizeBounds, null);
                     break;
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
-                    //TODO: Finish resize operation here.
+                    mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds);
                     mMotionHelper.synchronizePinnedStackBounds();
                     mCtrlType = CTRL_NONE;
                     mAllowGesture = false;
@@ -223,7 +225,7 @@
         mMaxSize.set(maxX, maxY);
     }
 
-    void updateMiniSize(int minX, int minY) {
+    void updateMinSize(int minX, int minY) {
         mMinSize.set(minX, minY);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index b5fb1a9..9b67d80 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -42,6 +42,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.systemui.R;
 import com.android.systemui.pip.PipBoundsHandler;
@@ -74,7 +75,7 @@
     private final Context mContext;
     private final IActivityManager mActivityManager;
     private final PipBoundsHandler mPipBoundsHandler;
-    private final PipResizeGestureHandler mPipResizeGestureHandler;
+    private PipResizeGestureHandler mPipResizeGestureHandler;
     private IPinnedStackController mPinnedStackController;
 
     private final PipMenuActivityController mMenuController;
@@ -85,14 +86,17 @@
 
     // The current movement bounds
     private Rect mMovementBounds = new Rect();
+    // The current resized bounds, changed by user resize.
+    // This is used during expand/un-expand to save/restore the user's resized size.
+    @VisibleForTesting Rect mResizedBounds = new Rect();
 
     // The reference inset bounds, used to determine the dismiss fraction
     private Rect mInsetBounds = new Rect();
     // The reference bounds used to calculate the normal/expanded target bounds
     private Rect mNormalBounds = new Rect();
-    private Rect mNormalMovementBounds = new Rect();
+    @VisibleForTesting Rect mNormalMovementBounds = new Rect();
     private Rect mExpandedBounds = new Rect();
-    private Rect mExpandedMovementBounds = new Rect();
+    @VisibleForTesting Rect mExpandedMovementBounds = new Rect();
     private int mExpandedShortestEdgeSize;
 
     // Used to workaround an issue where the WM rotation happens before we are notified, allowing
@@ -127,7 +131,7 @@
     private final PipTouchState mTouchState;
     private final FlingAnimationUtils mFlingAnimationUtils;
     private final FloatingContentCoordinator mFloatingContentCoordinator;
-    private final PipMotionHelper mMotionHelper;
+    private PipMotionHelper mMotionHelper;
     private PipTouchGesture mGesture;
 
     // Temp vars
@@ -240,14 +244,15 @@
 
             mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
         }
+        mResizedBounds.setEmpty();
         mPipResizeGestureHandler.onActivityUnpinned();
     }
 
     public void onPinnedStackAnimationEnded() {
         // Always synchronize the motion helper bounds once PiP animations finish
         mMotionHelper.synchronizePinnedStackBounds();
-        mPipResizeGestureHandler.updateMiniSize(mMotionHelper.getBounds().width(),
-                mMotionHelper.getBounds().height());
+        updateMovementBounds();
+        mResizedBounds.set(mMotionHelper.getBounds());
 
         if (mShowPipMenuOnAnimationEnd) {
             mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
@@ -292,11 +297,13 @@
         Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
                 mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
         mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
-        mPipResizeGestureHandler.updateMaxSize(expandedSize.getWidth(), expandedSize.getHeight());
         Rect expandedMovementBounds = new Rect();
         mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
                 bottomOffset);
 
+        mPipResizeGestureHandler.updateMinSize(mNormalBounds.width(), mNormalBounds.height());
+        mPipResizeGestureHandler.updateMaxSize(mExpandedBounds.width(), mExpandedBounds.height());
+
         // The extra offset does not really affect the movement bounds, but are applied based on the
         // current state (ime showing, or shelf offset) when we need to actually shift
         int extraOffset = Math.max(
@@ -332,7 +339,7 @@
         mExpandedMovementBounds = expandedMovementBounds;
         mDisplayRotation = displayRotation;
         mInsetBounds.set(insetBounds);
-        updateMovementBounds(mMenuState);
+        updateMovementBounds();
         mMovementBoundsExtraOffsets = extraOffset;
 
         // If we have a deferred resize, apply it now
@@ -392,7 +399,7 @@
             case MotionEvent.ACTION_UP: {
                 // Update the movement bounds again if the state has changed since the user started
                 // dragging (ie. when the IME shows)
-                updateMovementBounds(mMenuState);
+                updateMovementBounds();
 
                 if (mGesture.onUp(mTouchState)) {
                     break;
@@ -490,9 +497,11 @@
         if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
             // Save the current snap fraction and if we do not drag or move the PiP, then
             // we store back to this snap fraction.  Otherwise, we'll reset the snap
-            // fraction and snap to the closest edge
-            Rect expandedBounds = new Rect(mExpandedBounds);
+            // fraction and snap to the closest edge.
+            // Also save the current resized bounds so when the menu disappears, we can restore it.
             if (resize) {
+                mResizedBounds.set(mMotionHelper.getBounds());
+                Rect expandedBounds = new Rect(mExpandedBounds);
                 mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
                         mMovementBounds, mExpandedMovementBounds);
             }
@@ -520,9 +529,12 @@
                 }
 
                 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
-                    Rect normalBounds = new Rect(mNormalBounds);
-                    mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
-                            mNormalMovementBounds, mMovementBounds, false /* immediate */);
+                    Rect restoreBounds = new Rect(mResizedBounds);
+                    Rect restoredMovementBounds = new Rect();
+                    mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds,
+                            restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
+                    mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
+                            restoredMovementBounds, mMovementBounds, false /* immediate */);
                     mSavedSnapFraction = -1f;
                 }
             } else {
@@ -533,7 +545,7 @@
             }
         }
         mMenuState = menuState;
-        updateMovementBounds(menuState);
+        updateMovementBounds();
         // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
         // as well, or it can't handle a11y focus and pip menu can't perform any action.
         onRegistrationChanged(menuState == MENU_STATE_NONE);
@@ -549,6 +561,21 @@
         return mMotionHelper;
     }
 
+    @VisibleForTesting
+    PipResizeGestureHandler getPipResizeGestureHandler() {
+        return mPipResizeGestureHandler;
+    }
+
+    @VisibleForTesting
+    void setPipResizeGestureHandler(PipResizeGestureHandler pipResizeGestureHandler) {
+        mPipResizeGestureHandler = pipResizeGestureHandler;
+    }
+
+    @VisibleForTesting
+    void setPipMotionHelper(PipMotionHelper pipMotionHelper) {
+        mMotionHelper = pipMotionHelper;
+    }
+
     /**
      * @return the unexpanded bounds.
      */
@@ -709,14 +736,14 @@
      * Updates the current movement bounds based on whether the menu is currently visible and
      * resized.
      */
-    private void updateMovementBounds(int menuState) {
-        boolean isMenuExpanded = menuState == MENU_STATE_FULL;
-        mMovementBounds = isMenuExpanded && willResizeMenu()
-                ? mExpandedMovementBounds
-                : mNormalMovementBounds;
-        mPipBoundsHandler.setMinEdgeSize(
-                isMenuExpanded ? mExpandedShortestEdgeSize : 0);
+    private void updateMovementBounds() {
+        mSnapAlgorithm.getMovementBounds(mMotionHelper.getBounds(), mInsetBounds,
+                mMovementBounds, mIsImeShowing ? mImeHeight : 0);
         mMotionHelper.setCurrentMovementBounds(mMovementBounds);
+
+        boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
+        mPipBoundsHandler.setMinEdgeSize(
+                isMenuExpanded  && willResizeMenu() ? mExpandedShortestEdgeSize : 0);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 3cf0718..ece1ce8b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -28,6 +28,7 @@
     void forceCollapsePanels();
     void openPanels();
     Context getContext();
+    Context getUserContext();
     QSLogger getQSLogger();
     Collection<QSTile> getTiles();
     void addCallback(Callback callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 837256b..d5e5b10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -55,11 +55,13 @@
      * @param context
      * @param parent
      * @param manager
+     * @param foregroundExecutor
      * @param backgroundExecutor
      */
     public QSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager,
-            Executor backgroundExecutor) {
-        super(context, parent, manager, R.layout.qs_media_panel, QS_ACTION_IDS, backgroundExecutor);
+            Executor foregroundExecutor, Executor backgroundExecutor) {
+        super(context, parent, manager, R.layout.qs_media_panel, QS_ACTION_IDS, foregroundExecutor,
+                backgroundExecutor);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index bf72b33..5ccf8c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -53,6 +53,7 @@
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
@@ -101,6 +102,7 @@
     private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>();
     private final NotificationMediaManager mNotificationMediaManager;
     private final LocalBluetoothManager mLocalBluetoothManager;
+    private final Executor mForegroundExecutor;
     private final Executor mBackgroundExecutor;
     private LocalMediaManager mLocalMediaManager;
     private MediaDevice mDevice;
@@ -160,6 +162,7 @@
             BroadcastDispatcher broadcastDispatcher,
             QSLogger qsLogger,
             NotificationMediaManager notificationMediaManager,
+            @Main Executor foregroundExecutor,
             @Background Executor backgroundExecutor,
             @Nullable LocalBluetoothManager localBluetoothManager
     ) {
@@ -168,6 +171,7 @@
         mQSLogger = qsLogger;
         mDumpManager = dumpManager;
         mNotificationMediaManager = notificationMediaManager;
+        mForegroundExecutor = foregroundExecutor;
         mBackgroundExecutor = backgroundExecutor;
         mLocalBluetoothManager = localBluetoothManager;
 
@@ -270,7 +274,7 @@
         if (player == null) {
             Log.d(TAG, "creating new player");
             player = new QSMediaPlayer(mContext, this, mNotificationMediaManager,
-                    mBackgroundExecutor);
+                    mForegroundExecutor, mBackgroundExecutor);
 
             if (player.isPlaying()) {
                 mMediaCarousel.addView(player.getView(), 0, lp); // add in front
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index fab7191..9e8eb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -45,7 +45,6 @@
 import com.android.systemui.qs.external.TileLifecycleManager;
 import com.android.systemui.qs.external.TileServices;
 import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -98,7 +97,7 @@
     @Inject
     public QSTileHost(Context context,
             StatusBarIconController iconController,
-            QSFactoryImpl defaultFactory,
+            QSFactory defaultFactory,
             @Main Handler mainHandler,
             @Background Looper bgLooper,
             PluginManager pluginManager,
@@ -120,7 +119,6 @@
         mServices = new TileServices(this, bgLooper, mBroadcastDispatcher);
         mStatusBarOptional = statusBarOptional;
 
-        defaultFactory.setHost(this);
         mQsFactories.add(defaultFactory);
         pluginManager.addPluginListener(this, QSFactory.class, true);
         mDumpManager.registerDumpable(TAG, this);
@@ -211,10 +209,12 @@
         return mContext;
     }
 
+    @Override
     public Context getUserContext() {
         return mUserContext;
     }
 
+    @Override
     public TileServices getTileServices() {
         return mServices;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index 4512afb..0c50194 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -48,12 +48,13 @@
      * @param context
      * @param parent
      * @param manager
+     * @param foregroundExecutor
      * @param backgroundExecutor
      */
     public QuickQSMediaPlayer(Context context, ViewGroup parent, NotificationMediaManager manager,
-            Executor backgroundExecutor) {
+            Executor foregroundExecutor, Executor backgroundExecutor) {
         super(context, parent, manager, R.layout.qqs_media_panel, QQS_ACTION_IDS,
-                backgroundExecutor);
+                foregroundExecutor, backgroundExecutor);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 6654b7a..be01d75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -32,6 +32,7 @@
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
@@ -79,11 +80,12 @@
             BroadcastDispatcher broadcastDispatcher,
             QSLogger qsLogger,
             NotificationMediaManager notificationMediaManager,
+            @Main Executor foregroundExecutor,
             @Background Executor backgroundExecutor,
             @Nullable LocalBluetoothManager localBluetoothManager
     ) {
         super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, notificationMediaManager,
-                backgroundExecutor, localBluetoothManager);
+                foregroundExecutor, backgroundExecutor, localBluetoothManager);
         if (mFooter != null) {
             removeView(mFooter.getView());
         }
@@ -103,7 +105,7 @@
 
             int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
             mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout,
-                    notificationMediaManager, backgroundExecutor);
+                    notificationMediaManager, foregroundExecutor, backgroundExecutor);
             LayoutParams lp2 = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
             lp2.setMarginEnd(marginSize);
             lp2.setMarginStart(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 3b27fb7..08c8f86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -46,7 +46,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
@@ -79,7 +79,7 @@
     private boolean mIsTokenGranted;
     private boolean mIsShowingDialog;
 
-    private CustomTile(QSTileHost host, String action, Context userContext) {
+    private CustomTile(QSHost host, String action, Context userContext) {
         super(host);
         mWindowManager = WindowManagerGlobal.getWindowManagerService();
         mComponent = ComponentName.unflattenFromString(action);
@@ -392,7 +392,7 @@
         return ComponentName.unflattenFromString(action);
     }
 
-    public static CustomTile create(QSTileHost host, String spec, Context userContext) {
+    public static CustomTile create(QSHost host, String spec, Context userContext) {
         if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
             throw new IllegalArgumentException("Bad custom tile spec: " + spec);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 1b8717b..c182a58 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -24,7 +24,7 @@
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.tiles.AirplaneModeTile;
 import com.android.systemui.qs.tiles.BatterySaverTile;
@@ -51,6 +51,8 @@
 import javax.inject.Provider;
 import javax.inject.Singleton;
 
+import dagger.Lazy;
+
 @Singleton
 public class QSFactoryImpl implements QSFactory {
 
@@ -77,10 +79,11 @@
     private final Provider<UiModeNightTile> mUiModeNightTileProvider;
     private final Provider<ScreenRecordTile> mScreenRecordTileProvider;
 
-    private QSTileHost mHost;
+    private final Lazy<QSHost> mQsHostLazy;
 
     @Inject
-    public QSFactoryImpl(Provider<WifiTile> wifiTileProvider,
+    public QSFactoryImpl(Lazy<QSHost> qsHostLazy,
+            Provider<WifiTile> wifiTileProvider,
             Provider<BluetoothTile> bluetoothTileProvider,
             Provider<CellularTile> cellularTileProvider,
             Provider<DndTile> dndTileProvider,
@@ -100,6 +103,7 @@
             Provider<GarbageMonitor.MemoryTile> memoryTileProvider,
             Provider<UiModeNightTile> uiModeNightTileProvider,
             Provider<ScreenRecordTile> screenRecordTileProvider) {
+        mQsHostLazy = qsHostLazy;
         mWifiTileProvider = wifiTileProvider;
         mBluetoothTileProvider = bluetoothTileProvider;
         mCellularTileProvider = cellularTileProvider;
@@ -122,10 +126,6 @@
         mScreenRecordTileProvider = screenRecordTileProvider;
     }
 
-    public void setHost(QSTileHost host) {
-        mHost = host;
-    }
-
     public QSTile createTile(String tileSpec) {
         QSTileImpl tile = createTileInternal(tileSpec);
         if (tile != null) {
@@ -179,7 +179,8 @@
 
         // Custom tiles
         if (tileSpec.startsWith(CustomTile.PREFIX)) {
-            return CustomTile.create(mHost, tileSpec, mHost.getUserContext());
+            return CustomTile.create(mQsHostLazy.get(), tileSpec,
+                    mQsHostLazy.get().getUserContext());
         }
 
         // Debug tiles.
@@ -196,7 +197,7 @@
 
     @Override
     public QSTileView createTileView(QSTile tile, boolean collapsedView) {
-        Context context = new ContextThemeWrapper(mHost.getContext(), R.style.qs_theme);
+        Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme);
         QSIconView icon = tile.createTileView(context);
         if (collapsedView) {
             return new QSTileBaseView(context, icon, collapsedView);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 7c770f4..1780fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -347,6 +347,7 @@
     void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
             Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
         // TODO use taskId and visibleInsets
+        clearScreenshot("new screenshot requested");
         takeScreenshot(screenshot, finisher, screenshotScreenBounds);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index ea5ec05..d7eab3a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -31,7 +31,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.provider.Settings;
-import android.util.Log;
 import android.util.Slog;
 import android.view.IWindowContainer;
 import android.view.LayoutInflater;
@@ -73,7 +72,7 @@
         DisplayController.OnDisplaysChangedListener {
     private static final String TAG = "Divider";
 
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
 
     static final int DEFAULT_APP_TRANSITION_DURATION = 336;
     static final float ADJUSTED_NONFOCUS_DIM = 0.3f;
@@ -173,6 +172,9 @@
         @Nullable
         private ValueAnimator mAnimation = null;
 
+        private boolean mPaused = true;
+        private boolean mPausedTargetAdjusted = false;
+
         private boolean getSecondaryHasFocus(int displayId) {
             try {
                 IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController()
@@ -185,6 +187,14 @@
             return false;
         }
 
+        private void updateDimTargets() {
+            final boolean splitIsVisible = !mView.isHidden();
+            mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible)
+                    ? ADJUSTED_NONFOCUS_DIM : 0.f;
+            mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible)
+                    ? ADJUSTED_NONFOCUS_DIM : 0.f;
+        }
+
         @Override
         public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
                 boolean imeShouldShow, SurfaceControl.Transaction t) {
@@ -193,18 +203,31 @@
             }
             final boolean splitIsVisible = !mView.isHidden();
             mSecondaryHasFocus = getSecondaryHasFocus(displayId);
-            mTargetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
+            final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
                     && !mSplitLayout.mDisplayLayout.isLandscape();
             mHiddenTop = hiddenTop;
             mShownTop = shownTop;
             mTargetShown = imeShouldShow;
             if (mLastAdjustTop < 0) {
                 mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
+            } else {
+                // Check for an "interruption" of an existing animation. In this case, we need to
+                // fake-flip the last-known state direction so that the animation completes in the
+                // other direction.
+                if (mTargetAdjusted != targetAdjusted && targetAdjusted == mAdjusted) {
+                    if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) {
+                        mAdjusted = mTargetAdjusted;
+                    }
+                }
             }
-            mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible)
-                    ? ADJUSTED_NONFOCUS_DIM : 0.f;
-            mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible)
-                    ? ADJUSTED_NONFOCUS_DIM : 0.f;
+            if (mPaused) {
+                mPausedTargetAdjusted = targetAdjusted;
+                if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState());
+                return;
+            }
+            mTargetAdjusted = targetAdjusted;
+            updateDimTargets();
+            if (DEBUG) Slog.d(TAG, " ime starting. vis:" + splitIsVisible + "  " + dumpState());
             if (mAnimation != null || (mImeWasShown && imeShouldShow
                     && mTargetAdjusted != mAdjusted)) {
                 // We need to animate adjustment independently of the IME position, so
@@ -259,7 +282,7 @@
         @Override
         public void onImePositionChanged(int displayId, int imeTop,
                 SurfaceControl.Transaction t) {
-            if (mAnimation != null || !inSplitMode()) {
+            if (mAnimation != null || !inSplitMode() || mPaused) {
                 // Not synchronized with IME anymore, so return.
                 return;
             }
@@ -271,7 +294,7 @@
         @Override
         public void onImeEndPositioning(int displayId, boolean cancelled,
                 SurfaceControl.Transaction t) {
-            if (mAnimation != null || !inSplitMode()) {
+            if (mAnimation != null || !inSplitMode() || mPaused) {
                 // Not synchronized with IME anymore, so return.
                 return;
             }
@@ -279,7 +302,7 @@
         }
 
         private void onProgress(float progress, SurfaceControl.Transaction t) {
-            if (mTargetAdjusted != mAdjusted) {
+            if (mTargetAdjusted != mAdjusted && !mPaused) {
                 final float fraction = mTargetAdjusted ? progress : 1.f - progress;
                 mLastAdjustTop = (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop);
                 mSplitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop);
@@ -342,6 +365,52 @@
             });
             mAnimation.start();
         }
+
+        private String dumpState() {
+            return "top:" + mHiddenTop + "->" + mShownTop
+                    + " adj:" + mAdjusted + "->" + mTargetAdjusted + "(" + mLastAdjustTop + ")"
+                    + " shw:" + mImeWasShown + "->" + mTargetShown
+                    + " dims:" + mLastPrimaryDim + "," + mLastSecondaryDim
+                    + "->" + mTargetPrimaryDim + "," + mTargetSecondaryDim
+                    + " shf:" + mSecondaryHasFocus + " desync:" + (mAnimation != null)
+                    + " paus:" + mPaused + "[" + mPausedTargetAdjusted + "]";
+        }
+
+        /** Completely aborts/resets adjustment state */
+        public void pause(int displayId) {
+            if (DEBUG) Slog.d(TAG, "ime pause posting " + dumpState());
+            mHandler.post(() -> {
+                if (DEBUG) Slog.d(TAG, "ime pause run posted " + dumpState());
+                if (mPaused) {
+                    return;
+                }
+                mPaused = true;
+                mPausedTargetAdjusted = mTargetAdjusted;
+                mTargetAdjusted = false;
+                mTargetPrimaryDim = mTargetSecondaryDim = 0.f;
+                updateImeAdjustState();
+                startAsyncAnimation();
+            });
+        }
+
+        public void resume(int displayId) {
+            if (DEBUG) Slog.d(TAG, "ime resume posting " + dumpState());
+            mHandler.post(() -> {
+                if (DEBUG) Slog.d(TAG, "ime resume run posted " + dumpState());
+                if (!mPaused) {
+                    return;
+                }
+                mPaused = false;
+                mTargetAdjusted = mPausedTargetAdjusted;
+                updateDimTargets();
+                if ((mTargetAdjusted != mAdjusted) && !mMinimized && mView != null) {
+                    // End unminimize animations since they conflict with adjustment animations.
+                    mView.finishAnimations();
+                }
+                updateImeAdjustState();
+                startAsyncAnimation();
+            });
+        }
     }
     private final DividerImeController mImePositionProcessor = new DividerImeController();
 
@@ -493,6 +562,7 @@
     }
 
     void updateVisibility(final boolean visible) {
+        if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
         if (mVisible != visible) {
             mVisible = visible;
             mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
@@ -519,12 +589,21 @@
 
     /** Switch to minimized state if appropriate */
     public void setMinimized(final boolean minimized) {
+        if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
         mHandler.post(() -> {
+            if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
+            if (!mVisible) {
+                return;
+            }
             setHomeMinimized(minimized, mHomeStackResizable);
         });
     }
 
     private void setHomeMinimized(final boolean minimized, boolean homeStackResizable) {
+        if (DEBUG) {
+            Slog.d(TAG, "setHomeMinimized  min:" + mMinimized + "->" + minimized + " hrsz:"
+                    + mHomeStackResizable + "->" + homeStackResizable + " split:" + inSplitMode());
+        }
         WindowContainerTransaction wct = new WindowContainerTransaction();
         // Update minimized state
         if (mMinimized != minimized) {
@@ -544,7 +623,17 @@
 
         // Sync state to DividerView if it exists.
         if (mView != null) {
+            final int displayId = mView.getDisplay() != null
+                    ? mView.getDisplay().getDisplayId() : DEFAULT_DISPLAY;
+            // pause ime here (before updateMinimizedDockedStack)
+            if (mMinimized) {
+                mImePositionProcessor.pause(displayId);
+            }
             mView.setMinimizedDockStack(minimized, getAnimDuration(), homeStackResizable);
+            if (!mMinimized) {
+                // afterwards so it can end any animations started in view
+                mImePositionProcessor.resume(displayId);
+            }
         }
         updateTouchable();
         WindowManagerProxy.applyContainerTransaction(wct);
@@ -649,7 +738,7 @@
         if (!inSplitMode()) {
             // Wasn't in split-mode yet, so enter now.
             if (DEBUG) {
-                Log.d(TAG, " entering split mode with minimized=true");
+                Slog.d(TAG, " entering split mode with minimized=true");
             }
             updateVisibility(true /* visible */);
         }
@@ -660,7 +749,7 @@
         if (!inSplitMode()) {
             // Wasn't in split-mode, so enter now.
             if (DEBUG) {
-                Log.d(TAG, " enter split mode unminimized ");
+                Slog.d(TAG, " enter split mode unminimized ");
             }
             updateVisibility(true /* visible */);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 4114bb9..131f4e1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -34,6 +34,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
+import android.util.Slog;
 import android.view.Choreographer;
 import android.view.Display;
 import android.view.InsetsState;
@@ -67,12 +68,15 @@
 import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
+import java.util.function.Consumer;
+
 /**
  * Docked stack divider.
  */
 public class DividerView extends FrameLayout implements OnTouchListener,
         OnComputeInternalInsetsListener {
     private static final String TAG = "DividerView";
+    private static final boolean DEBUG = Divider.DEBUG;
 
     public interface DividerCallbacks {
         void onDraggingStart();
@@ -627,6 +631,7 @@
             cancelFlingAnimation();
             updateDockSide();
         }
+        if (DEBUG) Slog.d(TAG, "Getting fling " + position + "->" + snapTarget.position);
         final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE;
         ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
         anim.addUpdateListener(animation -> resizeStackDelayed((int) animation.getAnimatedValue(),
@@ -634,16 +639,21 @@
                         ? TASK_POSITION_SAME
                         : snapTarget.taskPosition,
                 snapTarget));
-        Runnable endAction = () -> {
+        Consumer<Boolean> endAction = cancelled -> {
+            if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction);
+            final boolean wasMinimizeInteraction = mIsInMinimizeInteraction;
+            // Reset minimized divider position after unminimized state animation finishes.
+            if (!cancelled && !mDockedStackMinimized && mIsInMinimizeInteraction) {
+                mIsInMinimizeInteraction = false;
+            }
             boolean dismissed = commitSnapFlags(snapTarget);
             mWindowManagerProxy.setResizing(false);
             updateDockSide();
             mCurrentAnimator = null;
             mEntranceAnimationRunning = false;
             mExitAnimationRunning = false;
-            if (!dismissed) {
-                WindowManagerProxy.applyResizeSplits((mIsInMinimizeInteraction
-                        ? mSnapTargetBeforeMinimized : snapTarget).position, mSplitLayout);
+            if (!dismissed && !wasMinimizeInteraction) {
+                WindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout);
             }
             if (mCallback != null) {
                 mCallback.onDraggingEnd();
@@ -667,12 +677,6 @@
                 }
             }
         };
-        Runnable notCancelledEndAction = () -> {
-            // Reset minimized divider position after unminimized state animation finishes
-            if (!mDockedStackMinimized && mIsInMinimizeInteraction) {
-                mIsInMinimizeInteraction = false;
-            }
-        };
         anim.addListener(new AnimatorListenerAdapter() {
 
             private boolean mCancelled;
@@ -694,15 +698,11 @@
                     delay = mSfChoreographer.getSurfaceFlingerOffsetMs();
                 }
                 if (delay == 0) {
-                    if (!mCancelled) {
-                        notCancelledEndAction.run();
-                    }
-                    endAction.run();
+                    endAction.accept(mCancelled);
                 } else {
-                    if (!mCancelled) {
-                        mHandler.postDelayed(notCancelledEndAction, delay);
-                    }
-                    mHandler.postDelayed(endAction, delay);
+                    final Boolean cancelled = mCancelled;
+                    if (DEBUG) Slog.d(TAG, "Posting endFling " + cancelled + " d:" + delay + "ms");
+                    mHandler.postDelayed(() -> endAction.accept(cancelled), delay);
                 }
             }
         });
@@ -899,6 +899,7 @@
 
     public void setMinimizedDockStack(boolean minimized, long animDuration,
             boolean isHomeStackResizable) {
+        if (DEBUG) Slog.d(TAG, "setMinDock: " + mDockedStackMinimized + "->" + minimized);
         mHomeStackResizable = isHomeStackResizable;
         updateDockSide();
         if (!isHomeStackResizable) {
@@ -946,6 +947,15 @@
                 .start();
     }
 
+    // Needed to end any currently playing animations when they might compete with other anims
+    // (specifically, IME adjust animation immediately after leaving minimized). Someday maybe
+    // these can be unified, but not today.
+    void finishAnimations() {
+        if (mCurrentAnimator != null) {
+            mCurrentAnimator.end();
+        }
+    }
+
     public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
         if (mAdjustedForIme == adjustedForIme) {
             return;
@@ -1061,6 +1071,11 @@
         mDividerPositionX = dockedRect.right;
         mDividerPositionY = dockedRect.bottom;
 
+        if (DEBUG) {
+            Slog.d(TAG, "Resizing split surfaces: " + dockedRect + " " + dockedTaskRect
+                    + " " + otherRect + " " + otherTaskRect);
+        }
+
         t.setPosition(mTiles.mPrimarySurface, dockedTaskRect.left, dockedTaskRect.top);
         Rect crop = new Rect(dockedRect);
         crop.offsetTo(-Math.min(dockedTaskRect.left - dockedRect.left, 0),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index 4597b16..b33424c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -33,6 +33,7 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry.OnSensitivityChangedListener;
 
 import java.util.List;
 
@@ -159,20 +160,30 @@
     }
 
     public void setEntry(NotificationEntry entry) {
-        if (entry != null) {
-            mShowingEntry = entry;
+        if (mShowingEntry != null) {
+            mShowingEntry.removeOnSensitivityChangedListener(mOnSensitivityChangedListener);
+        }
+        mShowingEntry = entry;
+
+        if (mShowingEntry != null) {
             CharSequence text = entry.headsUpStatusBarText;
             if (entry.isSensitive()) {
                 text = entry.headsUpStatusBarTextPublic;
             }
             mTextView.setText(text);
-            mShowingEntry.setOnSensitiveChangedListener(() -> setEntry(entry));
-        } else if (mShowingEntry != null){
-            mShowingEntry.setOnSensitiveChangedListener(null);
-            mShowingEntry = null;
+            mShowingEntry.addOnSensitivityChangedListener(mOnSensitivityChangedListener);
         }
     }
 
+    private final OnSensitivityChangedListener mOnSensitivityChangedListener = entry -> {
+        if (entry != mShowingEntry) {
+            throw new IllegalStateException("Got a sensitivity change for " + entry
+                    + " but mShowingEntry is " + mShowingEntry);
+        }
+        // Update the text
+        setEntry(entry);
+    };
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 047edd2..72d9d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -206,7 +206,8 @@
                     false,
                     false,
                     false,
-                    null
+                    null,
+                    false
             );
         }
         return ranking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 87be739..d8fdf92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -70,6 +70,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -262,11 +263,11 @@
         synchronized (mEntryManager) {
             NotificationEntry entry = mEntryManager
                     .getActiveNotificationUnfiltered(mMediaNotificationKey);
-            if (entry == null || entry.expandedIcon == null) {
+            if (entry == null || entry.getIcons().getShelfIcon() == null) {
                 return null;
             }
 
-            return entry.expandedIcon.getSourceIcon();
+            return entry.getIcons().getShelfIcon().getSourceIcon();
         }
     }
 
@@ -284,8 +285,7 @@
         boolean metaDataChanged = false;
 
         synchronized (mEntryManager) {
-            Set<NotificationEntry> allNotifications =
-                    mEntryManager.getPendingAndActiveNotifications();
+            Collection<NotificationEntry> allNotifications = mEntryManager.getAllNotifs();
 
             // Promote the media notification with a controller in 'playing' state, if any.
             NotificationEntry mediaNotification = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index eb8526d..8945f36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -22,6 +22,7 @@
 import android.app.WallpaperManager
 import android.view.Choreographer
 import android.view.View
+import androidx.annotation.VisibleForTesting
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.dynamicanimation.animation.SpringForce
@@ -29,8 +30,10 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.Interpolators
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController
 import com.android.systemui.statusbar.phone.PanelExpansionListener
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import java.io.FileDescriptor
@@ -44,17 +47,17 @@
  */
 @Singleton
 class NotificationShadeDepthController @Inject constructor(
-    private val statusBarStateController: SysuiStatusBarStateController,
+    private val statusBarStateController: StatusBarStateController,
     private val blurUtils: BlurUtils,
     private val biometricUnlockController: BiometricUnlockController,
     private val keyguardStateController: KeyguardStateController,
     private val choreographer: Choreographer,
     private val wallpaperManager: WallpaperManager,
+    private val notificationShadeWindowController: NotificationShadeWindowController,
     dumpManager: DumpManager
 ) : PanelExpansionListener, Dumpable {
     companion object {
         private const val WAKE_UP_ANIMATION_ENABLED = true
-        private const val SHADE_BLUR_ENABLED = true
     }
 
     lateinit var root: View
@@ -62,8 +65,9 @@
     private var keyguardAnimator: Animator? = null
     private var notificationAnimator: Animator? = null
     private var updateScheduled: Boolean = false
-    private var shadeExpansion = 1.0f
-    private val shadeSpring = SpringAnimation(this, object :
+    private var shadeExpansion = 0f
+    @VisibleForTesting
+    var shadeSpring = SpringAnimation(this, object :
             FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
         override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
             shadeBlurRadius = value.toInt()
@@ -74,12 +78,25 @@
         }
     })
     private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE
+
+    /**
+     * Radius that we're animating to.
+     */
+    private var pendingShadeBlurRadius = -1
+
+    /**
+     * Shade blur radius on the current frame.
+     */
     private var shadeBlurRadius = 0
         set(value) {
             if (field == value) return
             field = value
             scheduleUpdate()
         }
+
+    /**
+     * Blur radius of the wake-up animation on this frame.
+     */
     private var wakeAndUnlockBlurRadius = 0
         set(value) {
             if (field == value) return
@@ -100,6 +117,7 @@
         val rawZoom = max(blurUtils.ratioOfBlurRadius(blur), globalDialogVisibility)
         wallpaperManager.setWallpaperZoomOut(root.windowToken,
                 zoomInterpolator.getInterpolation(rawZoom))
+        notificationShadeWindowController.setBackgroundBlurRadius(blur)
     }
 
     /**
@@ -139,6 +157,18 @@
         }
     }
 
+    private val statusBarStateCallback = object : StatusBarStateController.StateListener {
+        override fun onStateChanged(newState: Int) {
+            updateShadeBlur()
+        }
+
+        override fun onDozingChanged(isDozing: Boolean) {
+            if (isDozing && shadeSpring.isRunning) {
+                shadeSpring.skipToEnd()
+            }
+        }
+    }
+
     init {
         dumpManager.registerDumpable(javaClass.name, this)
         if (WAKE_UP_ANIMATION_ENABLED) {
@@ -147,24 +177,31 @@
         shadeSpring.spring = SpringForce(0.0f)
         shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
         shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
+        shadeSpring.addEndListener { _, _, _, _ -> pendingShadeBlurRadius = -1 }
+        statusBarStateController.addCallback(statusBarStateCallback)
     }
 
     /**
      * Update blurs when pulling down the shade
      */
     override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
-        if (!SHADE_BLUR_ENABLED) {
+        if (expansion == shadeExpansion) {
             return
         }
+        shadeExpansion = expansion
+        updateShadeBlur()
+    }
 
+    private fun updateShadeBlur() {
         var newBlur = 0
         if (statusBarStateController.state == StatusBarState.SHADE) {
-            newBlur = blurUtils.blurRadiusOfRatio(expansion)
+            newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
         }
 
-        if (shadeBlurRadius == newBlur) {
+        if (pendingShadeBlurRadius == newBlur) {
             return
         }
+        pendingShadeBlurRadius = newBlur
         shadeSpring.animateToFinalPosition(newBlur.toFloat())
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 1a8454c..d7f2ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -329,7 +329,7 @@
                     expandableRow.setAboveShelf(false);
                 }
                 if (notGoneIndex == 0) {
-                    StatusBarIconView icon = expandableRow.getEntry().expandedIcon;
+                    StatusBarIconView icon = expandableRow.getEntry().getIcons().getShelfIcon();
                     NotificationIconContainer.IconState iconState = getIconState(icon);
                     // The icon state might be null in rare cases where the notification is actually
                     // added to the layout, but not to the shelf. An example are replied messages,
@@ -432,7 +432,7 @@
             // if the shelf is clipped, lets make sure we also clip the icon
             maxTop = Math.max(maxTop, getTranslationY() + getClipTopAmount());
         }
-        StatusBarIconView icon = row.getEntry().expandedIcon;
+        StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon();
         float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
         if (shelfIconPosition < maxTop && !mAmbientState.isFullyHidden()) {
             int top = (int) (maxTop - shelfIconPosition);
@@ -444,7 +444,7 @@
     }
 
     private void updateContinuousClipping(final ExpandableNotificationRow row) {
-        StatusBarIconView icon = row.getEntry().expandedIcon;
+        StatusBarIconView icon = row.getEntry().getIcons().getShelfIcon();
         boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDozing();
         boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
         if (needsContinuousClipping && !isContinuousClipping) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 7f0479c..c6d84ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -29,6 +29,7 @@
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -56,9 +57,9 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -105,6 +106,10 @@
      */
     public static final int UNDEFINED_DISMISS_REASON = 0;
 
+    private final Set<NotificationEntry> mAllNotifications = new ArraySet<>();
+    private final Set<NotificationEntry> mReadOnlyAllNotifications =
+            Collections.unmodifiableSet(mAllNotifications);
+
     /** Pending notifications are ones awaiting inflation */
     @VisibleForTesting
     protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
@@ -468,6 +473,8 @@
                     entry.removeRow();
                 }
 
+                mAllNotifications.remove(entry);
+
                 // Let's remove the children if this was a summary
                 handleGroupSummaryRemoved(key);
                 removeVisibleNotification(key);
@@ -548,6 +555,7 @@
                 notification,
                 ranking,
                 mFgsFeatureController.isForegroundServiceDismissalEnabled());
+        mAllNotifications.add(entry);
 
         mLeakDetector.trackInstance(entry);
 
@@ -709,15 +717,6 @@
     }
 
     /**
-     * @return all notifications we're currently aware of (both pending and active notifications)
-     */
-    public Set<NotificationEntry> getPendingAndActiveNotifications() {
-        Set<NotificationEntry> allNotifs = new HashSet<>(mPendingNotifications.values());
-        allNotifs.addAll(mSortedAndFiltered);
-        return allNotifs;
-    }
-
-    /**
      * Use this method to retrieve a notification entry that has been prepared for presentation.
      * Note that the notification may be filtered out and never shown to the user.
      *
@@ -842,7 +841,7 @@
 
     private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
         pw.print(indent);
-        pw.println("  [" + i + "] key=" + e.getKey() + " icon=" + e.icon);
+        pw.println("  [" + i + "] key=" + e.getKey() + " icon=" + e.getIcons().getStatusBarIcon());
         StatusBarNotification n = e.getSbn();
         pw.print(indent);
         pw.println("      pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
@@ -861,6 +860,15 @@
         return mReadOnlyNotifications;
     }
 
+    /**
+     * Returns a collections containing ALL notifications we know about, including ones that are
+     * hidden or for other users. See {@link CommonNotifCollection#getAllNotifs()}.
+     */
+    @Override
+    public Collection<NotificationEntry> getAllNotifs() {
+        return mReadOnlyAllNotifications;
+    }
+
     /** @return A count of the active notifications */
     public int getActiveNotificationsCount() {
         return mReadOnlyNotifications.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index fabe3a7..b357ada 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -45,7 +45,6 @@
     private final ArrayList<Callback> mCallbacks =  new ArrayList<>();
     private final Handler mHandler;
 
-    private NotificationPresenter mPresenter;
     private boolean mPanelExpanded;
     private boolean mScreenOn;
     private boolean mReorderingAllowed;
@@ -80,7 +79,6 @@
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter) {
-        mPresenter = presenter;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index b960b42..2c747bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -51,6 +52,7 @@
         return mSummary;
     }
 
+    @NonNull
     public List<NotificationEntry> getChildren() {
         return mUnmodifiableChildren;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 749e5e2..b90cfa8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -64,24 +64,34 @@
 import com.android.systemui.statusbar.notification.collection.coalescer.CoalescedEvent;
 import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
 import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CleanUpEntryEvent;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.EntryAddedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.EntryRemovedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.EntryUpdatedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.InitEntryEvent;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifEvent;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.notifcollection.RankingAppliedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.RankingUpdatedEvent;
 import com.android.systemui.util.Assert;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Queue;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -124,6 +134,8 @@
     private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
     private final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
 
+    private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
+
     private boolean mAttached = false;
     private boolean mAmDispatchingToOtherCode;
 
@@ -160,8 +172,8 @@
         mBuildListener = buildListener;
     }
 
-    /** @see NotifPipeline#getActiveNotifs() */
-    Collection<NotificationEntry> getActiveNotifs() {
+    /** @see NotifPipeline#getAllNotifs() */
+    Collection<NotificationEntry> getAllNotifs() {
         Assert.isMainThread();
         return mReadOnlyNotificationSet;
     }
@@ -242,7 +254,7 @@
         }
 
         locallyDismissNotifications(entriesToLocallyDismiss);
-        rebuildList();
+        dispatchEventsAndRebuildList();
     }
 
     /**
@@ -251,8 +263,7 @@
     public void dismissNotification(
             NotificationEntry entry,
             @NonNull DismissedByUserStats stats) {
-        dismissNotifications(List.of(
-                new Pair<NotificationEntry, DismissedByUserStats>(entry, stats)));
+        dismissNotifications(List.of(new Pair<>(entry, stats)));
     }
 
     /**
@@ -268,7 +279,7 @@
             // system process is dead if we're here.
         }
 
-        final List<NotificationEntry> entries = new ArrayList(getActiveNotifs());
+        final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs());
         for (int i = entries.size() - 1; i >= 0; i--) {
             NotificationEntry entry = entries.get(i);
             if (!shouldDismissOnClearAll(entry, userId)) {
@@ -283,7 +294,7 @@
         }
 
         locallyDismissNotifications(entries);
-        rebuildList();
+        dispatchEventsAndRebuildList();
     }
 
     /**
@@ -326,8 +337,9 @@
     private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
         Assert.isMainThread();
 
-        postNotification(sbn, requireRanking(rankingMap, sbn.getKey()), rankingMap);
-        rebuildList();
+        postNotification(sbn, requireRanking(rankingMap, sbn.getKey()));
+        applyRanking(rankingMap);
+        dispatchEventsAndRebuildList();
     }
 
     private void onNotificationGroupPosted(List<CoalescedEvent> batch) {
@@ -336,9 +348,9 @@
         mLogger.logNotifGroupPosted(batch.get(0).getSbn().getGroupKey(), batch.size());
 
         for (CoalescedEvent event : batch) {
-            postNotification(event.getSbn(), event.getRanking(), null);
+            postNotification(event.getSbn(), event.getRanking());
         }
-        rebuildList();
+        dispatchEventsAndRebuildList();
     }
 
     private void onNotificationRemoved(
@@ -354,55 +366,49 @@
             throw new IllegalStateException("No notification to remove with key " + sbn.getKey());
         }
         entry.mCancellationReason = reason;
-        applyRanking(rankingMap);
         tryRemoveNotification(entry);
-        rebuildList();
+        applyRanking(rankingMap);
+        dispatchEventsAndRebuildList();
     }
 
     private void onNotificationRankingUpdate(RankingMap rankingMap) {
         Assert.isMainThread();
+        mEventQueue.add(new RankingUpdatedEvent(rankingMap));
         applyRanking(rankingMap);
-        dispatchNotificationRankingUpdate(rankingMap);
-        rebuildList();
+        dispatchEventsAndRebuildList();
     }
 
     private void postNotification(
             StatusBarNotification sbn,
-            Ranking ranking,
-            @Nullable RankingMap rankingMap) {
+            Ranking ranking) {
         NotificationEntry entry = mNotificationSet.get(sbn.getKey());
 
         if (entry == null) {
             // A new notification!
-            mLogger.logNotifPosted(sbn.getKey());
-
             entry = new NotificationEntry(sbn, ranking);
             mNotificationSet.put(sbn.getKey(), entry);
-            dispatchOnEntryInit(entry);
 
-            if (rankingMap != null) {
-                applyRanking(rankingMap);
-            }
-
-            dispatchOnEntryAdded(entry);
+            mLogger.logNotifPosted(sbn.getKey());
+            mEventQueue.add(new InitEntryEvent(entry));
+            mEventQueue.add(new EntryAddedEvent(entry));
 
         } else {
             // Update to an existing entry
-            mLogger.logNotifUpdated(sbn.getKey());
 
             // Notification is updated so it is essentially re-added and thus alive again, so we
             // can reset its state.
+            // TODO: If a coalesced event ever gets here, it's possible to lose track of children,
+            //  since their rankings might have been updated earlier (and thus we may no longer
+            //  think a child is associated with this locally-dismissed entry).
             cancelLocalDismissal(entry);
             cancelLifetimeExtension(entry);
             cancelDismissInterception(entry);
             entry.mCancellationReason = REASON_NOT_CANCELED;
 
             entry.setSbn(sbn);
-            if (rankingMap != null) {
-                applyRanking(rankingMap);
-            }
 
-            dispatchOnEntryUpdated(entry);
+            mLogger.logNotifUpdated(sbn.getKey());
+            mEventQueue.add(new EntryUpdatedEvent(entry));
         }
     }
 
@@ -432,8 +438,8 @@
         if (!isLifetimeExtended(entry)) {
             mNotificationSet.remove(entry.getKey());
             cancelDismissInterception(entry);
-            dispatchOnEntryRemoved(entry, entry.mCancellationReason);
-            dispatchOnEntryCleanUp(entry);
+            mEventQueue.add(new EntryRemovedEvent(entry, entry.mCancellationReason));
+            mEventQueue.add(new CleanUpEntryEvent(entry));
             return true;
         } else {
             return false;
@@ -466,9 +472,16 @@
                 }
             }
         }
+        mEventQueue.add(new RankingAppliedEvent());
     }
 
-    private void rebuildList() {
+    private void dispatchEventsAndRebuildList() {
+        mAmDispatchingToOtherCode = true;
+        while (!mEventQueue.isEmpty()) {
+            mEventQueue.remove().dispatchTo(mNotifCollectionListeners);
+        }
+        mAmDispatchingToOtherCode = false;
+
         if (mBuildListener != null) {
             mBuildListener.onBuildList(mReadOnlyNotificationSet);
         }
@@ -491,7 +504,7 @@
 
         if (!isLifetimeExtended(entry)) {
             if (tryRemoveNotification(entry)) {
-                rebuildList();
+                dispatchEventsAndRebuildList();
             }
         }
     }
@@ -660,57 +673,9 @@
                 || entry.getSbn().getUser().getIdentifier() == userId;
     }
 
-    private void dispatchOnEntryInit(NotificationEntry entry) {
-        mAmDispatchingToOtherCode = true;
-        for (NotifCollectionListener listener : mNotifCollectionListeners) {
-            listener.onEntryInit(entry);
-        }
-        mAmDispatchingToOtherCode = false;
-    }
-
-    private void dispatchOnEntryAdded(NotificationEntry entry) {
-        mAmDispatchingToOtherCode = true;
-        for (NotifCollectionListener listener : mNotifCollectionListeners) {
-            listener.onEntryAdded(entry);
-        }
-        mAmDispatchingToOtherCode = false;
-    }
-
-    private void dispatchOnEntryUpdated(NotificationEntry entry) {
-        mAmDispatchingToOtherCode = true;
-        for (NotifCollectionListener listener : mNotifCollectionListeners) {
-            listener.onEntryUpdated(entry);
-        }
-        mAmDispatchingToOtherCode = false;
-    }
-
-    private void dispatchNotificationRankingUpdate(RankingMap map) {
-        mAmDispatchingToOtherCode = true;
-        for (NotifCollectionListener listener : mNotifCollectionListeners) {
-            listener.onRankingUpdate(map);
-        }
-        mAmDispatchingToOtherCode = false;
-    }
-
-    private void dispatchOnEntryRemoved(NotificationEntry entry, @CancellationReason int reason) {
-        mAmDispatchingToOtherCode = true;
-        for (NotifCollectionListener listener : mNotifCollectionListeners) {
-            listener.onEntryRemoved(entry, reason);
-        }
-        mAmDispatchingToOtherCode = false;
-    }
-
-    private void dispatchOnEntryCleanUp(NotificationEntry entry) {
-        mAmDispatchingToOtherCode = true;
-        for (NotifCollectionListener listener : mNotifCollectionListeners) {
-            listener.onEntryCleanUp(entry);
-        }
-        mAmDispatchingToOtherCode = false;
-    }
-
     @Override
     public void dump(@NonNull FileDescriptor fd, PrintWriter pw, @NonNull String[] args) {
-        final List<NotificationEntry> entries = new ArrayList<>(getActiveNotifs());
+        final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs());
 
         pw.println("\t" + TAG + " unsorted/unfiltered notifications:");
         if (entries.size() == 0) {
@@ -754,6 +719,7 @@
     private static final String TAG = "NotifCollection";
 
     @IntDef(prefix = { "REASON_" }, value = {
+            REASON_NOT_CANCELED,
             REASON_UNKNOWN,
             REASON_CLICK,
             REASON_CANCEL_ALL,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
index 14903cd..17899e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -82,14 +82,14 @@
     }
 
     /**
-     * Returns the list of "active" notifications, i.e. the notifications that are currently posted
+     * Returns the list of all known notifications, i.e. the notifications that are currently posted
      * to the phone. In general, this tracks closely to the list maintained by NotificationManager,
      * but it can diverge slightly due to lifetime extenders.
      *
      * The returned collection is read-only, unsorted, unfiltered, and ungrouped.
      */
-    public Collection<NotificationEntry> getActiveNotifs() {
-        return mNotifCollection.getActiveNotifs();
+    public Collection<NotificationEntry> getAllNotifs() {
+        return mNotifCollection.getAllNotifs();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt
new file mode 100644
index 0000000..e7948cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewBarn.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.view.textclassifier.Log
+import com.android.systemui.statusbar.notification.stack.NotificationListItem
+import java.lang.IllegalStateException
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * The ViewBarn is just a map from [ListEntry] to an instance of [NotificationListItem] which is
+ * usually just an [ExpandableNotificationRow]
+ */
+@Singleton
+class NotifViewBarn @Inject constructor() {
+    private val DEBUG = false
+
+    private val rowMap = mutableMapOf<String, NotificationListItem>()
+
+    fun requireView(forEntry: ListEntry): NotificationListItem {
+        if (DEBUG) {
+            Log.d(TAG, "requireView: $forEntry.key")
+        }
+        val li = rowMap[forEntry.key]
+        if (li == null) {
+            throw IllegalStateException("No view has been registered for entry: $forEntry")
+        }
+
+        return li
+    }
+
+    fun registerViewForEntry(entry: ListEntry, view: NotificationListItem) {
+        if (DEBUG) {
+            Log.d(TAG, "registerViewForEntry: $entry.key")
+        }
+        rowMap[entry.key] = view
+    }
+
+    fun removeViewForEntry(entry: ListEntry) {
+        if (DEBUG) {
+            Log.d(TAG, "removeViewForEntry: $entry.key")
+        }
+        rowMap.remove(entry.key)
+    }
+}
+
+private const val TAG = "NotifViewBarn"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
new file mode 100644
index 0000000..0437877
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.annotation.MainThread
+import android.view.ViewGroup
+
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.notification.VisualStabilityManager
+import com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY
+import com.android.systemui.statusbar.notification.stack.NotificationListItem
+import com.android.systemui.util.Assert
+
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.IllegalStateException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * A consumer of a Notification tree built by [ShadeListBuilder] which will update the notification
+ * presenter with the minimum operations required to make the old tree match the new one
+ */
+@MainThread
+@Singleton
+class NotifViewManager @Inject constructor(
+    private val rowRegistry: NotifViewBarn,
+    private val stabilityManager: VisualStabilityManager,
+    private val featureFlags: FeatureFlags
+) {
+    var currentNotifs = listOf<ListEntry>()
+
+    private lateinit var listContainer: SimpleNotificationListContainer
+
+    fun attach(listBuilder: ShadeListBuilder) {
+        if (featureFlags.isNewNotifPipelineRenderingEnabled) {
+            listBuilder.setOnRenderListListener { entries: List<ListEntry> ->
+                this.onNotifTreeBuilt(entries)
+            }
+        }
+    }
+
+    fun setViewConsumer(consumer: SimpleNotificationListContainer) {
+        listContainer = consumer
+    }
+
+    /**
+     * Callback for when the tree is rebuilt
+     */
+    fun onNotifTreeBuilt(notifList: List<ListEntry>) {
+        Assert.isMainThread()
+
+        /*
+         * The assumption here is that anything from the old NotificationViewHierarchyManager that
+         * is responsible for filtering is done via the NotifFilter logic. This tree we get should
+         * be *the stuff to display* +/- redacted stuff
+         */
+
+        detachRows(notifList)
+        attachRows(notifList)
+
+        currentNotifs = notifList
+    }
+
+    private fun detachRows(entries: List<ListEntry>) {
+        // To properly detach rows, we are looking to remove any view in the consumer that is not
+        // present in the incoming list.
+        //
+        // Every listItem was top-level, so it's entry's parent was ROOT_ENTRY, but now
+        // there are two possibilities:
+        //
+        //      1. It is not present in the entry list
+        //          1a. It has moved to be a child in the entry list - transfer it
+        //          1b. It is gone completely - remove it
+        //      2. It is present in the entry list - diff the children
+        getListItems(listContainer)
+                .filter {
+                    // Ignore things that are showing the blocking helper
+                    !it.isBlockingHelperShowing
+                }
+                .forEach { listItem ->
+                    val noLongerTopLevel = listItem.entry.parent != ROOT_ENTRY
+                    val becameChild = noLongerTopLevel && listItem.entry.parent != null
+
+                    val idx = entries.indexOf(listItem.entry)
+
+                    if (noLongerTopLevel) {
+                        // Summaries won't become children; remove the whole group
+                        if (listItem.isSummaryWithChildren) {
+                            listItem.removeAllChildren()
+                        }
+
+                        if (becameChild) {
+                            // Top-level element is becoming a child, don't generate an animation
+                            listContainer.setChildTransferInProgress(true)
+                        }
+                        listContainer.removeListItem(listItem)
+                        listContainer.setChildTransferInProgress(false)
+                    } else if (entries[idx] is GroupEntry) {
+                        // A top-level entry exists. If it's a group, diff the children
+                        val groupChildren = (entries[idx] as GroupEntry).children
+                        listItem.notificationChildren?.forEach { listChild ->
+                            if (!groupChildren.contains(listChild.entry)) {
+                                listItem.removeChildNotification(listChild)
+
+                                // TODO: the old code only calls this if the notif is gone from
+                                // NEM.getActiveNotificationUnfiltered(). Do we care?
+                                listContainer.notifyGroupChildRemoved(
+                                        listChild.view, listChild.view.parent as ViewGroup)
+                            }
+                        }
+                    }
+                }
+    }
+
+    /** Convenience method for getting a sequence of [NotificationListItem]s */
+    private fun getListItems(container: SimpleNotificationListContainer):
+            Sequence<NotificationListItem> {
+        return (0 until container.getContainerChildCount()).asSequence()
+                .map { container.getContainerChildAt(it) }
+                .filterIsInstance<NotificationListItem>()
+    }
+
+    private fun attachRows(entries: List<ListEntry>) {
+
+        var orderChanged = false
+
+        // To attach rows we can use _this one weird trick_: if the intended view to add does not
+        // have a parent, then simply add it (and its children).
+        entries.forEach { entry ->
+            val listItem = rowRegistry.requireView(entry)
+
+            if (listItem.view.parent != null) {
+                listContainer.addListItem(listItem)
+                stabilityManager.notifyViewAddition(listItem.view)
+            }
+
+            if (entry is GroupEntry) {
+                for ((idx, childEntry) in entry.children.withIndex()) {
+                    val childListItem = rowRegistry.requireView(childEntry)
+                    // Child hasn't been added yet. add it!
+                    if (!listItem.notificationChildren.contains(childListItem)) {
+                        // TODO: old code here just Log.wtf()'d here. This might wreak havoc
+                        if (childListItem.view.parent != null) {
+                            throw IllegalStateException("trying to add a notification child that " +
+                                    "already has a parent. class: " +
+                                    "${childListItem.view.parent?.javaClass} " +
+                                    "\n child: ${childListItem.view}"
+                            )
+                        }
+
+                        listItem.addChildNotification(childListItem, idx)
+                        stabilityManager.notifyViewAddition(childListItem.view)
+                        listContainer.notifyGroupChildAdded(childListItem.view)
+                    }
+                }
+
+                // finally after removing and adding has been performed we can apply the order
+                orderChanged = orderChanged ||
+                        listItem.applyChildOrder(
+                                getChildListFromParent(entry),
+                                stabilityManager,
+                                null /*TODO: stability callback */
+                        )
+            }
+        }
+
+        if (orderChanged) {
+            listContainer.generateChildOrderChangedEvent()
+        }
+    }
+
+    private fun getChildListFromParent(parent: ListEntry): List<NotificationListItem> {
+        if (parent is GroupEntry) {
+            return parent.children.map { child -> rowRegistry.requireView(child) }
+                    .toList()
+        }
+
+        return emptyList()
+    }
+
+    fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
+    }
+}
+
+private const val TAG = "NotifViewDataSource"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 3e9d8a4..7019b5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -29,8 +29,6 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 
 import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
 import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
@@ -44,10 +42,7 @@
 import android.app.Person;
 import android.app.RemoteInputHistoryItem;
 import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.ShortcutInfo;
-import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.SystemClock;
@@ -55,25 +50,20 @@
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.icon.IconPack;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -81,8 +71,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -102,15 +90,11 @@
  * clean this up in the future.
  */
 public final class NotificationEntry extends ListEntry {
-    private static final String TAG = "NotificationEntry";
 
     private final String mKey;
     private StatusBarNotification mSbn;
     private Ranking mRanking;
 
-    private StatusBarIcon mSmallIcon;
-    private StatusBarIcon mPeopleAvatar;
-
     /*
      * Bookkeeping members
      */
@@ -142,10 +126,7 @@
     * TODO: Remove every member beneath this line if possible
     */
 
-    public StatusBarIconView icon;
-    public StatusBarIconView expandedIcon;
-    public StatusBarIconView centeredIcon;
-    public StatusBarIconView aodIcon;
+    private IconPack mIcons = IconPack.buildEmptyPack(null);
     private boolean interruption;
     public int targetSdk;
     private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
@@ -191,7 +172,8 @@
     private boolean hasSentReply;
 
     private boolean mSensitive = true;
-    private Runnable mOnSensitiveChangedListener;
+    private List<OnSensitivityChangedListener> mOnSensitivityChangedListeners = new ArrayList<>();
+
     private boolean mAutoHeadsUp;
     private boolean mPulseSupressed;
     private boolean mAllowFgsDismissal;
@@ -347,6 +329,15 @@
      * TODO: Remove as many of these as possible
      */
 
+    @NonNull
+    public IconPack getIcons() {
+        return mIcons;
+    }
+
+    public void setIcons(@NonNull IconPack icons) {
+        mIcons = icons;
+    }
+
     public void setInterruption() {
         interruption = true;
     }
@@ -464,239 +455,6 @@
                 || SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
     }
 
-    /**
-     * Create the icons for a notification
-     * @param context the context to create the icons with
-     * @param sbn the notification
-     * @throws InflationException Exception if required icons are not valid or specified
-     */
-    public void createIcons(Context context, StatusBarNotification sbn)
-            throws InflationException {
-        StatusBarIcon ic = getIcon(context, sbn, false /* redact */);
-
-        // Construct the icon.
-        icon = new StatusBarIconView(context,
-                sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-        icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
-        // Construct the expanded icon.
-        expandedIcon = new StatusBarIconView(context,
-                sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-        expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
-        // Construct the expanded icon.
-        aodIcon = new StatusBarIconView(context,
-                sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-        aodIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-        aodIcon.setIncreasedSize(true);
-
-        try {
-            setIcons(ic, Collections.singletonList(icon));
-            if (isSensitive()) {
-                ic = getIcon(context, sbn, true /* redact */);
-            }
-            setIcons(ic, Arrays.asList(expandedIcon, aodIcon));
-        } catch (InflationException e) {
-            icon = null;
-            expandedIcon = null;
-            centeredIcon = null;
-            aodIcon = null;
-            throw e;
-        }
-
-        expandedIcon.setVisibility(View.INVISIBLE);
-        expandedIcon.setOnVisibilityChangedListener(
-                newVisibility -> {
-                    if (row != null) {
-                        row.setIconsVisible(newVisibility != View.VISIBLE);
-                    }
-                });
-
-        // Construct the centered icon
-        if (mSbn.getNotification().isMediaNotification()) {
-            centeredIcon = new StatusBarIconView(context,
-                    sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
-            centeredIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-            try {
-                setIcons(ic, Collections.singletonList(centeredIcon));
-            } catch (InflationException e) {
-                centeredIcon = null;
-                throw e;
-            }
-        }
-    }
-
-    /**
-     * Determines if this icon should be tinted based on the sensitivity of the icon, its context
-     * and the user's indicated sensitivity preference.
-     *
-     * @param ic The icon that should/should not be tinted.
-     * @return
-     */
-    private boolean shouldTintIcon(StatusBarIconView ic) {
-        boolean usedInSensitiveContext = (ic == expandedIcon || ic == aodIcon);
-        return !isImportantConversation() || (usedInSensitiveContext && isSensitive());
-    }
-
-
-    private void setIcons(StatusBarIcon ic, List<StatusBarIconView> icons)
-            throws InflationException {
-        for (StatusBarIconView icon: icons) {
-            if (icon == null) {
-              continue;
-            }
-            icon.setTintIcons(shouldTintIcon(icon));
-            if (!icon.set(ic)) {
-                throw new InflationException("Couldn't create icon" + ic);
-            }
-        }
-    }
-
-    private StatusBarIcon getIcon(Context context, StatusBarNotification sbn, boolean redact)
-            throws InflationException {
-        Notification n = sbn.getNotification();
-        final boolean showPeopleAvatar = isImportantConversation() && !redact;
-
-        // If cached, return corresponding cached values
-        if (showPeopleAvatar && mPeopleAvatar != null) {
-            return mPeopleAvatar;
-        } else if (!showPeopleAvatar && mSmallIcon != null) {
-            return mSmallIcon;
-        }
-
-        Icon icon = showPeopleAvatar ? createPeopleAvatar(context) : n.getSmallIcon();
-        if (icon == null) {
-            throw new InflationException("No icon in notification from " + sbn.getPackageName());
-        }
-
-        StatusBarIcon ic = new StatusBarIcon(
-                    sbn.getUser(),
-                    sbn.getPackageName(),
-                    icon,
-                    n.iconLevel,
-                    n.number,
-                    StatusBarIconView.contentDescForNotification(context, n));
-
-        // Cache if important conversation.
-        if (isImportantConversation()) {
-            if (showPeopleAvatar) {
-                mPeopleAvatar = ic;
-            } else {
-                mSmallIcon = ic;
-            }
-        }
-        return ic;
-    }
-
-    private Icon createPeopleAvatar(Context context) throws InflationException {
-        // Attempt to extract form shortcut.
-        String conversationId = getChannel().getConversationId();
-        ShortcutQuery query = new ShortcutQuery()
-                .setPackage(mSbn.getPackageName())
-                .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
-                .setShortcutIds(Collections.singletonList(conversationId));
-        List<ShortcutInfo> shortcuts = context.getSystemService(LauncherApps.class)
-                .getShortcuts(query, mSbn.getUser());
-        Icon ic = null;
-        if (shortcuts != null && !shortcuts.isEmpty()) {
-            ic = shortcuts.get(0).getIcon();
-        }
-
-        // Fall back to notification large icon if available
-        if (ic == null) {
-            ic = mSbn.getNotification().getLargeIcon();
-        }
-
-        // Fall back to extract from message
-        if (ic == null) {
-            Bundle extras = mSbn.getNotification().extras;
-            List<Message> messages = Message.getMessagesFromBundleArray(
-                    extras.getParcelableArray(Notification.EXTRA_MESSAGES));
-            Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
-
-            for (int i = messages.size() - 1; i >= 0; i--) {
-                Message message = messages.get(i);
-                Person sender = message.getSenderPerson();
-                if (sender != null && sender != user) {
-                    ic = message.getSenderPerson().getIcon();
-                    break;
-                }
-            }
-        }
-
-        // Revert to small icon if still not available
-        if (ic == null) {
-            ic = mSbn.getNotification().getSmallIcon();
-        }
-        if (ic == null) {
-            throw new InflationException("No icon in notification from " + mSbn.getPackageName());
-        }
-        return ic;
-    }
-
-    private void updateSensitiveIconState() {
-        try {
-            StatusBarIcon ic = getIcon(getRow().getContext(), mSbn, isSensitive());
-            setIcons(ic, Arrays.asList(expandedIcon, aodIcon));
-        } catch (InflationException e) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Unable to update icon", e);
-            }
-        }
-    }
-
-    public void setIconTag(int key, Object tag) {
-        if (icon != null) {
-            icon.setTag(key, tag);
-            expandedIcon.setTag(key, tag);
-        }
-
-        if (centeredIcon != null) {
-            centeredIcon.setTag(key, tag);
-        }
-
-        if (aodIcon != null) {
-            aodIcon.setTag(key, tag);
-        }
-    }
-
-    /**
-     * Update the notification icons.
-     *
-     * @param context the context to create the icons with.
-     * @param sbn the notification to read the icon from.
-     * @throws InflationException Exception if required icons are not valid or specified
-     */
-    public void updateIcons(Context context, StatusBarNotification sbn)
-            throws InflationException {
-        if (icon != null) {
-            // Update the icon
-            mSmallIcon = null;
-            mPeopleAvatar = null;
-
-            StatusBarIcon ic = getIcon(context, sbn, false /* redact */);
-
-            icon.setNotification(sbn);
-            expandedIcon.setNotification(sbn);
-            aodIcon.setNotification(sbn);
-            setIcons(ic, Arrays.asList(icon, expandedIcon));
-
-            if (isSensitive()) {
-                ic = getIcon(context, sbn, true /* redact */);
-            }
-            setIcons(ic, Collections.singletonList(aodIcon));
-
-            if (centeredIcon != null) {
-                centeredIcon.setNotification(sbn);
-                setIcons(ic, Collections.singletonList(centeredIcon));
-            }
-        }
-    }
-
-    private boolean isImportantConversation() {
-        return getChannel() != null && getChannel().isImportantConversation();
-    }
-
     public int getContrastedColor(Context context, boolean isLowPriority,
             int backgroundColor) {
         int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
@@ -1125,9 +883,8 @@
         getRow().setSensitive(sensitive, deviceSensitive);
         if (sensitive != mSensitive) {
             mSensitive = sensitive;
-            updateSensitiveIconState();
-            if (mOnSensitiveChangedListener != null) {
-                mOnSensitiveChangedListener.run();
+            for (int i = 0; i < mOnSensitivityChangedListeners.size(); i++) {
+                mOnSensitivityChangedListeners.get(i).onSensitivityChanged(this);
             }
         }
     }
@@ -1136,8 +893,14 @@
         return mSensitive;
     }
 
-    public void setOnSensitiveChangedListener(Runnable listener) {
-        mOnSensitiveChangedListener = listener;
+    /** Add a listener to be notified when the entry's sensitivity changes. */
+    public void addOnSensitivityChangedListener(OnSensitivityChangedListener listener) {
+        mOnSensitivityChangedListeners.add(listener);
+    }
+
+    /** Remove a listener that was registered above. */
+    public void removeOnSensitivityChangedListener(OnSensitivityChangedListener listener) {
+        mOnSensitivityChangedListeners.remove(listener);
     }
 
     public boolean isPulseSuppressed() {
@@ -1167,6 +930,12 @@
         }
     }
 
+    /** Listener interface for {@link #addOnSensitivityChangedListener} */
+    public interface OnSensitivityChangedListener {
+        /** Called when the sensitivity changes */
+        void onSensitivityChanged(@NonNull NotificationEntry entry);
+    }
+
     /** @see #getDismissState() */
     public enum DismissState {
         /** User has not dismissed this notif or its parent */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 5b73b1a..f7d6cef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -319,7 +319,7 @@
         logParentingChanges();
         freeEmptyGroups();
 
-        // Step 6: Dispatch the new list, first to any listeners and then to the view layer
+        // Step 8: Dispatch the new list, first to any listeners and then to the view layer
         if (mIterationCount % 10 == 0) {
             mLogger.logFinalList(mNotifList);
         }
@@ -328,7 +328,7 @@
             mOnRenderListListener.onRenderList(mReadOnlyNotifList);
         }
 
-        // Step 7: We're done!
+        // Step 9: We're done!
         mLogger.logEndBuildList(mIterationCount);
         mPipelineState.setState(STATE_IDLE);
         mIterationCount++;
@@ -816,7 +816,7 @@
          * @param entries A read-only view into the current notif list. Note that this list is
          *                backed by the live list and will change in response to new pipeline runs.
          */
-        void onRenderList(List<ListEntry> entries);
+        void onRenderList(@NonNull List<ListEntry> entries);
     }
 
     private static final NotifSection sDefaultSection =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt
new file mode 100644
index 0000000..2dbe555
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SimpleNotificationListContainer.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.statusbar.notification.stack.NotificationListItem
+
+/**
+ * Minimal interface of what [NotifViewManager] needs from [NotificationListContainer]
+ */
+interface SimpleNotificationListContainer {
+    /** Called to signify that a top-level element is becoming a child in the shade */
+    fun setChildTransferInProgress(b: Boolean)
+    /** Used to generate a list of [NotificationListItem] */
+    fun getContainerChildAt(i: Int): View
+    /** Similar to above */
+    fun getContainerChildCount(): Int
+    /** Remove a [NotificationListItem] from the container */
+    fun removeListItem(li: NotificationListItem)
+    /** Add a [NotificationListItem] to the container */
+    fun addListItem(li: NotificationListItem)
+    /** Allows [NotifViewManager] to notify the container about a group child removal */
+    fun notifyGroupChildRemoved(row: View, parent: ViewGroup)
+    /** Allows [NotifViewManager] to notify the container about a group child addition */
+    fun notifyGroupChildAdded(row: View)
+    /** [NotifViewManager] calls this when the order of the children changes */
+    fun generateChildOrderChangedEvent()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 98c45ff..596235c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -260,11 +260,14 @@
     private void applyRanking(RankingMap rankingMap) {
         for (CoalescedEvent event : mCoalescedEvents.values()) {
             Ranking ranking = new Ranking();
-            if (!rankingMap.getRanking(event.getKey(), ranking)) {
-                throw new IllegalStateException(
-                        "Ranking map doesn't contain key: " + event.getKey());
+            if (rankingMap.getRanking(event.getKey(), ranking)) {
+                event.setRanking(ranking);
+            } else {
+                // TODO: (b/148791039) We should crash if we are ever handed a ranking with
+                //  incomplete entries. Right now, there's a race condition in NotificationListener
+                //  that means this might occur when SystemUI is starting up.
+                mLogger.logMissingRanking(event.getKey());
             }
-            event.setRanking(ranking);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
index 6e8788d..d4d5b64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -57,6 +57,14 @@
             "Modification of notif $str1 triggered TIMEOUT emit of batched group $str2"
         })
     }
+
+    fun logMissingRanking(forKey: String) {
+        buffer.log(TAG, LogLevel.WARNING, {
+            str1 = forKey
+        }, {
+            "RankingMap is missing an entry for coalesced notification $str1"
+        })
+    }
 }
 
 private const val TAG = "GroupCoalescer"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 370de83..92426e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -126,7 +126,7 @@
                 mInterceptedDismissalEntries.remove(entry.getKey());
                 mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
                         createDismissedByUserStats(entry));
-            } else if (mNotifPipeline.getActiveNotifs().contains(entry)) {
+            } else if (mNotifPipeline.getAllNotifs().contains(entry)) {
                 // Bubbles are hiding the notifications from the shade, but the bubble was
                 // deleted; therefore, the notification should be cancelled as if it were a user
                 // dismissal (this won't re-enter handleInterceptDimissal because Bubbles
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
index 854444f..b5b756d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
@@ -240,7 +240,7 @@
     }
 
     private NotificationEntry findNotificationEntryWithKey(String key) {
-        for (NotificationEntry entry : mNotifPipeline.getActiveNotifs()) {
+        for (NotificationEntry entry : mNotifPipeline.getAllNotifs()) {
             if (entry.getKey().equals(key)) {
                 return entry;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
new file mode 100644
index 0000000..573c129
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
+
+import android.annotation.Nullable;
+
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.util.Objects;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Coordinates heads up notification (HUN) interactions with the notification pipeline based on
+ * the HUN state reported by the {@link HeadsUpManager}. In this class we only consider one
+ * notification, in particular the {@link HeadsUpManager#getTopEntry()}, to be HeadsUpping at a
+ * time even though other notifications may be queued to heads up next.
+ *
+ * The current HUN, but not HUNs that are queued to heads up, will be:
+ * - Lifetime extended until it's no longer heads upping.
+ * - Promoted out of its group if it's a child of a group.
+ * - In the HeadsUpCoordinatorSection. Ordering is configured in {@link NotifCoordinators}.
+ * - Removed from HeadsUpManager if it's removed from the NotificationCollection.
+ *
+ * Note: The inflation callback in {@link PreparationCoordinator} handles showing HUNs.
+ */
+@Singleton
+public class HeadsUpCoordinator implements Coordinator {
+    private static final String TAG = "HeadsUpCoordinator";
+
+    private final HeadsUpManager mHeadsUpManager;
+    private final NotificationRemoteInputManager mRemoteInputManager;
+
+    // tracks the current HeadUpNotification reported by HeadsUpManager
+    private @Nullable NotificationEntry mCurrentHun;
+
+    private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
+    private NotificationEntry mNotifExtendingLifetime; // notif we've extended the lifetime for
+
+    @Inject
+    public HeadsUpCoordinator(
+            HeadsUpManager headsUpManager,
+            NotificationRemoteInputManager remoteInputManager) {
+        mHeadsUpManager = headsUpManager;
+        mRemoteInputManager = remoteInputManager;
+    }
+
+    @Override
+    public void attach(NotifPipeline pipeline) {
+        mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
+        pipeline.addCollectionListener(mNotifCollectionListener);
+        pipeline.addPromoter(mNotifPromoter);
+        pipeline.addNotificationLifetimeExtender(mLifetimeExtender);
+    }
+
+    @Override
+    public NotifSection getSection() {
+        return mNotifSection;
+    }
+
+    private final NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
+        /**
+         * Stop alerting HUNs that are removed from the notification collection
+         */
+        @Override
+        public void onEntryRemoved(NotificationEntry entry, int reason) {
+            final String entryKey = entry.getKey();
+            if (mHeadsUpManager.isAlerting(entryKey)) {
+                boolean removeImmediatelyForRemoteInput =
+                        mRemoteInputManager.getController().isSpinning(entryKey)
+                                && !FORCE_REMOTE_INPUT_HISTORY;
+                mHeadsUpManager.removeNotification(entry.getKey(), removeImmediatelyForRemoteInput);
+            }
+        }
+    };
+
+    private final NotifLifetimeExtender mLifetimeExtender = new NotifLifetimeExtender() {
+        @Override
+        public String getName() {
+            return TAG;
+        }
+
+        @Override
+        public void setCallback(OnEndLifetimeExtensionCallback callback) {
+            mEndLifetimeExtension = callback;
+        }
+
+        @Override
+        public boolean shouldExtendLifetime(NotificationEntry entry, int reason) {
+            boolean isShowingHun = isCurrentlyShowingHun(entry);
+            if (isShowingHun) {
+                mNotifExtendingLifetime = entry;
+            }
+            return isShowingHun;
+        }
+
+        @Override
+        public void cancelLifetimeExtension(NotificationEntry entry) {
+            if (Objects.equals(mNotifExtendingLifetime, entry)) {
+                mNotifExtendingLifetime = null;
+            }
+        }
+    };
+
+    private final NotifPromoter mNotifPromoter = new NotifPromoter(TAG) {
+        @Override
+        public boolean shouldPromoteToTopLevel(NotificationEntry entry) {
+            return isCurrentlyShowingHun(entry);
+        }
+    };
+
+    private final NotifSection mNotifSection = new NotifSection(TAG) {
+        @Override
+        public boolean isInSection(ListEntry entry) {
+            return isCurrentlyShowingHun(entry);
+        }
+    };
+
+    private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
+            new OnHeadsUpChangedListener() {
+        @Override
+        public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+            NotificationEntry newHUN = mHeadsUpManager.getTopEntry();
+            if (!Objects.equals(mCurrentHun, newHUN)) {
+                endNotifLifetimeExtension();
+                mCurrentHun = newHUN;
+                mNotifPromoter.invalidateList();
+                mNotifSection.invalidateList();
+            }
+        }
+    };
+
+    private boolean isCurrentlyShowingHun(ListEntry entry) {
+        return mCurrentHun == entry.getRepresentativeEntry();
+    }
+
+    private void endNotifLifetimeExtension() {
+        if (mNotifExtendingLifetime != null) {
+            mEndLifetimeExtension.onEndLifetimeExtension(
+                    mLifetimeExtender,
+                    mNotifExtendingLifetime);
+            mNotifExtendingLifetime = null;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 7a22d75..98104f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -49,6 +49,7 @@
     public NotifCoordinators(
             DumpManager dumpManager,
             FeatureFlags featureFlags,
+            HeadsUpCoordinator headsUpCoordinator,
             KeyguardCoordinator keyguardCoordinator,
             RankingCoordinator rankingCoordinator,
             ForegroundCoordinator foregroundCoordinator,
@@ -56,7 +57,6 @@
             BubbleCoordinator bubbleCoordinator,
             PreparationCoordinator preparationCoordinator) {
         dumpManager.registerDumpable(TAG, this);
-
         mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
         mCoordinators.add(keyguardCoordinator);
         mCoordinators.add(rankingCoordinator);
@@ -64,9 +64,10 @@
         mCoordinators.add(deviceProvisionedCoordinator);
         mCoordinators.add(bubbleCoordinator);
         if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
+            mCoordinators.add(headsUpCoordinator);
             mCoordinators.add(preparationCoordinator);
         }
-        // TODO: add new Coordinators here! (b/145134683, b/112656837)
+        // TODO: add new Coordinators here! (b/112656837)
 
         // TODO: add the sections in a particular ORDER (HeadsUp < People < Alerting)
         for (Coordinator c : mCoordinators) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index ebecf18..742615c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -26,13 +26,16 @@
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifViewBarn;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -57,21 +60,31 @@
     private final PreparationCoordinatorLogger mLogger;
     private final NotifInflater mNotifInflater;
     private final NotifInflationErrorManager mNotifErrorManager;
+    private final NotifViewBarn mViewBarn;
     private final Map<NotificationEntry, Integer> mInflationStates = new ArrayMap<>();
     private final IStatusBarService mStatusBarService;
+    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    private final HeadsUpManager mHeadsUpManager;
 
     @Inject
     public PreparationCoordinator(
             PreparationCoordinatorLogger logger,
             NotifInflaterImpl notifInflater,
             NotifInflationErrorManager errorManager,
-            IStatusBarService service) {
+            NotifViewBarn viewBarn,
+            IStatusBarService service,
+            NotificationInterruptStateProvider notificationInterruptStateProvider,
+            HeadsUpManager headsUpManager
+    ) {
         mLogger = logger;
         mNotifInflater = notifInflater;
         mNotifInflater.setInflationCallback(mInflationCallback);
         mNotifErrorManager = errorManager;
         mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);
+        mViewBarn = viewBarn;
         mStatusBarService = service;
+        mNotificationInterruptStateProvider = notificationInterruptStateProvider;
+        mHeadsUpManager = headsUpManager;
     }
 
     @Override
@@ -109,6 +122,7 @@
         @Override
         public void onEntryCleanUp(NotificationEntry entry) {
             mInflationStates.remove(entry);
+            mViewBarn.removeViewForEntry(entry);
         }
     };
 
@@ -142,7 +156,13 @@
         @Override
         public void onInflationFinished(NotificationEntry entry) {
             mLogger.logNotifInflated(entry.getKey());
+            mViewBarn.registerViewForEntry(entry, entry.getRow());
             mInflationStates.put(entry, STATE_INFLATED);
+
+            // TODO: should eventually be moved to HeadsUpCoordinator
+            if (mNotificationInterruptStateProvider.shouldHeadsUp(entry)) {
+                mHeadsUpManager.showNotification(entry);
+            }
             mNotifInflatingFilter.invalidateList();
         }
     };
@@ -151,6 +171,7 @@
             new NotifInflationErrorManager.NotifInflationErrorListener() {
         @Override
         public void onNotifInflationError(NotificationEntry entry, Exception e) {
+            mViewBarn.removeViewForEntry(entry);
             mInflationStates.put(entry, STATE_ERROR);
             try {
                 final StatusBarNotification sbn = entry.getSbn();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 4beeede..7237284 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.collection.inflation;
 
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.Nullable;
@@ -29,8 +28,6 @@
 import android.view.ViewGroup;
 
 import com.android.internal.util.NotificationMessagingUtil;
-import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -38,25 +35,22 @@
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.icon.IconManager;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
 import com.android.systemui.statusbar.notification.row.RowContentBindStage;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.util.Objects;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 import javax.inject.Provider;
 import javax.inject.Singleton;
 
@@ -66,23 +60,23 @@
 
     private static final String TAG = "NotificationViewManager";
 
-    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
-
     private final Context mContext;
-    private final NotifBindPipeline mNotifBindPipeline;
-    private final RowContentBindStage mRowContentBindStage;
     private final NotificationMessagingUtil mMessagingUtil;
     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
     private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+    private final NotifBindPipeline mNotifBindPipeline;
+    private final RowContentBindStage mRowContentBindStage;
+    private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    private final Provider<RowInflaterTask> mRowInflaterTaskProvider;
+    private final ExpandableNotificationRowComponent.Builder
+            mExpandableNotificationRowComponentBuilder;
+    private final IconManager mIconManager;
 
     private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
     private NotificationRowContentBinder.InflationCallback mInflationCallback;
     private BindRowCallback mBindRowCallback;
     private NotificationClicker mNotificationClicker;
-    private final Provider<RowInflaterTask> mRowInflaterTaskProvider;
-    private final ExpandableNotificationRowComponent.Builder
-            mExpandableNotificationRowComponentBuilder;
 
     @Inject
     public NotificationRowBinderImpl(
@@ -92,14 +86,10 @@
             NotificationLockscreenUserManager notificationLockscreenUserManager,
             NotifBindPipeline notifBindPipeline,
             RowContentBindStage rowContentBindStage,
-            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
-            KeyguardBypassController keyguardBypassController,
-            StatusBarStateController statusBarStateController,
-            NotificationGroupManager notificationGroupManager,
-            NotificationGutsManager notificationGutsManager,
             NotificationInterruptStateProvider notificationInterruptionStateProvider,
             Provider<RowInflaterTask> rowInflaterTaskProvider,
-            ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) {
+            ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder,
+            IconManager iconManager) {
         mContext = context;
         mNotifBindPipeline = notifBindPipeline;
         mRowContentBindStage = rowContentBindStage;
@@ -109,6 +99,7 @@
         mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
         mRowInflaterTaskProvider = rowInflaterTaskProvider;
         mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
+        mIconManager = iconManager;
     }
 
     /**
@@ -120,6 +111,8 @@
         mPresenter = presenter;
         mListContainer = listContainer;
         mBindRowCallback = bindRowCallback;
+
+        mIconManager.attach();
     }
 
     public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) {
@@ -142,12 +135,12 @@
 
         final StatusBarNotification sbn = entry.getSbn();
         if (entry.rowExists()) {
-            entry.updateIcons(mContext, sbn);
+            mIconManager.updateIcons(entry);
             entry.reset();
             updateNotification(entry, pmUser, sbn, entry.getRow());
             entry.getRowController().setOnDismissRunnable(onDismissRunnable);
         } else {
-            entry.createIcons(mContext, sbn);
+            mIconManager.createIcons(entry);
             mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,
                     row -> {
                         // Setup the controller for the view.
@@ -227,8 +220,8 @@
         row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
                 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
 
-        // TODO: should updates to the entry be happening somewhere else?
-        entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
+        // TODO: should this be happening somewhere else?
+        mIconManager.updateIconTags(entry, entry.targetSdk);
 
         row.setOnActivatedListener(mPresenter);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
deleted file mode 100644
index 15f312d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/FakePipelineConsumer.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.init;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Temporary class that tracks the result of the list builder and dumps it to text when requested.
- *
- * Eventually, this will be something that hands off the result of the pipeline to the View layer.
- */
-public class FakePipelineConsumer implements Dumpable {
-    private List<ListEntry> mEntries = Collections.emptyList();
-
-    /** Attach the consumer to the pipeline. */
-    public void attach(ShadeListBuilder listBuilder) {
-        listBuilder.setOnRenderListListener(this::onBuildComplete);
-    }
-
-    private void onBuildComplete(List<ListEntry> entries) {
-        mEntries = entries;
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println();
-        pw.println("Active notif tree:");
-        for (int i = 0; i < mEntries.size(); i++) {
-            ListEntry entry = mEntries.get(i);
-            if (entry instanceof GroupEntry) {
-                GroupEntry ge = (GroupEntry) entry;
-                pw.println(dumpGroup(ge, "", i));
-
-                pw.println(dumpEntry(ge.getSummary(), INDENT, -1));
-                for (int j = 0; j < ge.getChildren().size(); j++) {
-                    pw.println(dumpEntry(ge.getChildren().get(j), INDENT, j));
-                }
-            } else {
-                pw.println(dumpEntry(entry.getRepresentativeEntry(), "", i));
-            }
-        }
-    }
-
-    private String dumpGroup(GroupEntry entry, String indent, int index) {
-        return String.format(
-                "%s[%d] %s (group)",
-                indent,
-                index,
-                entry.getKey());
-    }
-
-    private String dumpEntry(NotificationEntry entry, String indent, int index) {
-        return String.format(
-                "%s[%s] %s (channel=%s)",
-                indent,
-                index == -1 ? "*" : Integer.toString(index),
-                entry.getKey(),
-                entry.getChannel() != null ? entry.getChannel().getId() : "");
-    }
-
-    private static final String INDENT = "   ";
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 258f6d0..f150257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -25,10 +25,12 @@
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifViewManager;
 import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
 import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
 import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -50,7 +52,7 @@
     private final DumpManager mDumpManager;
     private final FeatureFlags mFeatureFlags;
 
-    private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer();
+    private final NotifViewManager mNotifViewManager;
 
     @Inject
     public NotifPipelineInitializer(
@@ -61,7 +63,8 @@
             NotifCoordinators notifCoordinators,
             NotifInflaterImpl notifInflater,
             DumpManager dumpManager,
-            FeatureFlags featureFlags) {
+            FeatureFlags featureFlags,
+            NotifViewManager notifViewManager) {
         mPipelineWrapper = pipelineWrapper;
         mGroupCoalescer = groupCoalescer;
         mNotifCollection = notifCollection;
@@ -70,12 +73,14 @@
         mDumpManager = dumpManager;
         mNotifInflater = notifInflater;
         mFeatureFlags = featureFlags;
+        mNotifViewManager = notifViewManager;
     }
 
     /** Hooks the new pipeline up to NotificationManager */
     public void initialize(
             NotificationListener notificationService,
-            NotificationRowBinderImpl rowBinder) {
+            NotificationRowBinderImpl rowBinder,
+            NotificationListContainer listContainer) {
 
         mDumpManager.registerDumpable("NotifPipeline", this);
 
@@ -88,7 +93,8 @@
         mNotifPluggableCoordinators.attach(mPipelineWrapper);
 
         // Wire up pipeline
-        mFakePipelineConsumer.attach(mListBuilder);
+        mNotifViewManager.setViewConsumer(listContainer);
+        mNotifViewManager.attach(mListBuilder);
         mListBuilder.attach(mNotifCollection);
         mNotifCollection.attach(mGroupCoalescer);
         mGroupCoalescer.attach(notificationService);
@@ -98,7 +104,7 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        mFakePipelineConsumer.dump(fd, pw, args);
+        mNotifViewManager.dump(fd, pw, args);
         mNotifPluggableCoordinators.dump(fd, pw, args);
         mGroupCoalescer.dump(fd, pw, args);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
index 171816f..b4c2bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
@@ -20,6 +20,8 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
+import java.util.Collection;
+
 /**
  * A notification collection that manages the list of {@link NotificationEntry}s that will be
  * rendered.
@@ -34,4 +36,13 @@
      * or deleted.
      */
     void addCollectionListener(NotifCollectionListener listener);
+
+    /**
+     * Returns the list of all known notifications, i.e. the notifications that are currently posted
+     * to the phone. In general, this tracks closely to the list maintained by NotificationManager,
+     * but it can diverge slightly due to lifetime extenders.
+     *
+     * The returned collection is read-only, unsorted, unfiltered, and ungrouped.
+     */
+    Collection<NotificationEntry> getAllNotifs();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
index b2c53da..0c0cded 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionListener.java
@@ -70,8 +70,24 @@
     }
 
     /**
-     * Called whenever the RankingMap is updated by system server. By the time this listener is
-     * called, the Rankings of all entries will have been updated.
+     * Called whenever a ranking update is applied. During a ranking update, all active,
+     * non-lifetime-extended notification entries will have their ranking object updated.
+     *
+     * Ranking updates occur whenever a notification is added, updated, or removed, or when a
+     * standalone ranking is sent from the server.
+     */
+    default void onRankingApplied() {
+    }
+
+    /**
+     * Called whenever system server sends a standalone ranking update (i.e. one that isn't
+     * associated with a notification being added or removed).
+     *
+     * In general it is unsafe to depend on this method as rankings can change for other reasons.
+     * Instead, listen for {@link #onRankingApplied()}, which is called whenever ANY ranking update
+     * is applied, regardless of source.
+     *
+     * @deprecated Use {@link #onRankingApplied()} instead.
      */
     default void onRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
new file mode 100644
index 0000000..2ef0368
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.notifcollection
+
+import android.service.notification.NotificationListenerService.RankingMap
+import com.android.systemui.statusbar.notification.collection.NotifCollection
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/**
+ * Set of classes that represent the various events that [NotifCollection] can dispatch to
+ * [NotifCollectionListener]s.
+ *
+ * These events build up in a queue and are periodically emitted in chunks by the collection.
+ */
+
+sealed class NotifEvent {
+    fun dispatchTo(listeners: List<NotifCollectionListener>) {
+        for (i in listeners.indices) {
+            dispatchToListener(listeners[i])
+        }
+    }
+
+    abstract fun dispatchToListener(listener: NotifCollectionListener)
+}
+
+data class InitEntryEvent(
+    val entry: NotificationEntry
+) : NotifEvent() {
+    override fun dispatchToListener(listener: NotifCollectionListener) {
+        listener.onEntryInit(entry)
+    }
+}
+
+data class EntryAddedEvent(
+    val entry: NotificationEntry
+) : NotifEvent() {
+    override fun dispatchToListener(listener: NotifCollectionListener) {
+        listener.onEntryAdded(entry)
+    }
+}
+
+data class EntryUpdatedEvent(
+    val entry: NotificationEntry
+) : NotifEvent() {
+    override fun dispatchToListener(listener: NotifCollectionListener) {
+        listener.onEntryUpdated(entry)
+    }
+}
+
+data class EntryRemovedEvent(
+    val entry: NotificationEntry,
+    val reason: Int
+) : NotifEvent() {
+    override fun dispatchToListener(listener: NotifCollectionListener) {
+        listener.onEntryRemoved(entry, reason)
+    }
+}
+
+data class CleanUpEntryEvent(
+    val entry: NotificationEntry
+) : NotifEvent() {
+    override fun dispatchToListener(listener: NotifCollectionListener) {
+        listener.onEntryCleanUp(entry)
+    }
+}
+
+data class RankingUpdatedEvent(
+    val rankingMap: RankingMap
+) : NotifEvent() {
+    override fun dispatchToListener(listener: NotifCollectionListener) {
+        listener.onRankingUpdate(rankingMap)
+    }
+}
+
+class RankingAppliedEvent() : NotifEvent() {
+    override fun dispatchToListener(listener: NotifCollectionListener) {
+        listener.onRankingApplied()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
new file mode 100644
index 0000000..afc123f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon
+
+import android.app.Notification
+import android.content.Context
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import javax.inject.Inject
+
+/**
+ * Testable wrapper around Context.
+ */
+class IconBuilder @Inject constructor(
+    private val context: Context
+) {
+    fun createIconView(entry: NotificationEntry): StatusBarIconView {
+        return StatusBarIconView(
+                context,
+                "${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}",
+                entry.sbn)
+    }
+
+    fun getIconContentDescription(n: Notification): CharSequence {
+        return StatusBarIconView.contentDescForNotification(context, n)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
new file mode 100644
index 0000000..bb0fcaf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon
+
+import android.app.Notification
+import android.app.Person
+import android.content.pm.LauncherApps
+import android.graphics.drawable.Icon
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import android.view.View
+import android.widget.ImageView
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.systemui.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.InflationException
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import javax.inject.Inject
+
+/**
+ * Inflates and updates icons associated with notifications
+ *
+ * Notifications are represented by icons in a few different places -- in the status bar, in the
+ * notification shelf, in AOD, etc. This class is in charge of inflating the views that hold these
+ * icons and keeping the icon assets themselves up to date as notifications change.
+ *
+ * TODO: Much of this code was copied whole-sale in order to get it out of NotificationEntry.
+ *  Long-term, it should probably live somewhere in the content inflation pipeline.
+ */
+class IconManager @Inject constructor(
+    private val notifCollection: CommonNotifCollection,
+    private val launcherApps: LauncherApps,
+    private val iconBuilder: IconBuilder
+) {
+    fun attach() {
+        notifCollection.addCollectionListener(entryListener)
+    }
+
+    private val entryListener = object : NotifCollectionListener {
+        override fun onEntryInit(entry: NotificationEntry) {
+            entry.addOnSensitivityChangedListener(sensitivityListener)
+        }
+
+        override fun onEntryCleanUp(entry: NotificationEntry) {
+            entry.removeOnSensitivityChangedListener(sensitivityListener)
+        }
+
+        override fun onRankingApplied() {
+            // When the sensitivity changes OR when the isImportantConversation status changes,
+            // we need to update the icons
+            for (entry in notifCollection.allNotifs) {
+                val isImportant = isImportantConversation(entry)
+                if (entry.icons.areIconsAvailable &&
+                        isImportant != entry.icons.isImportantConversation) {
+                    updateIconsSafe(entry)
+                }
+                entry.icons.isImportantConversation = isImportant
+            }
+        }
+    }
+
+    private val sensitivityListener = NotificationEntry.OnSensitivityChangedListener {
+        entry -> updateIconsSafe(entry)
+    }
+
+    /**
+     * Inflate icon views for each icon variant and assign appropriate icons to them. Stores the
+     * result in [NotificationEntry.getIcons].
+     *
+     * @throws InflationException Exception if required icons are not valid or specified
+     */
+    @Throws(InflationException::class)
+    fun createIcons(entry: NotificationEntry) {
+        // Construct the status bar icon view.
+        val sbIcon = iconBuilder.createIconView(entry)
+        sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+
+        // Construct the shelf icon view.
+        val shelfIcon = iconBuilder.createIconView(entry)
+        shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+
+        shelfIcon.visibility = View.INVISIBLE
+        // TODO: This doesn't belong here
+        shelfIcon.setOnVisibilityChangedListener { newVisibility: Int ->
+            if (entry.row != null) {
+                entry.row.setIconsVisible(newVisibility != View.VISIBLE)
+            }
+        }
+
+        // Construct the aod icon view.
+        val aodIcon = iconBuilder.createIconView(entry)
+        aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+        aodIcon.setIncreasedSize(true)
+
+        // Construct the centered icon view.
+        val centeredIcon = if (entry.sbn.notification.isMediaNotification) {
+            iconBuilder.createIconView(entry).apply {
+                scaleType = ImageView.ScaleType.CENTER_INSIDE
+            }
+        } else {
+            null
+        }
+
+        // Set the icon views' icons
+        val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+
+        try {
+            setIcon(entry, normalIconDescriptor, sbIcon)
+            setIcon(entry, sensitiveIconDescriptor, shelfIcon)
+            setIcon(entry, sensitiveIconDescriptor, aodIcon)
+            if (centeredIcon != null) {
+                setIcon(entry, normalIconDescriptor, centeredIcon)
+            }
+            entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, centeredIcon, entry.icons)
+        } catch (e: InflationException) {
+            entry.icons = IconPack.buildEmptyPack(entry.icons)
+            throw e
+        }
+    }
+
+    /**
+     * Update the notification icons.
+     *
+     * @param entry the notification to read the icon from.
+     * @throws InflationException Exception if required icons are not valid or specified
+     */
+    @Throws(InflationException::class)
+    fun updateIcons(entry: NotificationEntry) {
+        if (!entry.icons.areIconsAvailable) {
+            return
+        }
+        entry.icons.smallIconDescriptor = null
+        entry.icons.peopleAvatarDescriptor = null
+
+        val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+
+        entry.icons.statusBarIcon?.let {
+            it.notification = entry.sbn
+            setIcon(entry, normalIconDescriptor, it)
+        }
+
+        entry.icons.shelfIcon?.let {
+            it.notification = entry.sbn
+            setIcon(entry, normalIconDescriptor, it)
+        }
+
+        entry.icons.aodIcon?.let {
+            it.notification = entry.sbn
+            setIcon(entry, sensitiveIconDescriptor, it)
+        }
+
+        entry.icons.centeredIcon?.let {
+            it.notification = entry.sbn
+            setIcon(entry, sensitiveIconDescriptor, it)
+        }
+    }
+
+    /**
+     * Updates tags on the icon views to match the posting app's target SDK level
+     *
+     * Note that this method MUST be called after both [createIcons] and [updateIcons].
+     */
+    fun updateIconTags(entry: NotificationEntry, targetSdk: Int) {
+        setTagOnIconViews(
+                entry.icons,
+                R.id.icon_is_pre_L,
+                targetSdk < Build.VERSION_CODES.LOLLIPOP)
+    }
+
+    private fun updateIconsSafe(entry: NotificationEntry) {
+        try {
+            updateIcons(entry)
+        } catch (e: InflationException) {
+            // TODO This should mark the entire row as involved in an inflation error
+            Log.e(TAG, "Unable to update icon", e)
+        }
+    }
+
+    @Throws(InflationException::class)
+    private fun getIconDescriptors(
+        entry: NotificationEntry
+    ): Pair<StatusBarIcon, StatusBarIcon> {
+        val iconDescriptor = getIconDescriptor(entry, false /* redact */)
+        val sensitiveDescriptor = if (entry.isSensitive) {
+            getIconDescriptor(entry, true /* redact */)
+        } else {
+            iconDescriptor
+        }
+        return Pair(iconDescriptor, sensitiveDescriptor)
+    }
+
+    @Throws(InflationException::class)
+    private fun getIconDescriptor(
+        entry: NotificationEntry,
+        redact: Boolean
+    ): StatusBarIcon {
+        val n = entry.sbn.notification
+        val showPeopleAvatar = isImportantConversation(entry) && !redact
+
+        val peopleAvatarDescriptor = entry.icons.peopleAvatarDescriptor
+        val smallIconDescriptor = entry.icons.smallIconDescriptor
+
+        // If cached, return corresponding cached values
+        if (showPeopleAvatar && peopleAvatarDescriptor != null) {
+            return peopleAvatarDescriptor
+        } else if (!showPeopleAvatar && smallIconDescriptor != null) {
+            return smallIconDescriptor
+        }
+
+        val icon =
+                (if (showPeopleAvatar) {
+                    createPeopleAvatar(entry)
+                } else {
+                    n.smallIcon
+                }) ?: throw InflationException(
+                        "No icon in notification from " + entry.sbn.packageName)
+
+        val ic = StatusBarIcon(
+                entry.sbn.user,
+                entry.sbn.packageName,
+                icon,
+                n.iconLevel,
+                n.number,
+                iconBuilder.getIconContentDescription(n))
+
+        // Cache if important conversation.
+        if (isImportantConversation(entry)) {
+            if (showPeopleAvatar) {
+                entry.icons.peopleAvatarDescriptor = ic
+            } else {
+                entry.icons.smallIconDescriptor = ic
+            }
+        }
+
+        return ic
+    }
+
+    @Throws(InflationException::class)
+    private fun setIcon(
+        entry: NotificationEntry,
+        iconDescriptor: StatusBarIcon,
+        iconView: StatusBarIconView
+    ) {
+        iconView.setTintIcons(shouldTintIconView(entry, iconView))
+        if (!iconView.set(iconDescriptor)) {
+            throw InflationException("Couldn't create icon $iconDescriptor")
+        }
+    }
+
+    @Throws(InflationException::class)
+    private fun createPeopleAvatar(entry: NotificationEntry): Icon? {
+        // Attempt to extract form shortcut.
+        val conversationId = entry.ranking.channel.conversationId
+        val query = LauncherApps.ShortcutQuery()
+                .setPackage(entry.sbn.packageName)
+                .setQueryFlags(
+                        LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
+                                or LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED)
+                .setShortcutIds(listOf(conversationId))
+        val shortcuts = launcherApps.getShortcuts(query, entry.sbn.user)
+        var ic: Icon? = null
+        if (shortcuts != null && shortcuts.isNotEmpty()) {
+            ic = shortcuts[0].icon
+        }
+
+        // Fall back to notification large icon if available
+        if (ic == null) {
+            ic = entry.sbn.notification.getLargeIcon()
+        }
+
+        // Fall back to extract from message
+        if (ic == null) {
+            val extras: Bundle = entry.sbn.notification.extras
+            val messages = Notification.MessagingStyle.Message.getMessagesFromBundleArray(
+                    extras.getParcelableArray(Notification.EXTRA_MESSAGES))
+            val user = extras.getParcelable<Person>(Notification.EXTRA_MESSAGING_PERSON)
+            for (i in messages.indices.reversed()) {
+                val message = messages[i]
+                val sender = message.senderPerson
+                if (sender != null && sender !== user) {
+                    ic = message.senderPerson!!.icon
+                    break
+                }
+            }
+        }
+
+        // Revert to small icon if still not available
+        if (ic == null) {
+            ic = entry.sbn.notification.smallIcon
+        }
+        if (ic == null) {
+            throw InflationException("No icon in notification from " + entry.sbn.packageName)
+        }
+        return ic
+    }
+
+    /**
+     * Determines if this icon should be tinted based on the sensitivity of the icon, its context
+     * and the user's indicated sensitivity preference.
+     *
+     * @param iconView The icon that should/should not be tinted.
+     */
+    private fun shouldTintIconView(entry: NotificationEntry, iconView: StatusBarIconView): Boolean {
+        val usedInSensitiveContext =
+                iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
+        return !isImportantConversation(entry) || usedInSensitiveContext && entry.isSensitive
+    }
+
+    private fun isImportantConversation(entry: NotificationEntry): Boolean {
+        return entry.ranking.channel != null && entry.ranking.channel.isImportantConversation
+    }
+
+    private fun setTagOnIconViews(icons: IconPack, key: Int, tag: Any) {
+        icons.statusBarIcon?.setTag(key, tag)
+        icons.shelfIcon?.setTag(key, tag)
+        icons.aodIcon?.setTag(key, tag)
+        icons.centeredIcon?.setTag(key, tag)
+    }
+}
+
+private const val TAG = "IconManager"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
new file mode 100644
index 0000000..054e381
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.icon;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.StatusBarIconView;
+
+/**
+ * Data class for storing icons associated with a notification
+ */
+public final class IconPack {
+
+    private final boolean mAreIconsAvailable;
+    @Nullable private final StatusBarIconView mStatusBarIcon;
+    @Nullable private final StatusBarIconView mShelfIcon;
+    @Nullable private final StatusBarIconView mAodIcon;
+    @Nullable private final StatusBarIconView mCenteredIcon;
+
+    @Nullable private StatusBarIcon mSmallIconDescriptor;
+    @Nullable private StatusBarIcon mPeopleAvatarDescriptor;
+
+    private boolean mIsImportantConversation;
+
+    /**
+     * Builds an empty instance of IconPack that doesn't have any icons (because either they
+     * haven't been inflated yet or there was an error while inflating them).
+     */
+    public static IconPack buildEmptyPack(@Nullable IconPack fromSource) {
+        return new IconPack(false, null, null, null, null, fromSource);
+    }
+
+    /**
+     * Builds an instance of an IconPack that contains successfully-inflated icons
+     */
+    public static IconPack buildPack(
+            @NonNull StatusBarIconView statusBarIcon,
+            @NonNull StatusBarIconView shelfIcon,
+            @NonNull StatusBarIconView aodIcon,
+            @Nullable StatusBarIconView centeredIcon,
+            @Nullable IconPack source) {
+        return new IconPack(true, statusBarIcon, shelfIcon, aodIcon, centeredIcon, source);
+    }
+
+    private IconPack(
+            boolean areIconsAvailable,
+            @Nullable StatusBarIconView statusBarIcon,
+            @Nullable StatusBarIconView shelfIcon,
+            @Nullable StatusBarIconView aodIcon,
+            @Nullable StatusBarIconView centeredIcon,
+            @Nullable IconPack source) {
+        mAreIconsAvailable = areIconsAvailable;
+        mStatusBarIcon = statusBarIcon;
+        mShelfIcon = shelfIcon;
+        mCenteredIcon = centeredIcon;
+        mAodIcon = aodIcon;
+        if (source != null) {
+            mIsImportantConversation = source.mIsImportantConversation;
+        }
+    }
+
+    /** The version of the notification icon that appears in the status bar. */
+    @Nullable
+    public StatusBarIconView getStatusBarIcon() {
+        return mStatusBarIcon;
+    }
+
+    /**
+     * The version of the icon that appears in the "shelf" at the bottom of the notification shade.
+     * In general, this icon also appears somewhere on the notification and is "sucked" into the
+     * shelf as the scrolls beyond it.
+     */
+    @Nullable
+    public StatusBarIconView getShelfIcon() {
+        return mShelfIcon;
+    }
+
+    @Nullable
+    public StatusBarIconView getCenteredIcon() {
+        return mCenteredIcon;
+    }
+
+    /** The version of the icon that's shown when pulsing (in AOD). */
+    @Nullable
+    public StatusBarIconView getAodIcon() {
+        return mAodIcon;
+    }
+
+    @Nullable
+    StatusBarIcon getSmallIconDescriptor() {
+        return mSmallIconDescriptor;
+    }
+
+    void setSmallIconDescriptor(@Nullable StatusBarIcon smallIconDescriptor) {
+        mSmallIconDescriptor = smallIconDescriptor;
+    }
+
+    @Nullable
+    StatusBarIcon getPeopleAvatarDescriptor() {
+        return mPeopleAvatarDescriptor;
+    }
+
+    void setPeopleAvatarDescriptor(@Nullable StatusBarIcon peopleAvatarDescriptor) {
+        mPeopleAvatarDescriptor = peopleAvatarDescriptor;
+    }
+
+    boolean isImportantConversation() {
+        return mIsImportantConversation;
+    }
+
+    void setImportantConversation(boolean importantConversation) {
+        mIsImportantConversation = importantConversation;
+    }
+
+    public boolean getAreIconsAvailable() {
+        return mAreIconsAvailable;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 8a6d5c7..7a7178c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -94,7 +94,10 @@
         notifBindPipelineInitializer.initialize()
 
         if (featureFlags.isNewNotifPipelineEnabled) {
-            newNotifPipeline.get().initialize(notificationListener, notificationRowBinder)
+            newNotifPipeline.get().initialize(
+                    notificationListener,
+                    notificationRowBinder,
+                    listContainer)
         }
 
         if (featureFlags.isNewNotifPipelineRenderingEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index ea1bdd6..b9dd974 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -271,6 +271,10 @@
         }
     }
 
+    public boolean isActive() {
+        return mActivated;
+    }
+
     private void startActivateAnimation(final boolean reverse) {
         if (!isAttachedToWindow()) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 2643ec9..2f0e433 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -111,7 +111,7 @@
             if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
                     && mView.disallowSingleClick(ev)
                     && !mAccessibilityManager.isTouchExplorationEnabled()) {
-                if (!mView.isActivated()) {
+                if (!mView.isActive()) {
                     return true;
                 } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
                     mBlockNextTouch = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
index 373457d..dbfa27f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
@@ -37,7 +37,7 @@
         }
 
         (findViewById(R.id.icon) as StatusBarIconView).apply {
-            set(entry?.icon?.statusBarIcon)
+            set(entry?.icons?.statusBarIcon?.statusBarIcon)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index f61fe98..5f2b256 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -94,6 +94,7 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationListItem;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.SwipeableView;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -115,7 +116,8 @@
  * the group summary (which contains 1 or more child notifications).
  */
 public class ExpandableNotificationRow extends ActivatableNotificationView
-        implements PluginListener<NotificationMenuRowPlugin>, SwipeableView {
+        implements PluginListener<NotificationMenuRowPlugin>, SwipeableView,
+        NotificationListItem {
 
     private static final boolean DEBUG = false;
     private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
@@ -579,7 +581,7 @@
 
     @VisibleForTesting
     void updateShelfIconColor() {
-        StatusBarIconView expandedIcon = mEntry.expandedIcon;
+        StatusBarIconView expandedIcon = mEntry.getIcons().getShelfIcon();
         boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
         boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
                 ContrastColorUtil.getInstance(mContext));
@@ -666,6 +668,7 @@
         layout.setHeights(minHeight, headsUpHeight, mNotificationMaxHeight);
     }
 
+    @NonNull
     public NotificationEntry getEntry() {
         return mEntry;
     }
@@ -767,6 +770,17 @@
         row.setIsChildInGroup(true, this);
     }
 
+    /**
+     * Same as {@link #addChildNotification(ExpandableNotificationRow, int)}, but takes a
+     * {@link NotificationListItem} instead
+     *
+     * @param childItem item
+     * @param childIndex index
+     */
+    public void addChildNotification(NotificationListItem childItem, int childIndex) {
+        addChildNotification((ExpandableNotificationRow) childItem.getView(), childIndex);
+    }
+
     public void removeChildNotification(ExpandableNotificationRow row) {
         if (mChildrenContainer != null) {
             mChildrenContainer.removeNotification(row);
@@ -777,6 +791,11 @@
     }
 
     @Override
+    public void removeChildNotification(NotificationListItem child) {
+        removeChildNotification((ExpandableNotificationRow) child.getView());
+    }
+
+    @Override
     public boolean isChildInGroup() {
         return mNotificationParent != null;
     }
@@ -879,7 +898,7 @@
      * @param callback the callback to invoked in case it is not allowed
      * @return whether the list order has changed
      */
-    public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
+    public boolean applyChildOrder(List<? extends NotificationListItem> childOrder,
             VisualStabilityManager visualStabilityManager,
             VisualStabilityManager.Callback callback) {
         return mChildrenContainer != null && mChildrenContainer.applyChildOrder(childOrder,
@@ -1274,6 +1293,11 @@
         onChildrenCountChanged();
     }
 
+    @Override
+    public View getView() {
+        return this;
+    }
+
     public void setForceUnlocked(boolean forceUnlocked) {
         mForceUnlocked = forceUnlocked;
         if (mIsSummaryWithChildren) {
@@ -1290,7 +1314,7 @@
         setLongPressListener(null);
         mGroupParentWhenDismissed = mNotificationParent;
         mChildAfterViewWhenDismissed = null;
-        mEntry.icon.setDismissed();
+        mEntry.getIcons().getStatusBarIcon().setDismissed();
         if (isChildInGroup()) {
             List<ExpandableNotificationRow> notificationChildren =
                     mNotificationParent.getNotificationChildren();
@@ -1832,7 +1856,7 @@
                 mTranslateableViews.get(i).setTranslationX(0);
             }
             invalidateOutline();
-            getEntry().expandedIcon.setScrollX(0);
+            getEntry().getIcons().getShelfIcon().setScrollX(0);
         }
 
         if (mMenuRow != null) {
@@ -1912,7 +1936,7 @@
             // In order to keep the shelf in sync with this swiping, we're simply translating
             // it's icon by the same amount. The translation is already being used for the normal
             // positioning, so we can use the scrollX instead.
-            getEntry().expandedIcon.setScrollX((int) -translationX);
+            getEntry().getIcons().getShelfIcon().setScrollX((int) -translationX);
         }
 
         if (mMenuRow != null && mMenuRow.getMenuView() != null) {
@@ -2111,7 +2135,7 @@
 
     @Override
     public StatusBarIconView getShelfIcon() {
-        return getEntry().expandedIcon;
+        return getEntry().getIcons().getShelfIcon();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 1e2571b5..162786c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -40,6 +40,7 @@
     private var conversationIcon: View? = null
     private var conversationBadge: View? = null
     private var expandButton: View? = null
+    private lateinit var expandButtonContainer: View
     private var messagingLinearLayout: MessagingLinearLayout? = null
 
     init {
@@ -56,6 +57,8 @@
                 com.android.internal.R.id.conversation_icon_badge)
         expandButton = conversationLayout.requireViewById(
                 com.android.internal.R.id.expand_button)
+        expandButtonContainer = conversationLayout.requireViewById(
+                com.android.internal.R.id.expand_button_container)
     }
 
     override fun onContentUpdated(row: ExpandableNotificationRow) {
@@ -90,6 +93,14 @@
         conversationLayout.updateExpandability(expandable, onClickListener)
     }
 
+    override fun disallowSingleClick(x: Float, y: Float): Boolean {
+        if (expandButtonContainer.visibility == View.VISIBLE
+                && isOnView(expandButtonContainer, x, y)) {
+            return true
+        }
+        return super.disallowSingleClick(x, y)
+    }
+
     override fun getMinLayoutHeight(): Int {
         if (mActionsContainer != null && mActionsContainer.visibility != View.GONE) {
             return minHeightWithActions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
index d41f5af..2d99ab1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java
@@ -58,7 +58,6 @@
     private TextView mText;
     protected View mActionsContainer;
     private ImageView mReplyAction;
-    private Rect mTmpRect = new Rect();
 
     private int mContentHeight;
     private int mMinHeightHint;
@@ -271,18 +270,6 @@
         return super.disallowSingleClick(x, y);
     }
 
-    private boolean isOnView(View view, float x, float y) {
-        View searchView = (View) view.getParent();
-        while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) {
-            searchView.getHitRect(mTmpRect);
-            x -= mTmpRect.left;
-            y -= mTmpRect.top;
-            searchView = (View) searchView.getParent();
-        }
-        view.getHitRect(mTmpRect);
-        return mTmpRect.contains((int) x,(int) y);
-    }
-
     @Override
     public void onContentUpdated(ExpandableNotificationRow row) {
         // Reinspect the notification. Before the super call, because the super call also updates
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index c834e4b..46d7d93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -24,6 +24,7 @@
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -49,6 +50,7 @@
 
     protected final View mView;
     protected final ExpandableNotificationRow mRow;
+    private final Rect mTmpRect = new Rect();
 
     protected int mBackgroundColor = 0;
 
@@ -305,6 +307,26 @@
         return false;
     }
 
+    /**
+     * Is a given x and y coordinate on a view.
+     *
+     * @param view the view to be checked
+     * @param x the x coordinate, relative to the ExpandableNotificationRow
+     * @param y the y coordinate, relative to the ExpandableNotificationRow
+     * @return {@code true} if it is on the view
+     */
+    protected boolean isOnView(View view, float x, float y) {
+        View searchView = (View) view.getParent();
+        while (searchView != null && !(searchView instanceof ExpandableNotificationRow)) {
+            searchView.getHitRect(mTmpRect);
+            x -= mTmpRect.left;
+            y -= mTmpRect.top;
+            searchView = (View) searchView.getParent();
+        }
+        view.getHitRect(mTmpRect);
+        return mTmpRect.contains((int) x,(int) y);
+    }
+
     public int getMinLayoutHeight() {
         return 0;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index d7c88e3..2c17764 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -412,7 +412,7 @@
      * @param callback
      * @return whether the list order has changed
      */
-    public boolean applyChildOrder(List<ExpandableNotificationRow> childOrder,
+    public boolean applyChildOrder(List<? extends NotificationListItem> childOrder,
             VisualStabilityManager visualStabilityManager,
             VisualStabilityManager.Callback callback) {
         if (childOrder == null) {
@@ -421,7 +421,7 @@
         boolean result = false;
         for (int i = 0; i < mChildren.size() && i < childOrder.size(); i++) {
             ExpandableNotificationRow child = mChildren.get(i);
-            ExpandableNotificationRow desiredChild = childOrder.get(i);
+            ExpandableNotificationRow desiredChild = (ExpandableNotificationRow) childOrder.get(i);
             if (child != desiredChild) {
                 if (visualStabilityManager.canReorderNotification(desiredChild)) {
                     mChildren.remove(desiredChild);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 15cc72c..c4a720c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -18,12 +18,14 @@
 
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 
+import android.annotation.NonNull;
 import android.view.View;
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.SimpleNotificationListContainer;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -33,7 +35,7 @@
  * notification views added and removed from it, and will manage displaying them to the user.
  */
 public interface NotificationListContainer extends ExpandableView.OnHeightChangedListener,
-        VisibilityLocationProvider {
+        VisibilityLocationProvider, SimpleNotificationListContainer {
 
     /**
      * Called when a child is being transferred.
@@ -186,4 +188,10 @@
     }
 
     default void setWillExpand(boolean willExpand) {};
+
+    /**
+     * Remove a list item from the container
+     * @param v the item to remove
+     */
+    void removeListItem(@NonNull NotificationListItem v);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java
new file mode 100644
index 0000000..8991abe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListItem.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.View;
+
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.List;
+
+/**
+* A NotificationListItem is a child view of the notification list that can yield a
+* NotificationEntry when asked. I.e., it's an ExpandableNotificationRow but doesn't require us
+* to strictly rely on ExpandableNotificationRow as our consumed type
+ */
+public interface NotificationListItem {
+    /** @return entry for this item */
+    @NonNull
+    NotificationEntry getEntry();
+
+    /** @return true if the blocking helper is showing */
+    boolean isBlockingHelperShowing();
+
+    /** @return true if this list item is a summary with children */
+    boolean isSummaryWithChildren();
+
+    // This generic is kind of ugly - we should change this once the old VHM is gone
+    /** @return list of the children of this item */
+    List<? extends NotificationListItem> getNotificationChildren();
+
+    /** remove all children from this list item */
+    void removeAllChildren();
+
+    /** remove particular child */
+    void removeChildNotification(NotificationListItem child);
+
+    /** add an item as a child */
+    void addChildNotification(NotificationListItem child, int childIndex);
+
+    /** Update the order of the children with the new list */
+    boolean applyChildOrder(
+            List<? extends NotificationListItem> childOrderList,
+            VisualStabilityManager vsm,
+            @Nullable VisualStabilityManager.Callback callback);
+
+    /** return the associated view for this list item */
+    @NonNull
+    View getView();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index cfcbd88..4d4a2ded 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3323,11 +3323,21 @@
     }
 
     @Override
+    public void notifyGroupChildRemoved(View child, ViewGroup parent) {
+        notifyGroupChildRemoved((ExpandableView) child, parent);
+    }
+
+    @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void notifyGroupChildAdded(ExpandableView row) {
         onViewAddedInternal(row);
     }
 
+    @Override
+    public void notifyGroupChildAdded(View view) {
+        notifyGroupChildAdded((ExpandableView) view);
+    }
+
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void setAnimationsEnabled(boolean animationsEnabled) {
         mAnimationsEnabled = animationsEnabled;
@@ -5137,11 +5147,22 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    public void removeListItem(NotificationListItem v) {
+        removeContainerView(v.getView());
+    }
+
+    @Override
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void addContainerView(View v) {
         Assert.isMainThread();
         addView(v);
     }
 
+    @Override
+    public void addListItem(NotificationListItem v) {
+        addContainerView(v.getView());
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void runAfterAnimationFinished(Runnable runnable) {
         mAnimationFinishedRunnables.add(runnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 0996ff2..14442e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -453,9 +453,10 @@
                         needsAnimation = false;
                     }
                     NotificationEntry entry = row.getEntry();
-                    StatusBarIconView icon = entry.icon;
-                    if (entry.centeredIcon != null && entry.centeredIcon.getParent() != null) {
-                        icon = entry.centeredIcon;
+                    StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
+                    final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
+                    if (centeredIcon != null && centeredIcon.getParent() != null) {
+                        icon = centeredIcon;
                     }
                     if (icon.getParent() != null) {
                         icon.getLocationOnScreen(mTmpLocation);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 745843d..adca10f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -44,6 +44,7 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
+import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController;
@@ -99,8 +100,10 @@
     private final Region mExcludeRegion = new Region();
     private final Region mUnrestrictedExcludeRegion = new Region();
 
-    // The edge width where touch down is allowed
-    private int mEdgeWidth;
+    // The left side edge width where touch down is allowed
+    private int mEdgeWidthLeft;
+    // The right side edge width where touch down is allowed
+    private int mEdgeWidthRight;
     // The bottom gesture area height
     private int mBottomGestureHeight;
     // The slop to distinguish between horizontal and vertical motion
@@ -127,6 +130,8 @@
     private int mRightInset;
     private int mSysUiFlags;
 
+    private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+
     private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
             new NavigationEdgeBackPlugin.BackCallback() {
                 @Override
@@ -174,13 +179,17 @@
         mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
                 ViewConfiguration.getLongPressTimeout());
 
+        mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
+                mContext.getMainThreadHandler(), mContext, () -> updateCurrentUserResources(res));
+
         updateCurrentUserResources(res);
         sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
     }
 
     public void updateCurrentUserResources(Resources res) {
-        mEdgeWidth = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.config_backGestureInset);
+        mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
+        mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
+
         mBottomGestureHeight = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.navigation_bar_gesture_height);
     }
@@ -236,6 +245,7 @@
         }
 
         if (!mIsEnabled) {
+            mGestureNavigationSettingsObserver.unregister();
             mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
             mPluginManager.removePluginListener(this);
 
@@ -248,6 +258,7 @@
             }
 
         } else {
+            mGestureNavigationSettingsObserver.register();
             updateDisplaySize();
             mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
                     mContext.getMainThreadHandler());
@@ -321,7 +332,8 @@
 
     private boolean isWithinTouchRegion(int x, int y) {
         // Disallow if too far from the edge
-        if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
+        if (x > mEdgeWidthLeft + mLeftInset
+                && x < (mDisplaySize.x - mEdgeWidthRight - mRightInset)) {
             return false;
         }
 
@@ -364,7 +376,7 @@
         if (action == MotionEvent.ACTION_DOWN) {
             // Verify if this is in within the touch region and we aren't in immersive mode, and
             // either the bouncer is showing or the notification panel is hidden
-            mIsOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset;
+            mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
             mInRejectedExclusion = false;
             mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
                     && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
@@ -461,7 +473,8 @@
         pw.println("  mExcludeRegion=" + mExcludeRegion);
         pw.println("  mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
         pw.println("  mIsAttached=" + mIsAttached);
-        pw.println("  mEdgeWidth=" + mEdgeWidth);
+        pw.println("  mEdgeWidthLeft=" + mEdgeWidthLeft);
+        pw.println("  mEdgeWidthRight=" + mEdgeWidthRight);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index c39ee3a..51c02c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -269,7 +269,7 @@
             }
             updateIsolatedIconLocation(false /* requireUpdate */);
             mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
-                    : newEntry.icon, animateIsolation);
+                    : newEntry.getIcons().getStatusBarIcon(), animateIsolation);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index c282cb8..0b747f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -114,8 +114,9 @@
                     mInitialTouchY = y;
                     int startHeight = (int) (mPickedChild.getActualHeight()
                                                 + mPickedChild.getTranslationY());
-                    mPanel.setPanelScrimMinFraction((float) startHeight
-                            / mPanel.getMaxPanelHeight());
+                    float maxPanelHeight = mPanel.getMaxPanelHeight();
+                    mPanel.setPanelScrimMinFraction(maxPanelHeight > 0f
+                            ? (float) startHeight / maxPanelHeight : 0f);
                     mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
                     mPanel.startExpandingFromPeek();
                     // This call needs to be after the expansion start otherwise we will get a
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index b119f0b..31266db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -551,14 +551,15 @@
     public void setWindowState(
             int displayId, @WindowType int window, @WindowVisibleState int state) {
         if (displayId == mDisplayId
-                && mNavigationBarView != null
                 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
                 && mNavigationBarWindowState != state) {
             mNavigationBarWindowState = state;
+            updateSystemUiStateFlags(-1);
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
 
-            updateSystemUiStateFlags(-1);
-            mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+            if (mNavigationBarView != null) {
+                mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+            }
         }
     }
 
@@ -1219,6 +1220,7 @@
             @Override
             public void onViewDetachedFromWindow(View v) {
                 FragmentHostManager.removeAndDestroy(v);
+                navigationBarView.removeOnAttachStateChangeListener(this);
             }
         });
         context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index 43ac4cf..d24ccf3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -28,6 +28,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.om.IOverlayManager;
+import android.content.om.OverlayInfo;
 import android.content.pm.PackageManager;
 import android.content.res.ApkAssets;
 import android.os.PatternMatcher;
@@ -172,6 +173,9 @@
     public void updateCurrentInteractionMode(boolean notify) {
         mCurrentUserContext = getCurrentUserContext();
         int mode = getCurrentInteractionMode(mCurrentUserContext);
+        if (mode == NAV_BAR_MODE_GESTURAL) {
+            switchToDefaultGestureNavOverlayIfNecessary();
+        }
         mUiBgExecutor.execute(() -> {
             Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
                     Secure.NAVIGATION_MODE, String.valueOf(mode));
@@ -277,6 +281,34 @@
         }
     }
 
+    private void switchToDefaultGestureNavOverlayIfNecessary() {
+        final int userId = mCurrentUserContext.getUserId();
+        try {
+            final IOverlayManager om = mOverlayManager;
+            final OverlayInfo info = om.getOverlayInfo(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
+            if (info != null && !info.isEnabled()) {
+                // Enable the default gesture nav overlay, and move the back gesture inset scale to
+                // Settings.Secure for left and right sensitivity.
+                final int curInset = mCurrentUserContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.config_backGestureInset);
+                om.setEnabledExclusiveInCategory(NAV_BAR_MODE_GESTURAL_OVERLAY, userId);
+                final int defInset = mCurrentUserContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.config_backGestureInset);
+
+                final float scale = defInset == 0 ? 1.0f : ((float) curInset) / defInset;
+                Settings.Secure.putFloat(mCurrentUserContext.getContentResolver(),
+                        Secure.BACK_GESTURE_INSET_SCALE_LEFT, scale);
+                Settings.Secure.putFloat(mCurrentUserContext.getContentResolver(),
+                        Secure.BACK_GESTURE_INSET_SCALE_RIGHT, scale);
+                if (DEBUG) {
+                    Log.v(TAG, "Moved back sensitivity for user " + userId + " to scale " + scale);
+                }
+            }
+        } catch (SecurityException | IllegalStateException | RemoteException e) {
+            Log.e(TAG, "Failed to switch to default gesture nav overlay for user " + userId);
+        }
+    }
+
     public void setModeOverlay(String overlayPkg, int userId) {
         mUiBgExecutor.execute(() -> {
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
deleted file mode 100644
index 5d8044f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.annotation.IntDef;
-import android.content.ComponentCallbacks;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.Settings;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different
- * prototypes to run in the system. The class will handle communication changes from the settings
- * app and call back to listeners.
- */
-public class NavigationPrototypeController extends ContentObserver implements ComponentCallbacks {
-    private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
-    private static final String HIDE_HOME_BUTTON_SETTING = "quickstepcontroller_hidehome";
-    private static final String PROTOTYPE_ENABLED = "prototype_enabled";
-
-    public static final String EDGE_SENSITIVITY_WIDTH_SETTING =
-            "quickstepcontroller_edge_width_sensitivity";
-    private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
-    public static final String NAV_COLOR_ADAPT_ENABLE_SETTING = "navbar_color_adapt_enable";
-    public static final String SHOW_HOME_HANDLE_SETTING = "quickstepcontroller_showhandle";
-    public static final String ENABLE_ASSISTANT_GESTURE = "ENABLE_ASSISTANT_GESTURE";
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK,
-            ACTION_QUICKSWITCH, ACTION_NOTHING, ACTION_ASSISTANT})
-    @interface GestureAction {}
-    static final int ACTION_DEFAULT = 0;
-    static final int ACTION_QUICKSTEP = 1;
-    static final int ACTION_QUICKSCRUB = 2;
-    static final int ACTION_BACK = 3;
-    static final int ACTION_QUICKSWITCH = 4;
-    static final int ACTION_NOTHING = 5;
-    static final int ACTION_ASSISTANT = 6;
-
-    private OnPrototypeChangedListener mListener;
-
-    /**
-     * Each index corresponds to a different action set in QuickStepController
-     * {@see updateSwipeLTRBackSetting}
-     */
-    private int[] mActionMap = new int[6];
-
-    private final Context mContext;
-
-    public NavigationPrototypeController(Context context) {
-        super(new Handler());
-        mContext = context;
-        updateSwipeLTRBackSetting();
-    }
-
-    public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Observe all the settings to react to from prototype settings
-     */
-    public void register() {
-        registerObserver(HIDE_BACK_BUTTON_SETTING);
-        registerObserver(HIDE_HOME_BUTTON_SETTING);
-        registerObserver(GESTURE_MATCH_SETTING);
-        registerObserver(NAV_COLOR_ADAPT_ENABLE_SETTING);
-        registerObserver(SHOW_HOME_HANDLE_SETTING);
-        registerObserver(ENABLE_ASSISTANT_GESTURE);
-        mContext.registerComponentCallbacks(this);
-    }
-
-    /**
-     * Disable observing settings to react to from prototype settings
-     */
-    public void unregister() {
-        mContext.getContentResolver().unregisterContentObserver(this);
-        mContext.unregisterComponentCallbacks(this);
-    }
-
-    @Override
-    public void onChange(boolean selfChange, Uri uri) {
-        super.onChange(selfChange, uri);
-        if (!selfChange && mListener != null) {
-            final String path = uri.getPath();
-            if (path.endsWith(GESTURE_MATCH_SETTING)) {
-                // Get the settings gesture map corresponding to each action
-                // {@see updateSwipeLTRBackSetting}
-                updateSwipeLTRBackSetting();
-                mListener.onGestureRemap(mActionMap);
-            } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
-                mListener.onBackButtonVisibilityChanged(
-                        !getGlobalBool(HIDE_BACK_BUTTON_SETTING, false));
-            } else if (path.endsWith(HIDE_HOME_BUTTON_SETTING)) {
-                mListener.onHomeButtonVisibilityChanged(!hideHomeButton());
-            } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
-                mListener.onColorAdaptChanged(mContext.getDisplayId() == DEFAULT_DISPLAY);
-            } else if (path.endsWith(SHOW_HOME_HANDLE_SETTING)) {
-                mListener.onHomeHandleVisiblilityChanged(showHomeHandle());
-            } else if (path.endsWith(ENABLE_ASSISTANT_GESTURE)) {
-                mListener.onAssistantGestureEnabled(isAssistantGestureEnabled());
-            }
-        }
-    }
-
-    /**
-     * @return the width for edge swipe
-     */
-    public int getEdgeSensitivityWidth() {
-        return mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.config_backGestureInset);
-    }
-
-    /**
-     * @return full screen height
-     */
-    public int getEdgeSensitivityHeight() {
-        final Point size = new Point();
-        mContext.getDisplay().getRealSize(size);
-        return size.y;
-    }
-
-    public boolean isEnabled() {
-        return getGlobalBool(PROTOTYPE_ENABLED, false);
-    }
-
-    /**
-     * Retrieve the action map to apply to the quick step controller
-     * @return an action map
-     */
-    int[] getGestureActionMap() {
-        return mActionMap;
-    }
-
-    /**
-     * @return if home button should be invisible
-     */
-    boolean hideHomeButton() {
-        return getGlobalBool(HIDE_HOME_BUTTON_SETTING, false /* default */);
-    }
-
-    boolean showHomeHandle() {
-        return getGlobalBool(SHOW_HOME_HANDLE_SETTING, false /* default */);
-    }
-
-    boolean isAssistantGestureEnabled() {
-        return getGlobalBool(ENABLE_ASSISTANT_GESTURE, false /* default */);
-    }
-
-
-    /**
-     * Since Settings.Global cannot pass arrays, use a string to represent each character as a
-     * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
-     * Number: [up] [down] [left] [right]
-     */
-    private void updateSwipeLTRBackSetting() {
-        String value = Settings.Global.getString(mContext.getContentResolver(),
-                GESTURE_MATCH_SETTING);
-        if (value != null) {
-            for (int i = 0; i < mActionMap.length; ++i) {
-                mActionMap[i] = Character.getNumericValue(value.charAt(i));
-            }
-        }
-    }
-
-    private boolean getGlobalBool(String name, boolean defaultVal) {
-        return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal ? 1 : 0) == 1;
-    }
-
-    private int getGlobalInt(String name, int defaultVal) {
-        return Settings.Global.getInt(mContext.getContentResolver(), name, defaultVal);
-    }
-
-    private void registerObserver(String name) {
-        mContext.getContentResolver()
-                .registerContentObserver(Settings.Global.getUriFor(name), false, this);
-    }
-
-    private static int convertDpToPixel(float dp) {
-        return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        if (mListener != null) {
-            mListener.onEdgeSensitivityChanged(getEdgeSensitivityWidth(),
-                    getEdgeSensitivityHeight());
-        }
-    }
-
-    @Override
-    public void onLowMemory() {
-    }
-
-    public interface OnPrototypeChangedListener {
-        void onGestureRemap(@GestureAction int[] actions);
-        void onBackButtonVisibilityChanged(boolean visible);
-        void onHomeButtonVisibilityChanged(boolean visible);
-        void onHomeHandleVisiblilityChanged(boolean visible);
-        void onColorAdaptChanged(boolean enabled);
-        void onEdgeSensitivityChanged(int width, int height);
-        void onAssistantGestureEnabled(boolean enabled);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 77337e9..ccf6707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -398,8 +398,7 @@
         NotificationGroup group = mGroupMap.get(groupKey);
         //TODO: see if this can become an Entry
         return group == null ? null
-                : group.summary == null ? null
-                        : group.summary;
+                : group.summary;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index b09ccff..f58cce5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -253,8 +253,8 @@
             boolean hidePulsing, boolean onlyShowCenteredIcon) {
 
         final boolean isCenteredNotificationIcon = mCenteredIconView != null
-                && entry.centeredIcon != null
-                && Objects.equals(entry.centeredIcon, mCenteredIconView);
+                && entry.getIcons().getCenteredIcon() != null
+                && Objects.equals(entry.getIcons().getCenteredIcon(), mCenteredIconView);
         if (onlyShowCenteredIcon) {
             return isCenteredNotificationIcon;
         }
@@ -307,7 +307,7 @@
     }
 
     private void updateShelfIcons() {
-        updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons,
+        updateIconsForLayout(entry -> entry.getIcons().getShelfIcon(), mShelfIcons,
                 true /* showAmbient */,
                 true /* showLowPriority */,
                 false /* hideDismissed */,
@@ -319,7 +319,7 @@
     }
 
     public void updateStatusBarIcons() {
-        updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
+        updateIconsForLayout(entry -> entry.getIcons().getStatusBarIcon(), mNotificationIcons,
                 false /* showAmbient */,
                 mShowLowPriority,
                 true /* hideDismissed */,
@@ -331,7 +331,7 @@
     }
 
     private void updateCenterIcon() {
-        updateIconsForLayout(entry -> entry.centeredIcon, mCenteredIcon,
+        updateIconsForLayout(entry -> entry.getIcons().getCenteredIcon(), mCenteredIcon,
                 false /* showAmbient */,
                 true /* showLowPriority */,
                 false /* hideDismissed */,
@@ -343,7 +343,7 @@
     }
 
     public void updateAodNotificationIcons() {
-        updateIconsForLayout(entry -> entry.aodIcon, mAodIcons,
+        updateIconsForLayout(entry -> entry.getIcons().getAodIcon(), mAodIcons,
                 false /* showAmbient */,
                 true /* showLowPriority */,
                 true /* hideDismissed */,
@@ -517,7 +517,7 @@
      * Shows the icon view given in the center.
      */
     public void showIconCentered(NotificationEntry entry) {
-        StatusBarIconView icon = entry == null ? null :  entry.centeredIcon;
+        StatusBarIconView icon = entry == null ? null : entry.getIcons().getCenteredIcon();
         if (!Objects.equals(mCenteredIconView, icon)) {
             mCenteredIconView = icon;
             updateNotificationIcons();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index c61d7bb..f9726d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -1844,7 +1844,14 @@
         } else {
             maxHeight = calculatePanelHeightShade();
         }
-        maxHeight = Math.max(maxHeight, min);
+        maxHeight = Math.max(min, maxHeight);
+        if (maxHeight == 0) {
+            Log.wtf(TAG, "maxPanelHeight is 0. getOverExpansionAmount(): "
+                    + getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: "
+                    + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
+                    + calculatePanelHeightShade() + ", mStatusBarMinHeight = "
+                    + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight);
+        }
         return maxHeight;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 75f5023..e1e679f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -309,7 +309,8 @@
         return !state.mForceCollapsed && (state.isKeyguardShowingAndNotOccluded()
                 || state.mPanelVisible || state.mKeyguardFadingAway || state.mBouncerShowing
                 || state.mHeadsUpShowing || state.mBubblesShowing
-                || state.mScrimsVisibility != ScrimController.TRANSPARENT);
+                || state.mScrimsVisibility != ScrimController.TRANSPARENT)
+                || state.mBackgroundBlurRadius > 0;
     }
 
     private void applyFitsSystemWindows(State state) {
@@ -478,6 +479,19 @@
         apply(mCurrentState);
     }
 
+    /**
+     * Current blur level, controller by
+     * {@link com.android.systemui.statusbar.NotificationShadeDepthController}.
+     * @param backgroundBlurRadius Radius in pixels.
+     */
+    public void setBackgroundBlurRadius(int backgroundBlurRadius) {
+        if (mCurrentState.mBackgroundBlurRadius == backgroundBlurRadius) {
+            return;
+        }
+        mCurrentState.mBackgroundBlurRadius = backgroundBlurRadius;
+        apply(mCurrentState);
+    }
+
     public void setHeadsUpShowing(boolean showing) {
         mCurrentState.mHeadsUpShowing = showing;
         apply(mCurrentState);
@@ -665,6 +679,7 @@
         boolean mForcePluginOpen;
         boolean mDozing;
         int mScrimsVisibility;
+        int mBackgroundBlurRadius;
 
         private boolean isKeyguardShowingAndNotOccluded() {
             return mKeyguardShowing && !mKeyguardOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 8d8c8da..c106518 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static java.lang.Float.isNaN;
+
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -161,6 +163,9 @@
      *                 fraction as the panel also might be expanded if the fraction is 0
      */
     public void panelExpansionChanged(float frac, boolean expanded) {
+        if (isNaN(frac)) {
+            throw new IllegalArgumentException("frac cannot be NaN");
+        }
         boolean fullyClosed = true;
         boolean fullyOpened = false;
         if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 1359f74..1c1e7c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -18,6 +18,8 @@
 
 import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
 
+import static java.lang.Float.isNaN;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -40,6 +42,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.leak.RotationUtils;
 
 import java.util.Objects;
 
@@ -47,7 +50,6 @@
     private static final String TAG = "PhoneStatusBarView";
     private static final boolean DEBUG = StatusBar.DEBUG;
     private static final boolean DEBUG_GESTURES = false;
-    private static final int NO_VALUE = Integer.MIN_VALUE;
     private final CommandQueue mCommandQueue;
 
     StatusBar mBar;
@@ -64,13 +66,14 @@
         }
     };
     private DarkReceiver mBattery;
-    private int mLastOrientation;
+    private int mRotationOrientation;
     @Nullable
     private View mCenterIconSpace;
     @Nullable
     private View mCutoutSpace;
     @Nullable
     private DisplayCutout mDisplayCutout;
+    private int mStatusBarHeight;
 
     /**
      * Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -107,7 +110,7 @@
         super.onAttachedToWindow();
         // Always have Battery meters in the status bar observe the dark/light modes.
         Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
-        if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
+        if (updateOrientationAndCutout()) {
             updateLayoutForCutout();
         }
     }
@@ -124,7 +127,7 @@
         super.onConfigurationChanged(newConfig);
 
         // May trigger cutout space layout-ing
-        if (updateOrientationAndCutout(newConfig.orientation)) {
+        if (updateOrientationAndCutout()) {
             updateLayoutForCutout();
             requestLayout();
         }
@@ -132,7 +135,7 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (updateOrientationAndCutout(mLastOrientation)) {
+        if (updateOrientationAndCutout()) {
             updateLayoutForCutout();
             requestLayout();
         }
@@ -140,17 +143,14 @@
     }
 
     /**
-     *
-     * @param newOrientation may pass NO_VALUE for no change
      * @return boolean indicating if we need to update the cutout location / margins
      */
-    private boolean updateOrientationAndCutout(int newOrientation) {
+    private boolean updateOrientationAndCutout() {
         boolean changed = false;
-        if (newOrientation != NO_VALUE) {
-            if (mLastOrientation != newOrientation) {
-                changed = true;
-                mLastOrientation = newOrientation;
-            }
+        int newRotation = RotationUtils.getExactRotation(mContext);
+        if (newRotation != mRotationOrientation) {
+            changed = true;
+            mRotationOrientation = newRotation;
         }
 
         if (!Objects.equals(getRootWindowInsets().getDisplayCutout(), mDisplayCutout)) {
@@ -256,6 +256,9 @@
 
     @Override
     public void panelScrimMinFractionChanged(float minFraction) {
+        if (isNaN(minFraction)) {
+            throw new IllegalArgumentException("minFraction cannot be NaN");
+        }
         if (mMinFraction != minFraction) {
             mMinFraction = minFraction;
             updateScrimFraction();
@@ -293,17 +296,16 @@
         final int waterfallTopInset =
                 mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
-        layoutParams.height =
-                getResources().getDimensionPixelSize(R.dimen.status_bar_height) - waterfallTopInset;
+        mStatusBarHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_height);
+        layoutParams.height = mStatusBarHeight - waterfallTopInset;
         setLayoutParams(layoutParams);
     }
 
     private void updateLayoutForCutout() {
         updateStatusBarHeight();
-        Pair<Integer, Integer> cornerCutoutMargins =
-                StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay());
-        updateCutoutLocation(cornerCutoutMargins);
-        updateSafeInsets(cornerCutoutMargins);
+        updateCutoutLocation(StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay()));
+        updateSafeInsets(StatusBarWindowView.statusBarCornerCutoutMargins(mDisplayCutout,
+                getDisplay(), mRotationOrientation, mStatusBarHeight));
     }
 
     private void updateCutoutLocation(Pair<Integer, Integer> cornerCutoutMargins) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 22bf513..760a6d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -33,6 +33,8 @@
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
 
+import com.android.systemui.util.leak.RotationUtils;
+
 /**
  * Status bar view.
  */
@@ -52,17 +54,13 @@
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
         final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
-        mLeftInset = 0;
-        mRightInset = 0;
+        mLeftInset = insets.left;
+        mRightInset = insets.right;
         mTopInset = 0;
         DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
         if (displayCutout != null) {
             mTopInset = displayCutout.getWaterfallInsets().top;
-            mLeftInset = displayCutout.getSafeInsetLeft();
-            mRightInset = displayCutout.getSafeInsetRight();
         }
-        mLeftInset = Math.max(insets.left, mLeftInset);
-        mRightInset = Math.max(insets.right, mRightInset);
         applyMargins();
         return windowInsets;
     }
@@ -100,44 +98,33 @@
             return new Pair<>(roundedCornerContentPadding, roundedCornerContentPadding);
         }
 
-        // compute the padding needed for corner cutout.
-        final int leftMargin = cutout.getSafeInsetLeft();
-        final int rightMargin = cutout.getSafeInsetRight();
+        // padding needed for corner cutout.
         int leftCornerCutoutPadding = 0;
         int rightCornerCutoutPadding = 0;
         if (cornerCutoutPadding != null) {
-            if (cornerCutoutPadding.first > leftMargin) {
-                leftCornerCutoutPadding = cornerCutoutPadding.first - leftMargin;
-            }
-            if (cornerCutoutPadding.second > rightMargin) {
-                rightCornerCutoutPadding = cornerCutoutPadding.second - rightMargin;
-            }
-        }
-
-        // compute the padding needed for rounded corner
-        int leftRoundedCornerPadding = 0;
-        int rightRoundedCornerPadding = 0;
-        if (roundedCornerContentPadding > leftMargin) {
-            leftRoundedCornerPadding = roundedCornerContentPadding - leftMargin;
-        }
-        if (roundedCornerContentPadding > rightMargin) {
-            rightRoundedCornerPadding = roundedCornerContentPadding - rightMargin;
+            leftCornerCutoutPadding = cornerCutoutPadding.first;
+            rightCornerCutoutPadding = cornerCutoutPadding.second;
         }
 
         return new Pair<>(
-                Math.max(leftCornerCutoutPadding, leftRoundedCornerPadding),
-                Math.max(rightCornerCutoutPadding, rightRoundedCornerPadding));
+                Math.max(leftCornerCutoutPadding, roundedCornerContentPadding),
+                Math.max(rightCornerCutoutPadding, roundedCornerContentPadding));
     }
 
+
     /**
-     * Compute the corner cutout margins
-     *
-     * @param cutout
-     * @param display
-     * @return
+     * Compute the corner cutout margins in portrait mode
      */
     public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
             Display display) {
+        return statusBarCornerCutoutMargins(cutout, display, RotationUtils.ROTATION_NONE, 0);
+    }
+
+    /**
+     * Compute the corner cutout margins in the given orientation (exactRotation)
+     */
+    public static Pair<Integer, Integer> statusBarCornerCutoutMargins(DisplayCutout cutout,
+            Display display, int exactRotation, int statusBarHeight) {
         if (cutout == null) {
             return null;
         }
@@ -145,14 +132,33 @@
         display.getRealSize(size);
 
         Rect bounds = new Rect();
-        boundsFromDirection(cutout, Gravity.TOP, bounds);
+        switch (exactRotation) {
+            case RotationUtils.ROTATION_LANDSCAPE:
+                boundsFromDirection(cutout, Gravity.LEFT, bounds);
+                break;
+            case RotationUtils.ROTATION_SEASCAPE:
+                boundsFromDirection(cutout, Gravity.RIGHT, bounds);
+                break;
+            case RotationUtils.ROTATION_NONE:
+                boundsFromDirection(cutout, Gravity.TOP, bounds);
+                break;
+            case RotationUtils.ROTATION_UPSIDE_DOWN:
+                // we assume the cutout is always on top in portrait mode
+                return null;
+        }
+
+        if (statusBarHeight >= 0 && bounds.top > statusBarHeight) {
+            return null;
+        }
 
         if (bounds.left <= 0) {
             return new Pair<>(bounds.right, 0);
         }
+
         if (bounds.right >= size.x) {
             return new Pair<>(0, size.x - bounds.left);
         }
+
         return null;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 24b9685..a81189e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -53,6 +53,16 @@
     boolean isAodPowerSave();
 
     /**
+     * Returns {@code true} if reverse is supported.
+     */
+    default boolean isReverseSupported() { return false; }
+
+    /**
+     * Returns {@code true} if reverse is on.
+     */
+    default boolean isReverseOn() { return false; }
+
+    /**
      * Set reverse state.
      * @param isReverse true if turn on reverse, false otherwise
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 35954d8..496bf68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -99,7 +99,6 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
         filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
-        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
         filter.addAction(ACTION_LEVEL_TEST);
         mBroadcastDispatcher.registerReceiver(this, filter);
     }
@@ -155,8 +154,6 @@
             fireBatteryLevelChanged();
         } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
             updatePowerSave();
-        } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) {
-            setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
         } else if (action.equals(ACTION_LEVEL_TEST)) {
             mTestmode = true;
             mMainHandler.post(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 7d532a8..38c165b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -65,7 +65,6 @@
             new TetheringManager.TetheringEventCallback() {
                 @Override
                 public void onTetheringSupported(boolean supported) {
-                    super.onTetheringSupported(supported);
                     if (mIsTetheringSupported != supported) {
                         mIsTetheringSupported = supported;
                         fireHotspotAvailabilityChanged();
@@ -75,7 +74,6 @@
                 @Override
                 public void onTetherableInterfaceRegexpsChanged(
                         TetheringManager.TetheringInterfaceRegexps reg) {
-                    super.onTetherableInterfaceRegexpsChanged(reg);
                     final boolean newValue = reg.getTetherableWifiRegexs().size() != 0;
                     if (mHasTetherableWifiRegexs != newValue) {
                         mHasTetherableWifiRegexs = newValue;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 2f1b160..5411839 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -49,6 +49,8 @@
 public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
     private static final String TAG = "DisplayImeController";
 
+    private static final boolean DEBUG = false;
+
     public static final int ANIMATION_DURATION_SHOW_MS = 275;
     public static final int ANIMATION_DURATION_HIDE_MS = 340;
     public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
@@ -209,6 +211,7 @@
             if ((types & WindowInsets.Type.ime()) == 0) {
                 return;
             }
+            if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
             startAnimation(true /* show */);
         }
 
@@ -217,6 +220,7 @@
             if ((types & WindowInsets.Type.ime()) == 0) {
                 return;
             }
+            if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
             startAnimation(false /* show */);
         }
 
@@ -241,6 +245,11 @@
                 return;
             }
             mHandler.post(() -> {
+                if (DEBUG) {
+                    Slog.d(TAG, "Run startAnim  show:" + show + "  was:"
+                            + (mAnimationDirection == DIRECTION_SHOW ? "SHOW"
+                            : (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE")));
+                }
                 if ((mAnimationDirection == DIRECTION_SHOW && show)
                         || (mAnimationDirection == DIRECTION_HIDE && !show)) {
                     return;
@@ -289,6 +298,11 @@
                     public void onAnimationStart(Animator animation) {
                         SurfaceControl.Transaction t = mTransactionPool.acquire();
                         t.setPosition(mImeSourceControl.getLeash(), x, startY);
+                        if (DEBUG) {
+                            Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
+                                    + imeTop(imeSource, hiddenY) + "->" + imeTop(imeSource, shownY)
+                                    + " showing:" + (mAnimationDirection == DIRECTION_SHOW));
+                        }
                         dispatchStartPositioning(mDisplayId, imeTop(imeSource, hiddenY),
                                 imeTop(imeSource, shownY), mAnimationDirection == DIRECTION_SHOW,
                                 t);
@@ -304,6 +318,7 @@
                     }
                     @Override
                     public void onAnimationEnd(Animator animation) {
+                        if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
                         SurfaceControl.Transaction t = mTransactionPool.acquire();
                         if (!mCancelled) {
                             t.setPosition(mImeSourceControl.getLeash(), x, endY);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 1b34b3d..b6537bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -23,6 +23,8 @@
 
 import static com.android.systemui.ScreenDecorations.rectsToRegion;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
@@ -42,6 +44,7 @@
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.graphics.drawable.VectorDrawable;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
@@ -142,6 +145,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         // no cutout
         doReturn(null).when(mScreenDecorations).getCutout();
@@ -161,6 +166,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius, 20);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 20);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         // no cutout
         doReturn(null).when(mScreenDecorations).getCutout();
@@ -182,6 +189,67 @@
     }
 
     @Test
+    public void testRoundingRadius_NoCutout() {
+        final int testRadius = 1;
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, testRadius);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_top, testRadius);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius_bottom, testRadius);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
+
+        // no cutout
+        doReturn(null).when(mScreenDecorations).getCutout();
+
+        mScreenDecorations.start();
+        // Size of corner view should same as rounded_corner_radius{_top|_bottom}
+        assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(testRadius);
+        assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(testRadius);
+        assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(testRadius);
+    }
+
+    @Test
+    public void testRoundingMultipleRadius_NoCutout() {
+        final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
+        final int multipleRadiusSize = Math.max(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.dimen.rounded_corner_radius, 9999);
+        mContext.getOrCreateTestableResources()
+                .addOverride(dimen.rounded_corner_content_padding, 9999);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, true);
+
+        // no cutout
+        doReturn(null).when(mScreenDecorations).getCutout();
+
+        mScreenDecorations.start();
+        // Top and bottom windows are created for rounded corners.
+        verify(mWindowManager, times(1))
+                .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
+        verify(mWindowManager, times(1))
+                .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
+
+        // Left and right window should be null.
+        assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
+        assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+
+        // One tunable.
+        verify(mTunerService, times(1)).addTunable(any(), any());
+
+        // Size of corner view should exactly match max(width, height) of R.drawable.rounded
+        assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(multipleRadiusSize);
+        assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(multipleRadiusSize);
+        assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(multipleRadiusSize);
+    }
+
+    @Test
     public void testNoRounding_CutoutShortEdge() {
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, true);
@@ -193,6 +261,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         // top cutout
         doReturn(new DisplayCutout(
@@ -227,6 +297,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         // left cutout
         doReturn(new DisplayCutout(
@@ -257,6 +329,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius, 20);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 20);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         // top cutout
         doReturn(new DisplayCutout(
@@ -288,6 +362,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius, 20);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 20);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         // left cutout
         doReturn(new DisplayCutout(
@@ -319,6 +395,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius, 20);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 20);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         // top and left cutout
         doReturn(new DisplayCutout(
@@ -355,6 +433,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         // Set to short edge cutout(top).
         doReturn(new DisplayCutout(
@@ -402,6 +482,8 @@
                 com.android.internal.R.dimen.rounded_corner_radius_bottom, 0);
         mContext.getOrCreateTestableResources()
                 .addOverride(dimen.rounded_corner_content_padding, 0);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         // top cutout
         doReturn(new DisplayCutout(
@@ -440,6 +522,8 @@
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.dimen.rounded_corner_radius, 20);
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.bool.config_roundedCornerMultipleRadius, false);
 
         mScreenDecorations.start();
         assertEquals(mScreenDecorations.mRoundedDefault, 20);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index a36f2c7..bb2eea9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -31,6 +31,7 @@
 import androidx.test.InstrumentationRegistry;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.FalsingManager;
 
@@ -80,6 +81,9 @@
         // None of them actually need it.
         mDependency.injectTestDependency(FalsingManager.class, new FalsingManagerFake());
         mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+
+        // TODO: b/151614195 investigate root cause of needing this mock dependency
+        mDependency.injectMockDependency(LocalBluetoothManager.class);
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
index e3187cb9..b1ac022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java
@@ -43,6 +43,7 @@
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.IntSupplier;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -59,7 +60,13 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        mStackController = spy(new TestableStackController(mFloatingContentCoordinator));
+        mStackController = spy(new TestableStackController(
+                mFloatingContentCoordinator, new IntSupplier() {
+                    @Override
+                    public int getAsInt() {
+                        return mLayout.getChildCount();
+                    }
+                }));
         mLayout.setActiveController(mStackController);
         addOneMoreThanBubbleLimitBubbles();
         mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
@@ -295,8 +302,9 @@
      */
     private class TestableStackController extends StackAnimationController {
         TestableStackController(
-                FloatingContentCoordinator floatingContentCoordinator) {
-            super(floatingContentCoordinator);
+                FloatingContentCoordinator floatingContentCoordinator,
+                IntSupplier bubbleCountSupplier) {
+            super(floatingContentCoordinator, bubbleCountSupplier);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 8320b05..6871aad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.phone.NavigationModeController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.util.DeviceConfigProxy;
@@ -71,6 +72,7 @@
     private @Mock DumpManager mDumpManager;
     private @Mock PowerManager mPowerManager;
     private @Mock TrustManager mTrustManager;
+    private @Mock NavigationModeController mNavigationModeController;
     private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
 
@@ -88,7 +90,7 @@
                 mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher,
                 mNotificationShadeWindowController, () -> mStatusBarKeyguardViewManager,
                 mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
-                mPowerManager, mTrustManager, mDeviceConfig);
+                mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController);
         mViewMediator.start();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
index b12db2b..0bf0f04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java
@@ -24,6 +24,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
+import android.util.Size;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 
@@ -51,6 +52,7 @@
     private static final float MIN_ASPECT_RATIO = 0.5f;
     private static final float MAX_ASPECT_RATIO = 2f;
     private static final Rect EMPTY_CURRENT_BOUNDS = null;
+    private static final Size EMPTY_MINIMAL_SIZE = null;
 
     private PipBoundsHandler mPipBoundsHandler;
     private DisplayInfo mDefaultDisplayInfo;
@@ -119,7 +121,7 @@
         };
         for (float aspectRatio : aspectRatios) {
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    aspectRatio, EMPTY_CURRENT_BOUNDS);
+                    aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
             final float actualAspectRatio =
                     destinationBounds.width() / (destinationBounds.height() * 1f);
             assertEquals("Destination bounds matches the given aspect ratio",
@@ -135,7 +137,7 @@
         };
         for (float aspectRatio : invalidAspectRatios) {
             final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                    aspectRatio, EMPTY_CURRENT_BOUNDS);
+                    aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
             final float actualAspectRatio =
                     destinationBounds.width() / (destinationBounds.height() * 1f);
             assertEquals("Destination bounds fallbacks to default aspect ratio",
@@ -151,7 +153,7 @@
         currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
 
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                aspectRatio, currentBounds);
+                aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE);
 
         final float actualAspectRatio =
                 destinationBounds.width() / (destinationBounds.height() * 1f);
@@ -160,14 +162,58 @@
     }
 
     @Test
+    public void getDestinationBounds_withMinSize_returnMinBounds() {
+        final float[] aspectRatios = new float[] {
+                (MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2,
+                DEFAULT_ASPECT_RATIO,
+                (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2
+        };
+        final Size[] minimalSizes = new Size[] {
+                new Size((int) (100 * aspectRatios[0]), 100),
+                new Size((int) (100 * aspectRatios[1]), 100),
+                new Size((int) (100 * aspectRatios[2]), 100)
+        };
+        for (int i = 0; i < aspectRatios.length; i++) {
+            final float aspectRatio = aspectRatios[i];
+            final Size minimalSize = minimalSizes[i];
+            final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+                    aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize);
+            assertTrue("Destination bounds is no smaller than minimal requirement",
+                    (destinationBounds.width() == minimalSize.getWidth()
+                            && destinationBounds.height() >= minimalSize.getHeight())
+                            || (destinationBounds.height() == minimalSize.getHeight()
+                            && destinationBounds.width() >= minimalSize.getWidth()));
+            final float actualAspectRatio =
+                    destinationBounds.width() / (destinationBounds.height() * 1f);
+            assertEquals("Destination bounds matches the given aspect ratio",
+                    aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN);
+        }
+    }
+
+    @Test
+    public void getDestinationBounds_withCurrentBounds_ignoreMinBounds() {
+        final float aspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2;
+        final Rect currentBounds = new Rect(0, 0, 0, 100);
+        currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left;
+        final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2);
+
+        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
+                aspectRatio, currentBounds, minSize);
+
+        assertTrue("Destination bounds ignores minimal size",
+                destinationBounds.width() > minSize.getWidth()
+                        && destinationBounds.height() > minSize.getHeight());
+    }
+
+    @Test
     public void setShelfHeight_offsetBounds() {
         final int shelfHeight = 100;
         final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         mPipBoundsHandler.setShelfHeight(true, shelfHeight);
         final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -shelfHeight);
         assertBoundsWithMargin("offsetBounds by shelf", oldPosition, newPosition);
@@ -177,11 +223,11 @@
     public void onImeVisibilityChanged_offsetBounds() {
         final int imeHeight = 100;
         final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight);
         final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -imeHeight);
         assertBoundsWithMargin("offsetBounds by IME", oldPosition, newPosition);
@@ -191,13 +237,13 @@
     public void onSaveReentryBounds_restoreLastPosition() {
         final ComponentName componentName = new ComponentName(mContext, "component1");
         final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         oldPosition.offset(0, -100);
         mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition);
 
         final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         assertBoundsWithMargin("restoreLastPosition", oldPosition, newPosition);
     }
@@ -206,14 +252,14 @@
     public void onResetReentryBounds_useDefaultBounds() {
         final ComponentName componentName = new ComponentName(mContext, "component1");
         final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
         final Rect newBounds = new Rect(defaultBounds);
         newBounds.offset(0, -100);
         mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
 
         mPipBoundsHandler.onResetReentryBounds(componentName);
         final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         assertBoundsWithMargin("useDefaultBounds", defaultBounds, actualBounds);
     }
@@ -222,14 +268,14 @@
     public void onResetReentryBounds_componentMismatch_restoreLastPosition() {
         final ComponentName componentName = new ComponentName(mContext, "component1");
         final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
         final Rect newBounds = new Rect(defaultBounds);
         newBounds.offset(0, -100);
         mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds);
 
         mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2"));
         final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(
-                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
+                DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE);
 
         assertBoundsWithMargin("restoreLastPosition", newBounds, actualBounds);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
new file mode 100644
index 0000000..4d7e6ae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.pip.phone;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.IActivityManager;
+import android.app.IActivityTaskManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Size;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.pip.PipBoundsHandler;
+import com.android.systemui.pip.PipSnapAlgorithm;
+import com.android.systemui.pip.PipTaskOrganizer;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.FloatingContentCoordinator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests against {@link PipTouchHandler}, including but not limited to:
+ * - Update movement bounds based on new bounds
+ * - Update movement bounds based on IME/shelf
+ * - Update movement bounds to PipResizeHandler
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipTouchHandlerTest extends SysuiTestCase {
+    private static final int ROUNDING_ERROR_MARGIN = 10;
+    private static final float DEFAULT_ASPECT_RATIO = 1f;
+    private static final Rect EMPTY_CURRENT_BOUNDS = null;
+
+    private PipTouchHandler mPipTouchHandler;
+    private DisplayInfo mDefaultDisplayInfo;
+
+    @Mock
+    private IActivityManager mActivityManager;
+
+    @Mock
+    private IActivityTaskManager mIActivityTaskManager;
+
+    @Mock
+    private PipMenuActivityController mPipMenuActivityController;
+
+    @Mock
+    private InputConsumerController mInputConsumerController;
+
+    @Mock
+    private PipBoundsHandler mPipBoundsHandler;
+
+    @Mock
+    private PipTaskOrganizer mPipTaskOrganizer;
+
+    @Mock
+    private FloatingContentCoordinator mFloatingContentCoordinator;
+
+    @Mock
+    private DeviceConfigProxy mDeviceConfigProxy;
+
+
+    private PipSnapAlgorithm mPipSnapAlgorithm;
+    private PipMotionHelper mMotionHelper;
+    private PipResizeGestureHandler mPipResizeGestureHandler;
+
+    Rect mInsetBounds;
+    Rect mMinBounds;
+    Rect mCurBounds;
+    boolean mFromImeAdjustment;
+    boolean mFromShelfAdjustment;
+    int mDisplayRotation;
+
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
+        mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mIActivityTaskManager,
+                mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
+                mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy,
+                mPipSnapAlgorithm);
+        mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
+        mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
+        mPipTouchHandler.setPipMotionHelper(mMotionHelper);
+        mPipTouchHandler.setPipResizeGestureHandler(mPipResizeGestureHandler);
+
+        // Assume a display of 1000 x 1000
+        // inset of 10
+        mInsetBounds = new Rect(10, 10, 990, 990);
+        // minBounds of 100x100 bottom right corner
+        mMinBounds = new Rect(890, 890, 990, 990);
+        mCurBounds = new Rect();
+        mFromImeAdjustment = false;
+        mFromShelfAdjustment = false;
+        mDisplayRotation = 0;
+    }
+
+    @Test
+    public void updateMovementBounds_minBounds() {
+        Rect expectedMinMovementBounds = new Rect();
+        mPipSnapAlgorithm.getMovementBounds(mMinBounds, mInsetBounds, expectedMinMovementBounds, 0);
+
+        mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+                mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+        assertEquals(expectedMinMovementBounds, mPipTouchHandler.mNormalMovementBounds);
+        verify(mPipResizeGestureHandler, times(1))
+                .updateMinSize(mMinBounds.width(), mMinBounds.height());
+    }
+
+    @Test
+    public void updateMovementBounds_maxBounds() {
+        Point displaySize = new Point();
+        mContext.getDisplay().getRealSize(displaySize);
+        Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1,
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y);
+        Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());
+        Rect expectedMaxMovementBounds = new Rect();
+        mPipSnapAlgorithm.getMovementBounds(maxBounds, mInsetBounds, expectedMaxMovementBounds, 0);
+
+        mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+                mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+        assertEquals(expectedMaxMovementBounds, mPipTouchHandler.mExpandedMovementBounds);
+        verify(mPipResizeGestureHandler, times(1))
+                .updateMaxSize(maxBounds.width(), maxBounds.height());
+    }
+
+    @Test
+    public void updateMovementBounds_withImeAdjustment_movesPip() {
+        mFromImeAdjustment = true;
+        mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mMinBounds, mCurBounds,
+                mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+
+        verify(mMotionHelper, times(1)).animateToOffset(any(), anyInt());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index ac30421..dbbbaac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -85,6 +85,8 @@
     @Mock
     private NotificationMediaManager mNotificationMediaManager;
     @Mock
+    private Executor mForegroundExecutor;
+    @Mock
     private Executor mBackgroundExecutor;
     @Mock
     private LocalBluetoothManager mLocalBluetoothManager;
@@ -97,7 +99,7 @@
         mTestableLooper.runWithLooper(() -> {
             mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
             mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher,
-                    mQSLogger, mNotificationMediaManager, mBackgroundExecutor,
+                    mQSLogger, mNotificationMediaManager, mForegroundExecutor, mBackgroundExecutor,
                     mLocalBluetoothManager);
             // Provides a parent with non-zero size for QSPanel
             mParentView = new FrameLayout(mContext);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 73f3ddd..95c3e5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -86,7 +86,7 @@
     @Mock
     private StatusBarIconController mIconController;
     @Mock
-    private QSFactoryImpl mDefaultFactory;
+    private QSFactory mDefaultFactory;
     @Mock
     private PluginManager mPluginManager;
     @Mock
@@ -295,7 +295,7 @@
 
     private static class TestQSTileHost extends QSTileHost {
         TestQSTileHost(Context context, StatusBarIconController iconController,
-                QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
+                QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
                 PluginManager pluginManager, TunerService tunerService,
                 Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
                 BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 58be50e..953198c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -30,7 +30,7 @@
 import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.QSHost
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import org.junit.Assert.assertEquals
@@ -56,7 +56,7 @@
         val TILE_SPEC = CustomTile.toSpec(componentName)
     }
 
-    @Mock private lateinit var mTileHost: QSTileHost
+    @Mock private lateinit var mTileHost: QSHost
     @Mock private lateinit var mTileService: IQSTileService
     @Mock private lateinit var mTileServices: TileServices
     @Mock private lateinit var mTileServiceManager: TileServiceManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
new file mode 100644
index 0000000..f061f34
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.app.WallpaperManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Choreographer
+import android.view.View
+import android.view.ViewRootImpl
+import androidx.dynamicanimation.animation.SpringAnimation
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.*
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+@SmallTest
+class NotificationShadeDepthControllerTest : SysuiTestCase() {
+
+    @Mock private lateinit var statusBarStateController: StatusBarStateController
+    @Mock private lateinit var blurUtils: BlurUtils
+    @Mock private lateinit var biometricUnlockController: BiometricUnlockController
+    @Mock private lateinit var keyguardStateController: KeyguardStateController
+    @Mock private lateinit var choreographer: Choreographer
+    @Mock private lateinit var wallpaperManager: WallpaperManager
+    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock private lateinit var dumpManager: DumpManager
+    @Mock private lateinit var root: View
+    @Mock private lateinit var viewRootImpl: ViewRootImpl
+    @Mock private lateinit var shadeSpring: SpringAnimation
+    @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
+
+    private lateinit var statusBarStateListener: StatusBarStateController.StateListener
+    private var statusBarState = StatusBarState.SHADE
+    private val maxBlur = 150
+    private lateinit var notificationShadeDepthController: NotificationShadeDepthController
+
+    @Before
+    fun setup() {
+        `when`(root.viewRootImpl).thenReturn(viewRootImpl)
+        `when`(statusBarStateController.state).then { statusBarState }
+        `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
+            (answer.arguments[0] as Float * maxBlur).toInt()
+        }
+        notificationShadeDepthController = NotificationShadeDepthController(
+                statusBarStateController, blurUtils, biometricUnlockController,
+                keyguardStateController, choreographer, wallpaperManager,
+                notificationShadeWindowController, dumpManager)
+        notificationShadeDepthController.shadeSpring = shadeSpring
+        notificationShadeDepthController.root = root
+
+        val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
+        verify(statusBarStateController).addCallback(captor.capture())
+        statusBarStateListener = captor.value
+    }
+
+    @Test
+    fun setupListeners() {
+        verify(dumpManager).registerDumpable(anyString(), safeEq(notificationShadeDepthController))
+    }
+
+    @Test
+    fun onPanelExpansionChanged_apliesBlur_ifShade() {
+        notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
+                false /* tracking */)
+        verify(shadeSpring).animateToFinalPosition(eq(maxBlur.toFloat()))
+    }
+
+    @Test
+    fun onStateChanged_reevalutesBlurs_ifSameRadiusAndNewState() {
+        onPanelExpansionChanged_apliesBlur_ifShade()
+        clearInvocations(shadeSpring)
+
+        statusBarState = StatusBarState.KEYGUARD
+        statusBarStateListener.onStateChanged(statusBarState)
+        verify(shadeSpring).animateToFinalPosition(eq(0f))
+    }
+
+    @Test
+    fun updateGlobalDialogVisibility_schedulesUpdate() {
+        notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
+        verify(choreographer).postFrameCallback(any())
+    }
+
+    private fun <T : Any> safeEq(value: T): T {
+        return eq(value) ?: value
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index cc5f149..83877f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -52,6 +52,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationListItem;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
@@ -285,9 +286,15 @@
         public void notifyGroupChildAdded(ExpandableView row) {}
 
         @Override
+        public void notifyGroupChildAdded(View v) {}
+
+        @Override
         public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {}
 
         @Override
+        public void notifyGroupChildRemoved(View v, ViewGroup childrenContainer) {}
+
+        @Override
         public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {}
 
         @Override
@@ -313,12 +320,22 @@
         }
 
         @Override
+        public void removeListItem(NotificationListItem li) {
+            removeContainerView(li.getView());
+        }
+
+        @Override
         public void addContainerView(View v) {
             mLayout.addView(v);
             mRows.add(v);
         }
 
         @Override
+        public void addListItem(NotificationListItem li) {
+            addContainerView(li.getView());
+        }
+
+        @Override
         public void setMaxDisplayedNotifications(int maxNotifications) {
             if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) {
                 mViewHierarchyManager.onDynamicPrivacyChanged();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index fe8b89f..a58000d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -55,6 +55,7 @@
     private boolean mIsVisuallyInterruptive = false;
     private boolean mIsConversation = false;
     private ShortcutInfo mShortcutInfo = null;
+    private boolean mIsBubble = false;
 
     public RankingBuilder() {
     }
@@ -82,6 +83,7 @@
         mIsVisuallyInterruptive = ranking.visuallyInterruptive();
         mIsConversation = ranking.isConversation();
         mShortcutInfo = ranking.getShortcutInfo();
+        mIsBubble = ranking.isBubble();
     }
 
     public Ranking build() {
@@ -108,7 +110,8 @@
                 mCanBubble,
                 mIsVisuallyInterruptive,
                 mIsConversation,
-                mShortcutInfo);
+                mShortcutInfo,
+                mIsBubble);
         return ranking;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 312bb7f..972357e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -143,7 +143,7 @@
                     IMPORTANCE_DEFAULT,
                     null, null,
                     null, null, null, true, sentiment, false, -1, false, null, null, false, false,
-                    false, null);
+                    false, null, false);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
     }
@@ -162,7 +162,7 @@
                     null, null,
                     null, null, null, true,
                     Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
-                    false, smartActions, null, false, false, false, null);
+                    false, smartActions, null, false, false, false, null, false);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(Ranking.class));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index b7184be..82de4a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -34,16 +34,17 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static java.util.Collections.singletonList;
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.Nullable;
@@ -83,13 +84,13 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
 
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -123,6 +124,8 @@
     private NotifCollection mCollection;
     private BatchableNotificationHandler mNotifHandler;
 
+    private InOrder mListenerInOrder;
+
     private NoManSimulator mNoMan;
 
     @Before
@@ -133,6 +136,8 @@
         when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
         when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(true);
 
+        mListenerInOrder = inOrder(mCollectionListener);
+
         mCollection = new NotifCollection(
                 mStatusBarService,
                 mock(DumpManager.class),
@@ -159,10 +164,12 @@
                         .setRank(4747));
 
         // THEN the listener is notified
-        verify(mCollectionListener).onEntryInit(mEntryCaptor.capture());
-        NotificationEntry entry = mEntryCaptor.getValue();
+        final NotificationEntry entry = mCollectionListener.getEntry(notif1.key);
 
-        verify(mCollectionListener).onEntryAdded(entry);
+        mListenerInOrder.verify(mCollectionListener).onEntryInit(entry);
+        mListenerInOrder.verify(mCollectionListener).onEntryAdded(entry);
+        mListenerInOrder.verify(mCollectionListener).onRankingApplied();
+
         assertEquals(notif1.key, entry.getKey());
         assertEquals(notif1.sbn, entry.getSbn());
         assertEquals(notif1.ranking, entry.getRanking());
@@ -215,12 +222,11 @@
         assertEquals(entry2.getRanking(), capturedUpdate.getRanking());
 
         // THEN onBuildList is called only once
-        verify(mBuildListener).onBuildList(mBuildListCaptor.capture());
-        assertEquals(new ArraySet<>(Arrays.asList(
-                capturedAdds.get(0),
-                capturedAdds.get(1),
-                capturedUpdate
-        )), new ArraySet<>(mBuildListCaptor.getValue()));
+        verifyBuiltList(
+                List.of(
+                        capturedAdds.get(0),
+                        capturedAdds.get(1),
+                        capturedUpdate));
     }
 
     @Test
@@ -234,9 +240,11 @@
                 .setRank(89));
 
         // THEN the listener is notified
-        verify(mCollectionListener).onEntryUpdated(mEntryCaptor.capture());
+        final NotificationEntry entry = mCollectionListener.getEntry(notif2.key);
 
-        NotificationEntry entry = mEntryCaptor.getValue();
+        mListenerInOrder.verify(mCollectionListener).onEntryUpdated(entry);
+        mListenerInOrder.verify(mCollectionListener).onRankingApplied();
+
         assertEquals(notif2.key, entry.getKey());
         assertEquals(notif2.sbn, entry.getSbn());
         assertEquals(notif2.ranking, entry.getRanking());
@@ -256,8 +264,10 @@
         mNoMan.retractNotif(notif.sbn, REASON_APP_CANCEL);
 
         // THEN the listener is notified
-        verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL);
-        verify(mCollectionListener).onEntryCleanUp(entry);
+        mListenerInOrder.verify(mCollectionListener).onEntryRemoved(entry, REASON_APP_CANCEL);
+        mListenerInOrder.verify(mCollectionListener).onEntryCleanUp(entry);
+        mListenerInOrder.verify(mCollectionListener).onRankingApplied();
+
         assertEquals(notif.sbn, entry.getSbn());
         assertEquals(notif.ranking, entry.getRanking());
     }
@@ -415,8 +425,8 @@
 
         // THEN the dismissed entry still appears in the notification set
         assertEquals(
-                new ArraySet<>(Collections.singletonList(entry1)),
-                new ArraySet<>(mCollection.getActiveNotifs()));
+                new ArraySet<>(singletonList(entry1)),
+                new ArraySet<>(mCollection.getAllNotifs()));
     }
 
     @Test
@@ -444,7 +454,7 @@
         mNoMan.retractNotif(notif2.sbn, REASON_CANCEL);
         assertEquals(
                 new ArraySet<>(List.of(entry1, entry2, entry3)),
-                new ArraySet<>(mCollection.getActiveNotifs()));
+                new ArraySet<>(mCollection.getAllNotifs()));
 
         // WHEN the summary is dismissed by the user
         mCollection.dismissNotification(entry1, defaultStats(entry1));
@@ -452,7 +462,7 @@
         // THEN the summary is removed, but both children stick around
         assertEquals(
                 new ArraySet<>(List.of(entry2, entry3)),
-                new ArraySet<>(mCollection.getActiveNotifs()));
+                new ArraySet<>(mCollection.getAllNotifs()));
         assertEquals(NOT_DISMISSED, entry2.getDismissState());
         assertEquals(NOT_DISMISSED, entry3.getDismissState());
     }
@@ -561,7 +571,7 @@
     }
 
     @Test
-    public void testEndDismissInterceptionUpdatesDismissInterceptors() throws RemoteException {
+    public void testEndDismissInterceptionUpdatesDismissInterceptors() {
         // GIVEN a collection with notifications with multiple dismiss interceptors
         mInterceptor1.shouldInterceptDismissal = true;
         mInterceptor2.shouldInterceptDismissal = true;
@@ -592,7 +602,7 @@
 
 
     @Test(expected = IllegalStateException.class)
-    public void testEndingDismissalOfNonInterceptedThrows() throws RemoteException {
+    public void testEndingDismissalOfNonInterceptedThrows() {
         // GIVEN a collection with notifications with a dismiss interceptor that hasn't been called
         mInterceptor1.shouldInterceptDismissal = false;
         mCollection.addNotificationDismissInterceptor(mInterceptor1);
@@ -820,7 +830,7 @@
         verify(mExtender3).shouldExtendLifetime(entry2, REASON_CLICK);
 
         // THEN the entry is not removed
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
 
         // THEN the entry properly records all extenders that returned true
         assertEquals(Arrays.asList(mExtender1, mExtender2), entry2.mLifetimeExtenders);
@@ -841,7 +851,7 @@
 
         // GIVEN a notification gets lifetime-extended by one of them
         mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
         clearInvocations(mExtender1, mExtender2, mExtender3);
 
         // WHEN the last active extender expires (but new ones become active)
@@ -856,7 +866,7 @@
         verify(mExtender3).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
 
         // THEN the entry is not removed
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
 
         // THEN the entry properly records all extenders that returned true
         assertEquals(Arrays.asList(mExtender1, mExtender3), entry2.mLifetimeExtenders);
@@ -878,7 +888,7 @@
 
         // GIVEN a notification gets lifetime-extended by a couple of them
         mNoMan.retractNotif(notif2.sbn, REASON_APP_CANCEL);
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
         clearInvocations(mExtender1, mExtender2, mExtender3);
 
         // WHEN one (but not all) of the extenders expires
@@ -886,7 +896,7 @@
         mExtender2.callback.onEndLifetimeExtension(mExtender2, entry2);
 
         // THEN the entry is not removed
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
 
         // THEN we don't re-query the extenders
         verify(mExtender1, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
@@ -894,7 +904,7 @@
         verify(mExtender3, never()).shouldExtendLifetime(entry2, REASON_APP_CANCEL);
 
         // THEN the entry properly records all extenders that returned true
-        assertEquals(Arrays.asList(mExtender1), entry2.mLifetimeExtenders);
+        assertEquals(singletonList(mExtender1), entry2.mLifetimeExtenders);
     }
 
     @Test
@@ -913,7 +923,7 @@
 
         // GIVEN a notification gets lifetime-extended by a couple of them
         mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
         clearInvocations(mExtender1, mExtender2, mExtender3);
 
         // WHEN all of the active extenders expire
@@ -923,7 +933,7 @@
         mExtender1.callback.onEndLifetimeExtension(mExtender1, entry2);
 
         // THEN the entry removed
-        assertFalse(mCollection.getActiveNotifs().contains(entry2));
+        assertFalse(mCollection.getAllNotifs().contains(entry2));
         verify(mCollectionListener).onEntryRemoved(entry2, REASON_UNKNOWN);
     }
 
@@ -943,7 +953,7 @@
 
         // GIVEN a notification gets lifetime-extended by a couple of them
         mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
         clearInvocations(mExtender1, mExtender2, mExtender3);
 
         // WHEN the notification is reposted
@@ -954,7 +964,7 @@
         verify(mExtender2).cancelLifetimeExtension(entry2);
 
         // THEN the notification is still present
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
     }
 
     @Test(expected = IllegalStateException.class)
@@ -973,7 +983,7 @@
 
         // GIVEN a notification gets lifetime-extended by a couple of them
         mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
         clearInvocations(mExtender1, mExtender2, mExtender3);
 
         // WHEN a lifetime extender makes a reentrant call during cancelLifetimeExtension()
@@ -1002,7 +1012,7 @@
 
         // GIVEN a notification gets lifetime-extended by a couple of them
         mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
-        assertTrue(mCollection.getActiveNotifs().contains(entry2));
+        assertTrue(mCollection.getAllNotifs().contains(entry2));
         clearInvocations(mExtender1, mExtender2, mExtender3);
 
         // WHEN the notification is reposted
@@ -1055,11 +1065,11 @@
 
         // WHEN both notifications are manually dismissed together
         mCollection.dismissNotifications(
-                List.of(new Pair(entry1, defaultStats(entry1)),
-                        new Pair(entry2, defaultStats(entry2))));
+                List.of(new Pair<>(entry1, defaultStats(entry1)),
+                        new Pair<>(entry2, defaultStats(entry2))));
 
         // THEN build list is only called one time
-        verify(mBuildListener).onBuildList(any(Collection.class));
+        verifyBuiltList(List.of(entry1, entry2));
     }
 
     @Test
@@ -1074,8 +1084,8 @@
         DismissedByUserStats stats1 = defaultStats(entry1);
         DismissedByUserStats stats2 = defaultStats(entry2);
         mCollection.dismissNotifications(
-                List.of(new Pair(entry1, defaultStats(entry1)),
-                        new Pair(entry2, defaultStats(entry2))));
+                List.of(new Pair<>(entry1, defaultStats(entry1)),
+                        new Pair<>(entry2, defaultStats(entry2))));
 
         // THEN we send the dismissals to system server
         verify(mStatusBarService).onNotificationClear(
@@ -1109,8 +1119,8 @@
 
         // WHEN both notifications are manually dismissed together
         mCollection.dismissNotifications(
-                List.of(new Pair(entry1, defaultStats(entry1)),
-                        new Pair(entry2, defaultStats(entry2))));
+                List.of(new Pair<>(entry1, defaultStats(entry1)),
+                        new Pair<>(entry2, defaultStats(entry2))));
 
         // THEN the entries are marked as dismissed
         assertEquals(DISMISSED, entry1.getDismissState());
@@ -1134,8 +1144,8 @@
 
         // WHEN both notifications are manually dismissed together
         mCollection.dismissNotifications(
-                List.of(new Pair(entry1, defaultStats(entry1)),
-                        new Pair(entry2, defaultStats(entry2))));
+                List.of(new Pair<>(entry1, defaultStats(entry1)),
+                        new Pair<>(entry2, defaultStats(entry2))));
 
         // THEN all interceptors get checked
         verify(mInterceptor1).shouldInterceptDismissal(entry1);
@@ -1162,7 +1172,7 @@
         mCollection.dismissAllNotifications(entry1.getSbn().getUser().getIdentifier());
 
         // THEN build list is only called one time
-        verify(mBuildListener).onBuildList(any(Collection.class));
+        verifyBuiltList(List.of(entry1, entry2));
     }
 
     @Test
@@ -1271,13 +1281,18 @@
                 NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
     }
 
-    public CollectionEvent postNotif(NotificationEntryBuilder builder) {
+    private CollectionEvent postNotif(NotificationEntryBuilder builder) {
         clearInvocations(mCollectionListener);
         NotifEvent rawEvent = mNoMan.postNotif(builder);
         verify(mCollectionListener).onEntryAdded(mEntryCaptor.capture());
         return new CollectionEvent(rawEvent, requireNonNull(mEntryCaptor.getValue()));
     }
 
+    private void verifyBuiltList(Collection<NotificationEntry> list) {
+        verify(mBuildListener).onBuildList(mBuildListCaptor.capture());
+        assertEquals(new ArraySet<>(list), new ArraySet<>(mBuildListCaptor.getValue()));
+    }
+
     private static class RecordingCollectionListener implements NotifCollectionListener {
         private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();
 
@@ -1303,6 +1318,14 @@
         public void onEntryCleanUp(NotificationEntry entry) {
         }
 
+        @Override
+        public void onRankingApplied() {
+        }
+
+        @Override
+        public void onRankingUpdate(RankingMap rankingMap) {
+        }
+
         public NotificationEntry getEntry(String key) {
             if (!mLastSeenEntries.containsKey(key)) {
                 throw new RuntimeException("Key not found: " + key);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
index 67b1aad..407e1e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinatorTest.java
@@ -223,7 +223,7 @@
                 .setPkg(TEST_PKG)
                 .setId(2)
                 .build();
-        when(mNotifPipeline.getActiveNotifs()).thenReturn(List.of(entry1, entry2, entry2Other));
+        when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry1, entry2, entry2Other));
 
         // GIVEN that entry2 is currently associated with a foreground service
         when(mForegroundServiceController.getStandardLayoutKey(0, TEST_PKG))
@@ -253,7 +253,7 @@
                 .setPkg(TEST_PKG)
                 .setId(2)
                 .build();
-        when(mNotifPipeline.getActiveNotifs()).thenReturn(List.of(entry));
+        when(mNotifPipeline.getAllNotifs()).thenReturn(List.of(entry));
         when(mForegroundServiceController.getStandardLayoutKey(0, TEST_PKG))
                 .thenReturn(entry.getKey());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
new file mode 100644
index 0000000..0c109c4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class HeadsUpCoordinatorTest extends SysuiTestCase {
+
+    private HeadsUpCoordinator mCoordinator;
+
+    // captured listeners and pluggables:
+    private NotifCollectionListener mCollectionListener;
+    private NotifPromoter mNotifPromoter;
+    private NotifLifetimeExtender mNotifLifetimeExtender;
+    private OnHeadsUpChangedListener mOnHeadsUpChangedListener;
+    private NotifSection mNotifSection;
+
+    @Mock private NotifPipeline mNotifPipeline;
+    @Mock private HeadsUpManager mHeadsUpManager;
+    @Mock private NotificationRemoteInputManager mRemoteInputManager;
+    @Mock private RemoteInputController mRemoteInputController;
+    @Mock private NotifLifetimeExtender.OnEndLifetimeExtensionCallback mEndLifetimeExtension;
+
+    private NotificationEntry mEntry;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
+
+        mCoordinator = new HeadsUpCoordinator(
+                mHeadsUpManager,
+                mRemoteInputManager
+        );
+
+        mCoordinator.attach(mNotifPipeline);
+
+        // capture arguments:
+        ArgumentCaptor<NotifCollectionListener> notifCollectionCaptor =
+                ArgumentCaptor.forClass(NotifCollectionListener.class);
+        ArgumentCaptor<NotifPromoter> notifPromoterCaptor =
+                ArgumentCaptor.forClass(NotifPromoter.class);
+        ArgumentCaptor<NotifLifetimeExtender> notifLifetimeExtenderCaptor =
+                ArgumentCaptor.forClass(NotifLifetimeExtender.class);
+        ArgumentCaptor<OnHeadsUpChangedListener> headsUpChangedListenerCaptor =
+                ArgumentCaptor.forClass(OnHeadsUpChangedListener.class);
+
+        verify(mNotifPipeline).addCollectionListener(notifCollectionCaptor.capture());
+        verify(mNotifPipeline).addPromoter(notifPromoterCaptor.capture());
+        verify(mNotifPipeline).addNotificationLifetimeExtender(
+                notifLifetimeExtenderCaptor.capture());
+        verify(mHeadsUpManager).addListener(headsUpChangedListenerCaptor.capture());
+
+        mCollectionListener = notifCollectionCaptor.getValue();
+        mNotifPromoter = notifPromoterCaptor.getValue();
+        mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue();
+        mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue();
+
+        mNotifSection = mCoordinator.getSection();
+        mNotifLifetimeExtender.setCallback(mEndLifetimeExtension);
+        mEntry = new NotificationEntryBuilder().build();
+    }
+
+    @Test
+    public void testPromotesCurrentHUN() {
+        // GIVEN the current HUN is set to mEntry
+        setCurrentHUN(mEntry);
+
+        // THEN only promote the current HUN, mEntry
+        assertTrue(mNotifPromoter.shouldPromoteToTopLevel(mEntry));
+        assertFalse(mNotifPromoter.shouldPromoteToTopLevel(new NotificationEntryBuilder().build()));
+    }
+
+    @Test
+    public void testIncludeInSectionCurrentHUN() {
+        // GIVEN the current HUN is set to mEntry
+        setCurrentHUN(mEntry);
+
+        // THEN only section the current HUN, mEntry
+        assertTrue(mNotifSection.isInSection(mEntry));
+        assertFalse(mNotifSection.isInSection(new NotificationEntryBuilder().build()));
+    }
+
+    @Test
+    public void testLifetimeExtendsCurrentHUN() {
+        // GIVEN there is a HUN, mEntry
+        setCurrentHUN(mEntry);
+
+        // THEN only the current HUN, mEntry, should be lifetimeExtended
+        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(mEntry, /* cancellationReason */ 0));
+        assertFalse(mNotifLifetimeExtender.shouldExtendLifetime(
+                new NotificationEntryBuilder().build(), /* cancellationReason */ 0));
+    }
+
+    @Test
+    public void testLifetimeExtensionEndsOnNewHUN() {
+        // GIVEN there was a HUN that was lifetime extended
+        setCurrentHUN(mEntry);
+        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
+                mEntry, /* cancellation reason */ 0));
+
+        // WHEN there's a new HUN
+        NotificationEntry newHUN = new NotificationEntryBuilder().build();
+        setCurrentHUN(newHUN);
+
+        // THEN the old entry's lifetime extension should be cancelled
+        verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
+    }
+
+    @Test
+    public void testLifetimeExtensionEndsOnNoHUNs() {
+        // GIVEN there was a HUN that was lifetime extended
+        setCurrentHUN(mEntry);
+        assertTrue(mNotifLifetimeExtender.shouldExtendLifetime(
+                mEntry, /* cancellation reason */ 0));
+
+        // WHEN there's no longer a HUN
+        setCurrentHUN(null);
+
+        // THEN the old entry's lifetime extension should be cancelled
+        verify(mEndLifetimeExtension).onEndLifetimeExtension(mNotifLifetimeExtender, mEntry);
+    }
+
+    @Test
+    public void testOnEntryRemovedRemovesHeadsUpNotification() {
+        // GIVEN the current HUN is mEntry
+        setCurrentHUN(mEntry);
+
+        // WHEN mEntry is removed from the notification collection
+        mCollectionListener.onEntryRemoved(mEntry, /* cancellation reason */ 0);
+        when(mRemoteInputController.isSpinning(any())).thenReturn(false);
+
+        // THEN heads up manager should remove the entry
+        verify(mHeadsUpManager).removeNotification(mEntry.getKey(), false);
+    }
+
+    private void setCurrentHUN(NotificationEntry entry) {
+        when(mHeadsUpManager.getTopEntry()).thenReturn(entry);
+        when(mHeadsUpManager.isAlerting(any())).thenReturn(false);
+        if (entry != null) {
+            when(mHeadsUpManager.isAlerting(entry.getKey())).thenReturn(true);
+        }
+        mOnHeadsUpChangedListener.onHeadsUpStateChanged(entry, entry != null);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 792b4d5..8143cf5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -20,8 +20,10 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
@@ -33,12 +35,15 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifViewBarn;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -73,6 +78,8 @@
     @Mock private NotifPipeline mNotifPipeline;
     @Mock private IStatusBarService mService;
     @Mock private NotifInflaterImpl mNotifInflater;
+    @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+    @Mock private HeadsUpManager mHeadsUpManager;
 
     @Before
     public void setUp() {
@@ -86,7 +93,10 @@
                 mock(PreparationCoordinatorLogger.class),
                 mNotifInflater,
                 mErrorManager,
-                mService);
+                mock(NotifViewBarn.class),
+                mService,
+                mNotificationInterruptStateProvider,
+                mHeadsUpManager);
 
         ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
         mCoordinator.attach(mNotifPipeline);
@@ -170,4 +180,24 @@
         // THEN it isn't filtered from shade list
         assertFalse(mUninflatedFilter.shouldFilterOut(mEntry, 0));
     }
+
+    @Test
+    public void testShowHUNOnInflationFinished() {
+        // WHEN a notification should HUN and its inflation is finished
+        when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(true);
+        mCallback.onInflationFinished(mEntry);
+
+        // THEN we tell the HeadsUpManager to show the notification
+        verify(mHeadsUpManager).showNotification(mEntry);
+    }
+
+    @Test
+    public void testNoHUNOnInflationFinished() {
+        // WHEN a notification shouldn't HUN and its inflation is finished
+        when(mNotificationInterruptStateProvider.shouldHeadsUp(mEntry)).thenReturn(false);
+        mCallback.onInflationFinished(mEntry);
+
+        // THEN we never tell the HeadsUpManager to show the notification
+        verify(mHeadsUpManager, never()).showNotification(mEntry);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index e960185..c356e0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -329,10 +329,10 @@
     @Test
     public void testIconScrollXAfterTranslationAndReset() throws Exception {
         mGroupRow.setTranslation(50);
-        assertEquals(50, -mGroupRow.getEntry().expandedIcon.getScrollX());
+        assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
 
         mGroupRow.resetTranslation();
-        assertEquals(0, mGroupRow.getEntry().expandedIcon.getScrollX());
+        assertEquals(0, mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index a21a047..2e787ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Notification;
+import android.content.pm.LauncherApps;
 import android.os.Handler;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
@@ -61,6 +62,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.icon.IconBuilder;
+import com.android.systemui.statusbar.notification.icon.IconManager;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -245,14 +248,13 @@
                 mLockscreenUserManager,
                 pipeline,
                 stage,
-                true, /* allowLongPress */
-                mock(KeyguardBypassController.class),
-                mock(StatusBarStateController.class),
-                mGroupManager,
-                mGutsManager,
                 mNotificationInterruptionStateProvider,
                 RowInflaterTask::new,
-                mExpandableNotificationRowComponentBuilder);
+                mExpandableNotificationRowComponentBuilder,
+                new IconManager(
+                        mEntryManager,
+                        mock(LauncherApps.class),
+                        new IconBuilder(mContext)));
 
         mEntryManager.setUpWithPresenter(mPresenter);
         mEntryManager.addNotificationEntryListener(mEntryListener);
@@ -284,7 +286,8 @@
                 false,
                 false,
                 false,
-                null);
+                null,
+                false);
         mRankingMap = new NotificationListenerService.RankingMap(new Ranking[] {ranking});
 
         TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 5a89fc4..dc3374b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -34,6 +34,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
 import android.graphics.drawable.Icon;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
@@ -53,6 +54,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.icon.IconBuilder;
+import com.android.systemui.statusbar.notification.icon.IconManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -93,6 +96,7 @@
     private final NotifBindPipeline mBindPipeline;
     private final NotifCollectionListener mBindPipelineEntryListener;
     private final RowContentBindStage mBindStage;
+    private final IconManager mIconManager;
     private StatusBarStateController mStatusBarStateController;
 
     public NotificationTestHelper(Context context, TestableDependency dependency) {
@@ -106,6 +110,10 @@
                 mock(KeyguardBypassController.class), mock(NotificationGroupManager.class),
                 mock(ConfigurationControllerImpl.class));
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
+        mIconManager = new IconManager(
+                mock(CommonNotifCollection.class),
+                mock(LauncherApps.class),
+                new IconBuilder(mContext));
 
         NotificationContentInflater contentBinder = new NotificationContentInflater(
                 mock(NotifRemoteViewCache.class),
@@ -377,7 +385,7 @@
                 .build();
 
         entry.setRow(row);
-        entry.createIcons(mContext, entry.getSbn());
+        mIconManager.createIcons(entry);
         row.setEntry(entry);
 
         mBindPipelineEntryListener.onEntryInit(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
index 83e89bd..5320ecd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerTest.java
@@ -20,6 +20,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@
 import android.app.IActivityManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
@@ -110,4 +112,13 @@
     public void testSetForcePluginOpen_beforeStatusBarInitialization() {
         mNotificationShadeWindowController.setForcePluginOpen(true);
     }
+
+    @Test
+    public void setBackgroundBlurRadius_expandedWithBlurs() {
+        mNotificationShadeWindowController.setBackgroundBlurRadius(10);
+        verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE));
+
+        mNotificationShadeWindowController.setBackgroundBlurRadius(0);
+        verify(mNotificationShadeWindowView).setVisibility(eq(View.INVISIBLE));
+    }
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 8b8b9e5..48be0d9 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -64,16 +64,26 @@
         dest.writeInt(mTetheringType);
     }
 
+    /**
+     * Get the MAC address used to identify the client.
+     */
     @NonNull
     public MacAddress getMacAddress() {
         return mMacAddress;
     }
 
+    /**
+     * Get information on the list of addresses that are associated with the client.
+     */
     @NonNull
     public List<AddressInfo> getAddresses() {
         return new ArrayList<>(mAddresses);
     }
 
+    /**
+     * Get the type of tethering used by the client.
+     * @return one of the {@code TetheringManager#TETHERING_*} constants.
+     */
     public int getTetheringType() {
         return mTetheringType;
     }
@@ -115,45 +125,47 @@
         private final LinkAddress mAddress;
         @Nullable
         private final String mHostname;
-        // TODO: use LinkAddress expiration time once it is supported
-        private final long mExpirationTime;
 
         /** @hide */
         public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) {
-            this(address, hostname, 0);
-        }
-
-        /** @hide */
-        public AddressInfo(@NonNull LinkAddress address, String hostname, long expirationTime) {
             this.mAddress = address;
             this.mHostname = hostname;
-            this.mExpirationTime = expirationTime;
         }
 
         private AddressInfo(Parcel in) {
-            this(in.readParcelable(null),  in.readString(), in.readLong());
+            this(in.readParcelable(null),  in.readString());
         }
 
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeParcelable(mAddress, flags);
             dest.writeString(mHostname);
-            dest.writeLong(mExpirationTime);
         }
 
+        /**
+         * Get the link address (including prefix length and lifetime) used by the client.
+         *
+         * This may be an IPv4 or IPv6 address.
+         */
         @NonNull
         public LinkAddress getAddress() {
             return mAddress;
         }
 
+        /**
+         * Get the hostname that was advertised by the client when obtaining its address, if any.
+         */
         @Nullable
         public String getHostname() {
             return mHostname;
         }
 
-        /** @hide TODO: use expiration time in LinkAddress */
+        /**
+         * Get the expiration time of the address assigned to the client.
+         * @hide
+         */
         public long getExpirationTime() {
-            return mExpirationTime;
+            return mAddress.getExpirationTime();
         }
 
         @Override
@@ -163,7 +175,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(mAddress, mHostname, mExpirationTime);
+            return Objects.hash(mAddress, mHostname);
         }
 
         @Override
@@ -173,8 +185,7 @@
             // Use .equals() for addresses as all changes, including address expiry changes,
             // should be included.
             return other.mAddress.equals(mAddress)
-                    && Objects.equals(mHostname, other.mHostname)
-                    && mExpirationTime == other.mExpirationTime;
+                    && Objects.equals(mHostname, other.mHostname);
         }
 
         @NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index a18f5da..fd6f171 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -32,7 +32,7 @@
  * @hide
  */
 @SystemApi(client = MODULE_LIBRARIES)
-public class TetheringConstants {
+public final class TetheringConstants {
     /** An explicit private class to avoid exposing constructor.*/
     private TetheringConstants() { }
 
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
index 4af5c53..04d6215 100644
--- a/packages/Tethering/res/values/config.xml
+++ b/packages/Tethering/res/values/config.xml
@@ -157,4 +157,49 @@
 
     <!-- ComponentName of the service used to run no ui tether provisioning. -->
     <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
+
+    <!-- Enable tethering notification -->
+    <!-- Icons for showing tether enable notification.
+         Each item should have two elements and be separated with ";".
+
+         The first element is downstream types which is one of tethering. This element has to be
+         made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream
+         types and use "," to separate each combinations. Such as
+
+             USB|BT,WIFI|USB|BT
+
+         The second element is icon for the item. This element has to be composed by
+         <package name>:drawable/<resource name>. Such as
+
+             1. com.android.networkstack.tethering:drawable/stat_sys_tether_general
+             2. android:drawable/xxx
+
+         So the entire string of each item would be
+
+             USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general
+
+         NOTE: One config can be separated into two or more for readability. Such as
+
+               WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx
+
+               can be separated into
+
+               WIFI|USB;android:drawable/xxx
+               WIFI|BT;android:drawable/xxx
+               USB|BT;android:drawable/xxx
+               WIFI|USB|BT;android:drawable/xxx
+
+         Notification will not show if the downstream type isn't listed in array.
+         Empty array means disable notifications. -->
+    <!-- In AOSP, hotspot is configured to no notification by default. Because status bar has showed
+         an icon on the right side already -->
+    <string-array translatable="false" name="tethering_notification_icons">
+        <item>USB;com.android.networkstack.tethering:drawable/stat_sys_tether_usb</item>
+        <item>BT;com.android.networkstack.tethering:drawable/stat_sys_tether_bluetooth</item>
+        <item>WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general</item>
+    </string-array>
+    <!-- String for tether enable notification title. -->
+    <string name="tethering_notification_title">@string/tethered_notification_title</string>
+    <!-- String for tether enable notification message. -->
+    <string name="tethering_notification_message">@string/tethered_notification_message</string>
 </resources>
diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml
index fe025c7..bbba3f3 100644
--- a/packages/Tethering/res/values/overlayable.xml
+++ b/packages/Tethering/res/values/overlayable.xml
@@ -16,6 +16,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <overlayable name="TetheringConfig">
         <policy type="product|system|vendor">
+            <!-- Params from config.xml that can be overlaid -->
             <item type="array" name="config_tether_usb_regexs"/>
             <item type="array" name="config_tether_ncm_regexs" />
             <item type="array" name="config_tether_wifi_regexs"/>
@@ -31,6 +32,45 @@
             <item type="string" name="config_mobile_hotspot_provision_response"/>
             <item type="integer" name="config_mobile_hotspot_provision_check_period"/>
             <item type="string" name="config_wifi_tether_enable"/>
+            <!-- Configuration values for TetheringNotificationUpdater -->
+            <!-- Icons for showing tether enable notification.
+            Each item should have two elements and be separated with ";".
+
+            The first element is downstream types which is one of tethering. This element has to be
+            made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream
+            types and use "," to separate each combinations. Such as
+
+            USB|BT,WIFI|USB|BT
+
+            The second element is icon for the item. This element has to be composed by
+            <package name>:drawable/<resource name>. Such as
+
+            1. com.android.networkstack.tethering:drawable/stat_sys_tether_general
+            2. android:drawable/xxx
+
+            So the entire string of each item would be
+
+            USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general
+
+            NOTE: One config can be separated into two or more for readability. Such as
+
+                  WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx
+
+                  can be separated into
+
+                  WIFI|USB;android:drawable/xxx
+                  WIFI|BT;android:drawable/xxx
+                  USB|BT;android:drawable/xxx
+                  WIFI|USB|BT;android:drawable/xxx
+
+            Notification will not show if the downstream type isn't listed in array.
+            Empty array means disable notifications. -->
+            <item type="array" name="tethering_notification_icons"/>
+            <!-- String for tether enable notification title. -->
+            <item type="string" name="tethering_notification_title"/>
+            <!-- String for tether enable notification message. -->
+            <item type="string" name="tethering_notification_message"/>
+            <!-- Params from config.xml that can be overlaid -->
         </policy>
     </overlayable>
 </resources>
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
index 792bce9..ba98a66 100644
--- a/packages/Tethering/res/values/strings.xml
+++ b/packages/Tethering/res/values/strings.xml
@@ -15,19 +15,21 @@
 -->
 <resources>
     <!-- Shown when the device is tethered -->
-    <!-- Strings for tethered notification title [CHAR LIMIT=200] -->
+    <!-- String for tethered notification title [CHAR LIMIT=200] -->
     <string name="tethered_notification_title">Tethering or hotspot active</string>
-    <!-- Strings for tethered notification message [CHAR LIMIT=200] -->
+    <!-- String for tethered notification message [CHAR LIMIT=200] -->
     <string name="tethered_notification_message">Tap to set up.</string>
 
     <!-- This notification is shown when tethering has been disabled on a user's device.
     The device is managed by the user's employer. Tethering can't be turned on unless the
     IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
-    <!-- Strings for tether disabling notification title [CHAR LIMIT=200] -->
+    <!-- String for tether disabling notification title [CHAR LIMIT=200] -->
     <string name="disable_tether_notification_title">Tethering is disabled</string>
-    <!-- Strings for tether disabling notification message [CHAR LIMIT=200] -->
+    <!-- String for tether disabling notification message [CHAR LIMIT=200] -->
     <string name="disable_tether_notification_message">Contact your admin for details</string>
 
-    <!-- Strings for tether notification channel name [CHAR LIMIT=200] -->
+    <!-- This string should be consistent with the "Hotspot & tethering" text in the "Network and
+    Internet" settings page. That is currently the tether_settings_title_all string. -->
+    <!-- String for tether notification channel name [CHAR LIMIT=200] -->
     <string name="notification_channel_tethering_status">Hotspot &amp; tethering status</string>
 </resources>
\ No newline at end of file
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 38f8609..6c0c432 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -24,6 +24,7 @@
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 import static android.net.util.NetworkConstants.asByte;
 import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
 import android.net.INetd;
 import android.net.INetworkStackStatusCallback;
@@ -448,7 +449,9 @@
             final ArrayList<TetheredClient> leases = new ArrayList<>();
             for (DhcpLeaseParcelable lease : leaseParcelables) {
                 final LinkAddress address = new LinkAddress(
-                        intToInet4AddressHTH(lease.netAddr), lease.prefixLength);
+                        intToInet4AddressHTH(lease.netAddr), lease.prefixLength,
+                        0 /* flags */, RT_SCOPE_UNIVERSE /* as per RFC6724#3.2 */,
+                        lease.expTime /* deprecationTime */, lease.expTime /* expirationTime */);
 
                 final MacAddress macAddress;
                 try {
@@ -460,7 +463,7 @@
                 }
 
                 final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo(
-                        address, lease.hostname, lease.expTime);
+                        address, lease.hostname);
                 leases.add(new TetheredClient(
                         macAddress,
                         Collections.singletonList(addressInfo),
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index a402ffa..15cdb6a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -39,8 +39,7 @@
 import android.net.netlink.ConntrackMessage;
 import android.net.netlink.NetlinkConstants;
 import android.net.netlink.NetlinkSocket;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.provider.Settings;
@@ -89,8 +88,8 @@
     private final Handler mHandler;
     private final OffloadHardwareInterface mHwInterface;
     private final ContentResolver mContentResolver;
-    private final @NonNull OffloadTetheringStatsProvider mStatsProvider;
-    private final @Nullable NetworkStatsProviderCallback mStatsProviderCb;
+    @Nullable
+    private final OffloadTetheringStatsProvider mStatsProvider;
     private final SharedLog mLog;
     private final HashMap<String, LinkProperties> mDownstreams;
     private boolean mConfigInitialized;
@@ -124,19 +123,18 @@
         mHandler = h;
         mHwInterface = hwi;
         mContentResolver = contentResolver;
-        mStatsProvider = new OffloadTetheringStatsProvider();
         mLog = log.forSubComponent(TAG);
         mDownstreams = new HashMap<>();
         mExemptPrefixes = new HashSet<>();
         mLastLocalPrefixStrs = new HashSet<>();
-        NetworkStatsProviderCallback providerCallback = null;
+        OffloadTetheringStatsProvider provider = new OffloadTetheringStatsProvider();
         try {
-            providerCallback = nsm.registerNetworkStatsProvider(
-                    getClass().getSimpleName(), mStatsProvider);
+            nsm.registerNetworkStatsProvider(getClass().getSimpleName(), provider);
         } catch (RuntimeException e) {
             Log.wtf(TAG, "Cannot register offload stats provider: " + e);
+            provider = null;
         }
-        mStatsProviderCb = providerCallback;
+        mStatsProvider = provider;
     }
 
     /** Start hardware offload. */
@@ -185,7 +183,7 @@
                         // and we need to synchronize stats and limits between
                         // software and hardware forwarding.
                         updateStatsForAllUpstreams();
-                        mStatsProvider.pushTetherStats();
+                        if (mStatsProvider != null) mStatsProvider.pushTetherStats();
                     }
 
                     @Override
@@ -198,7 +196,7 @@
                         // limits set take into account any software tethering
                         // traffic that has been happening in the meantime.
                         updateStatsForAllUpstreams();
-                        mStatsProvider.pushTetherStats();
+                        if (mStatsProvider != null) mStatsProvider.pushTetherStats();
                         // [2] (Re)Push all state.
                         computeAndPushLocalPrefixes(UpdateType.FORCE);
                         pushAllDownstreamState();
@@ -217,10 +215,12 @@
                         // TODO: rev the HAL so that it provides an interface name.
 
                         updateStatsForCurrentUpstream();
-                        mStatsProvider.pushTetherStats();
-                        // Push stats to service does not cause the service react to it immediately.
-                        // Inform the service about limit reached.
-                        if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached();
+                        if (mStatsProvider != null) {
+                            mStatsProvider.pushTetherStats();
+                            // Push stats to service does not cause the service react to it
+                            // immediately. Inform the service about limit reached.
+                            mStatsProvider.notifyLimitReached();
+                        }
                     }
 
                     @Override
@@ -263,13 +263,17 @@
     }
 
     @VisibleForTesting
-    class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider {
+    class OffloadTetheringStatsProvider extends NetworkStatsProvider {
         // These stats must only ever be touched on the handler thread.
         @NonNull
         private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
         @NonNull
         private NetworkStats mUidStats = new NetworkStats(0L, 0);
 
+        /**
+         * A helper function that collect tether stats from local hashmap. Note that this does not
+         * invoke binder call.
+         */
         @VisibleForTesting
         @NonNull
         NetworkStats getTetherStats(@NonNull StatsType how) {
@@ -280,14 +284,14 @@
                 final ForwardedStats value = kv.getValue();
                 final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
                         ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
-                stats = stats.addValues(entry);
+                stats = stats.addEntry(entry);
             }
 
             return stats;
         }
 
         @Override
-        public void setLimit(String iface, long quotaBytes) {
+        public void onSetLimit(String iface, long quotaBytes) {
             // Listen for all iface is necessary since upstream might be changed after limit
             // is set.
             mHandler.post(() -> {
@@ -315,13 +319,12 @@
          */
         public void pushTetherStats() {
             // TODO: remove the accumulated stats and report the diff from HAL directly.
-            if (null == mStatsProviderCb) return;
             final NetworkStats ifaceDiff =
                     getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
             final NetworkStats uidDiff =
                     getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
             try {
-                mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
+                notifyStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
                 mIfaceStats = mIfaceStats.add(ifaceDiff);
                 mUidStats = mUidStats.add(uidDiff);
             } catch (RuntimeException e) {
@@ -330,7 +333,7 @@
         }
 
         @Override
-        public void requestStatsUpdate(int token) {
+        public void onRequestStatsUpdate(int token) {
             // Do not attempt to update stats by querying the offload HAL
             // synchronously from a different thread than the Handler thread. http://b/64771555.
             mHandler.post(() -> {
@@ -340,7 +343,7 @@
         }
 
         @Override
-        public void setAlert(long quotaBytes) {
+        public void onSetAlert(long quotaBytes) {
             // TODO: Ask offload HAL to notify alert without stopping traffic.
         }
     }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index f89da84..3d8dbab 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -59,10 +59,8 @@
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
+import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+
 import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothPan;
@@ -72,7 +70,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
 import android.net.EthernetManager;
@@ -128,7 +125,6 @@
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
-import com.android.networkstack.tethering.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -224,14 +220,13 @@
     private final ActiveDataSubIdListener mActiveDataSubIdListener;
     private final ConnectedClientsTracker mConnectedClientsTracker;
     private final TetheringThreadExecutor mExecutor;
+    private final TetheringNotificationUpdater mNotificationUpdater;
     private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
     // All the usage of mTetheringEventCallback should run in the same thread.
     private ITetheringEventCallback mTetheringEventCallback = null;
 
     private volatile TetheringConfiguration mConfig;
     private InterfaceSet mCurrentUpstreamIfaceSet;
-    private Notification.Builder mTetheredNotificationBuilder;
-    private int mLastNotificationId;
 
     private boolean mRndisEnabled;       // track the RNDIS function enabled state
     // True iff. WiFi tethering should be started when soft AP is ready.
@@ -255,6 +250,7 @@
         mContext = mDeps.getContext();
         mNetd = mDeps.getINetd(mContext);
         mLooper = mDeps.getTetheringLooper();
+        mNotificationUpdater = mDeps.getNotificationUpdater(mContext);
 
         mPublicSync = new Object();
 
@@ -738,13 +734,10 @@
         final ArrayList<String> erroredList = new ArrayList<>();
         final ArrayList<Integer> lastErrorList = new ArrayList<>();
 
-        boolean wifiTethered = false;
-        boolean usbTethered = false;
-        boolean bluetoothTethered = false;
-
         final TetheringConfiguration cfg = mConfig;
         mTetherStatesParcel = new TetherStatesParcel();
 
+        int downstreamTypesMask = DOWNSTREAM_NONE;
         synchronized (mPublicSync) {
             for (int i = 0; i < mTetherStates.size(); i++) {
                 TetherState tetherState = mTetherStates.valueAt(i);
@@ -758,11 +751,11 @@
                     localOnlyList.add(iface);
                 } else if (tetherState.lastState == IpServer.STATE_TETHERED) {
                     if (cfg.isUsb(iface)) {
-                        usbTethered = true;
+                        downstreamTypesMask |= (1 << TETHERING_USB);
                     } else if (cfg.isWifi(iface)) {
-                        wifiTethered = true;
+                        downstreamTypesMask |= (1 << TETHERING_WIFI);
                     } else if (cfg.isBluetooth(iface)) {
-                        bluetoothTethered = true;
+                        downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
                     }
                     tetherList.add(iface);
                 }
@@ -796,98 +789,7 @@
                     "error", TextUtils.join(",", erroredList)));
         }
 
-        if (usbTethered) {
-            if (wifiTethered || bluetoothTethered) {
-                showTetheredNotification(R.drawable.stat_sys_tether_general);
-            } else {
-                showTetheredNotification(R.drawable.stat_sys_tether_usb);
-            }
-        } else if (wifiTethered) {
-            if (bluetoothTethered) {
-                showTetheredNotification(R.drawable.stat_sys_tether_general);
-            } else {
-                /* We now have a status bar icon for WifiTethering, so drop the notification */
-                clearTetheredNotification();
-            }
-        } else if (bluetoothTethered) {
-            showTetheredNotification(R.drawable.stat_sys_tether_bluetooth);
-        } else {
-            clearTetheredNotification();
-        }
-    }
-
-    private void showTetheredNotification(int id) {
-        showTetheredNotification(id, true);
-    }
-
-    @VisibleForTesting
-    protected void showTetheredNotification(int id, boolean tetheringOn) {
-        NotificationManager notificationManager =
-                (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
-                        .getSystemService(Context.NOTIFICATION_SERVICE);
-        if (notificationManager == null) {
-            return;
-        }
-        final NotificationChannel channel = new NotificationChannel(
-                "TETHERING_STATUS",
-                mContext.getResources().getString(R.string.notification_channel_tethering_status),
-                NotificationManager.IMPORTANCE_LOW);
-        notificationManager.createNotificationChannel(channel);
-
-        if (mLastNotificationId != 0) {
-            if (mLastNotificationId == id) {
-                return;
-            }
-            notificationManager.cancel(null, mLastNotificationId);
-            mLastNotificationId = 0;
-        }
-
-        Intent intent = new Intent();
-        intent.setClassName("com.android.settings", "com.android.settings.TetherSettings");
-        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
-
-        PendingIntent pi = PendingIntent.getActivity(
-                mContext.createContextAsUser(UserHandle.CURRENT, 0), 0, intent, 0, null);
-
-        Resources r = mContext.getResources();
-        final CharSequence title;
-        final CharSequence message;
-
-        if (tetheringOn) {
-            title = r.getText(R.string.tethered_notification_title);
-            message = r.getText(R.string.tethered_notification_message);
-        } else {
-            title = r.getText(R.string.disable_tether_notification_title);
-            message = r.getText(R.string.disable_tether_notification_message);
-        }
-
-        if (mTetheredNotificationBuilder == null) {
-            mTetheredNotificationBuilder = new Notification.Builder(mContext, channel.getId());
-            mTetheredNotificationBuilder.setWhen(0)
-                    .setOngoing(true)
-                    .setColor(mContext.getColor(
-                            android.R.color.system_notification_accent_color))
-                    .setVisibility(Notification.VISIBILITY_PUBLIC)
-                    .setCategory(Notification.CATEGORY_STATUS);
-        }
-        mTetheredNotificationBuilder.setSmallIcon(id)
-                .setContentTitle(title)
-                .setContentText(message)
-                .setContentIntent(pi);
-        mLastNotificationId = id;
-
-        notificationManager.notify(null, mLastNotificationId, mTetheredNotificationBuilder.build());
-    }
-
-    @VisibleForTesting
-    protected void clearTetheredNotification() {
-        NotificationManager notificationManager =
-                (NotificationManager) mContext.createContextAsUser(UserHandle.ALL, 0)
-                        .getSystemService(Context.NOTIFICATION_SERVICE);
-        if (notificationManager != null && mLastNotificationId != 0) {
-            notificationManager.cancel(null, mLastNotificationId);
-            mLastNotificationId = 0;
-        }
+        mNotificationUpdater.onDownstreamChanged(downstreamTypesMask);
     }
 
     private class StateReceiver extends BroadcastReceiver {
@@ -1081,12 +983,10 @@
                 return;
             }
 
-            mWrapper.clearTetheredNotification();
+            // TODO: Add user restrictions notification.
             final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0);
 
             if (newlyDisallowed && isTetheringActiveOnDevice) {
-                mWrapper.showTetheredNotification(
-                        R.drawable.stat_sys_tether_general, false);
                 mWrapper.untetherAll();
                 // TODO(b/148139325): send tetheringSupported on restriction change
             }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
index e019c3a..0330dad 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -26,6 +26,8 @@
 import android.os.IBinder;
 import android.os.Looper;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.util.StateMachine;
 
 import java.util.ArrayList;
@@ -102,6 +104,13 @@
     }
 
     /**
+     * Get a reference to the TetheringNotificationUpdater to be used by tethering.
+     */
+    public TetheringNotificationUpdater getNotificationUpdater(@NonNull final Context ctx) {
+        return new TetheringNotificationUpdater(ctx);
+    }
+
+    /**
      * Get tethering thread looper.
      */
     public abstract Looper getTetheringLooper();
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
new file mode 100644
index 0000000..b97f752
--- /dev/null
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.ArrayRes;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IntRange;
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.networkstack.tethering.R;
+
+/**
+ * A class to display tethering-related notifications.
+ *
+ * <p>This class is not thread safe, it is intended to be used only from the tethering handler
+ * thread. However the constructor is an exception, as it is called on another thread ;
+ * therefore for thread safety all members of this class MUST either be final or initialized
+ * to their default value (0, false or null).
+ *
+ * @hide
+ */
+public class TetheringNotificationUpdater {
+    private static final String TAG = TetheringNotificationUpdater.class.getSimpleName();
+    private static final String CHANNEL_ID = "TETHERING_STATUS";
+    private static final boolean NOTIFY_DONE = true;
+    private static final boolean NO_NOTIFY = false;
+    // Id to update and cancel tethering notification. Must be unique within the tethering app.
+    private static final int NOTIFY_ID = 20191115;
+    @VisibleForTesting
+    static final int NO_ICON_ID = 0;
+    @VisibleForTesting
+    static final int DOWNSTREAM_NONE = 0;
+    private final Context mContext;
+    private final NotificationManager mNotificationManager;
+    private final NotificationChannel mChannel;
+    // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
+    // This value has to be made 1 2 and 4, and OR'd with the others.
+    // WARNING : the constructor is called on a different thread. Thread safety therefore
+    // relies on this value being initialized to 0, and not any other value. If you need
+    // to change this, you will need to change the thread where the constructor is invoked,
+    // or to introduce synchronization.
+    private int mDownstreamTypesMask = DOWNSTREAM_NONE;
+
+    public TetheringNotificationUpdater(@NonNull final Context context) {
+        mContext = context;
+        mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+        mChannel = new NotificationChannel(
+                CHANNEL_ID,
+                context.getResources().getString(R.string.notification_channel_tethering_status),
+                NotificationManager.IMPORTANCE_LOW);
+        mNotificationManager.createNotificationChannel(mChannel);
+    }
+
+    /** Called when downstream has changed */
+    public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
+        if (mDownstreamTypesMask == downstreamTypesMask) return;
+        mDownstreamTypesMask = downstreamTypesMask;
+        updateNotification();
+    }
+
+    private void updateNotification() {
+        final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
+
+        if (tetheringInactive || setupNotification() == NO_NOTIFY) {
+            clearNotification();
+        }
+    }
+
+    private void clearNotification() {
+        mNotificationManager.cancel(null /* tag */, NOTIFY_ID);
+    }
+
+    /**
+     * Returns the downstream types mask which convert from given string.
+     *
+     * @param types This string has to be made by "WIFI", "USB", "BT", and OR'd with the others.
+     *
+     * @return downstream types mask value.
+     */
+    @IntRange(from = 0, to = 7)
+    private int getDownstreamTypesMask(@NonNull final String types) {
+        int downstreamTypesMask = DOWNSTREAM_NONE;
+        final String[] downstreams = types.split("\\|");
+        for (String downstream : downstreams) {
+            if ("USB".equals(downstream.trim())) {
+                downstreamTypesMask |= (1 << TETHERING_USB);
+            } else if ("WIFI".equals(downstream.trim())) {
+                downstreamTypesMask |= (1 << TETHERING_WIFI);
+            } else if ("BT".equals(downstream.trim())) {
+                downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
+            }
+        }
+        return downstreamTypesMask;
+    }
+
+    /**
+     * Returns the icons {@link android.util.SparseArray} which get from given string-array resource
+     * id.
+     *
+     * @param id String-array resource id
+     *
+     * @return {@link android.util.SparseArray} with downstream types and icon id info.
+     */
+    @NonNull
+    private SparseArray<Integer> getIcons(@ArrayRes int id) {
+        final Resources res = mContext.getResources();
+        final String[] array = res.getStringArray(id);
+        final SparseArray<Integer> icons = new SparseArray<>();
+        for (String config : array) {
+            if (TextUtils.isEmpty(config)) continue;
+
+            final String[] elements = config.split(";");
+            if (elements.length != 2) {
+                Log.wtf(TAG,
+                        "Unexpected format in Tethering notification configuration : " + config);
+                continue;
+            }
+
+            final String[] types = elements[0].split(",");
+            for (String type : types) {
+                int mask = getDownstreamTypesMask(type);
+                if (mask == DOWNSTREAM_NONE) continue;
+                icons.put(mask, res.getIdentifier(
+                        elements[1].trim(), null /* defType */, null /* defPackage */));
+            }
+        }
+        return icons;
+    }
+
+    private boolean setupNotification() {
+        final Resources res = mContext.getResources();
+        final SparseArray<Integer> downstreamIcons = getIcons(R.array.tethering_notification_icons);
+
+        final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID);
+        if (iconId == NO_ICON_ID) return NO_NOTIFY;
+
+        final String title = res.getString(R.string.tethering_notification_title);
+        final String message = res.getString(R.string.tethering_notification_message);
+
+        showNotification(iconId, title, message);
+        return NOTIFY_DONE;
+    }
+
+    private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
+            @NonNull final String message) {
+        final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
+        final PendingIntent pi = PendingIntent.getActivity(
+                mContext.createContextAsUser(UserHandle.CURRENT, 0),
+                0 /* requestCode */, intent, 0 /* flags */, null /* options */);
+        final Notification notification =
+                new Notification.Builder(mContext, mChannel.getId())
+                        .setSmallIcon(iconId)
+                        .setContentTitle(title)
+                        .setContentText(message)
+                        .setOngoing(true)
+                        .setColor(mContext.getColor(
+                                android.R.color.system_notification_accent_color))
+                        .setVisibility(Notification.VISIBILITY_PUBLIC)
+                        .setCategory(Notification.CATEGORY_STATUS)
+                        .setContentIntent(pi)
+                        .build();
+
+        mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification);
+    }
+}
diff --git a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
index d85389a..a20a0df 100644
--- a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
+++ b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
@@ -20,6 +20,7 @@
 import android.net.TetheredClient.AddressInfo
 import android.net.TetheringManager.TETHERING_BLUETOOTH
 import android.net.TetheringManager.TETHERING_USB
+import android.system.OsConstants.RT_SCOPE_UNIVERSE
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.testutils.assertParcelSane
@@ -30,11 +31,19 @@
 
 private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67))
 private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78))
-private val TEST_ADDR1 = LinkAddress(parseNumericAddress("192.168.113.3"), 24)
-private val TEST_ADDR2 = LinkAddress(parseNumericAddress("fe80::1:2:3"), 64)
+private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L)
+private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L)
 private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname")
 private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null)
 
+private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress(
+        parseNumericAddress(addr),
+        prefixLength,
+        0 /* flags */,
+        RT_SCOPE_UNIVERSE,
+        expTime /* deprecationTime */,
+        expTime /* expirationTime */)
+
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class TetheredClientTest {
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
index 56f3e21..1cdc3bb 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
@@ -46,23 +46,28 @@
 
     private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A")
     private val client1 = TetheredClient(client1Addr, listOf(
-            AddressInfo(LinkAddress("192.168.43.44/32"), null /* hostname */, clock.time + 20)),
+            makeAddrInfo("192.168.43.44/32", null /* hostname */, clock.time + 20)),
             TETHERING_WIFI)
     private val wifiClient1 = makeWifiClient(client1Addr)
     private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB")
-    private val client2Exp30AddrInfo = AddressInfo(
-            LinkAddress("192.168.43.45/32"), "my_hostname", clock.time + 30)
+    private val client2Exp30AddrInfo = makeAddrInfo(
+            "192.168.43.45/32", "my_hostname", clock.time + 30)
     private val client2 = TetheredClient(client2Addr, listOf(
             client2Exp30AddrInfo,
-            AddressInfo(LinkAddress("2001:db8:12::34/72"), "other_hostname", clock.time + 10)),
+            makeAddrInfo("2001:db8:12::34/72", "other_hostname", clock.time + 10)),
             TETHERING_WIFI)
     private val wifiClient2 = makeWifiClient(client2Addr)
     private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC")
     private val client3 = TetheredClient(client3Addr,
-            listOf(AddressInfo(LinkAddress("2001:db8:34::34/72"), "other_other_hostname",
-                    clock.time + 10)),
+            listOf(makeAddrInfo("2001:db8:34::34/72", "other_other_hostname", clock.time + 10)),
             TETHERING_USB)
 
+    private fun makeAddrInfo(addr: String, hostname: String?, expTime: Long) =
+            LinkAddress(addr).let {
+                AddressInfo(LinkAddress(it.address, it.prefixLength, it.flags, it.scope,
+                        expTime /* deprecationTime */, expTime /* expirationTime */), hostname)
+            }
+
     @Test
     fun testUpdateConnectedClients() {
         doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 7e62e5a..fe84086 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -33,6 +33,8 @@
 import static com.android.testutils.MiscAssertsKt.assertThrows;
 import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals;
 
+import static junit.framework.Assert.assertNotNull;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -61,8 +63,7 @@
 import android.net.NetworkStats;
 import android.net.NetworkStats.Entry;
 import android.net.RouteInfo;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.INetworkStatsProviderCallback;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.os.Looper;
@@ -108,12 +109,10 @@
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
     @Mock private NetworkStatsManager mStatsManager;
-    @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb;
+    @Mock private INetworkStatsProviderCallback mTetherStatsProviderCb;
+    private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider;
     private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
             ArgumentCaptor.forClass(ArrayList.class);
-    private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
-            mTetherStatsProviderCaptor =
-            ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
     private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
             ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
     private MockContentResolver mContentResolver;
@@ -126,8 +125,6 @@
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
         FakeSettingsProvider.clearSettingsProvider();
-        when(mStatsManager.registerNetworkStatsProvider(anyString(), any()))
-                .thenReturn(mTetherStatsProviderCb);
     }
 
     @After public void tearDown() throws Exception {
@@ -154,8 +151,14 @@
     private OffloadController makeOffloadController() throws Exception {
         OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
                 mHardware, mContentResolver, mStatsManager, new SharedLog("test"));
+        final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
+                tetherStatsProviderCaptor =
+                ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
         verify(mStatsManager).registerNetworkStatsProvider(anyString(),
-                mTetherStatsProviderCaptor.capture());
+                tetherStatsProviderCaptor.capture());
+        mTetherStatsProvider = tetherStatsProviderCaptor.getValue();
+        assertNotNull(mTetherStatsProvider);
+        mTetherStatsProvider.setProviderCallbackBinder(mTetherStatsProviderCb);
         return offload;
     }
 
@@ -413,9 +416,6 @@
         final OffloadController offload = makeOffloadController();
         offload.start();
 
-        final OffloadController.OffloadTetheringStatsProvider provider =
-                mTetherStatsProviderCaptor.getValue();
-
         final String ethernetIface = "eth1";
         final String mobileIface = "rmnet_data0";
 
@@ -443,15 +443,15 @@
         inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
 
         // Verify that the fetched stats are stored.
-        final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE);
-        final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID);
+        final NetworkStats ifaceStats = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
+        final NetworkStats uidStats = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
         final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
-                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
+                .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+                .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
 
         final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
-                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
+                .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+                .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
 
         assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats));
         assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats));
@@ -462,13 +462,12 @@
                 NetworkStats.class);
 
         // Force pushing stats update to verify the stats reported.
-        provider.pushTetherStats();
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
-                ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+        mTetherStatsProvider.pushTetherStats();
+        verify(mTetherStatsProviderCb, times(1))
+                .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
         assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue()));
         assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue()));
 
-
         when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
                 new ForwardedStats(100000, 100000));
         offload.setUpstreamLinkProperties(null);
@@ -483,31 +482,31 @@
         inOrder.verifyNoMoreInteractions();
 
         // Verify that the stored stats is accumulated.
-        final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE);
-        final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID);
+        final NetworkStats ifaceStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_IFACE);
+        final NetworkStats uidStatsAccu = mTetherStatsProvider.getTetherStats(STATS_PER_UID);
         final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
-                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
+                .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+                .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
 
         final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
-                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
+                .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+                .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
 
         assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu));
         assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu));
 
         // Verify that only diff of stats is reported.
         reset(mTetherStatsProviderCb);
-        provider.pushTetherStats();
+        mTetherStatsProvider.pushTetherStats();
         final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
-                .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
+                .addEntry(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
+                .addEntry(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
 
         final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
-                .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
-                .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
-                ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+                .addEntry(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
+                .addEntry(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
+        verify(mTetherStatsProviderCb, times(1))
+                .notifyStatsUpdated(anyInt(), ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
         assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue()));
         assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue()));
     }
@@ -529,19 +528,18 @@
         lp.setInterfaceName(ethernetIface);
         offload.setUpstreamLinkProperties(lp);
 
-        AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue();
         final InOrder inOrder = inOrder(mHardware);
         when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
         when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
 
         // Applying an interface quota to the current upstream immediately sends it to the hardware.
-        provider.setLimit(ethernetIface, ethernetLimit);
+        mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
         waitForIdle();
         inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
         inOrder.verifyNoMoreInteractions();
 
         // Applying an interface quota to another upstream does not take any immediate action.
-        provider.setLimit(mobileIface, mobileLimit);
+        mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
 
@@ -554,7 +552,7 @@
 
         // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
         // to Long.MAX_VALUE.
-        provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+        mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
         waitForIdle();
         inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
 
@@ -562,7 +560,7 @@
         when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
         lp.setInterfaceName(ethernetIface);
         offload.setUpstreamLinkProperties(lp);
-        provider.setLimit(mobileIface, mobileLimit);
+        mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
 
@@ -571,7 +569,7 @@
         when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
         lp.setInterfaceName(mobileIface);
         offload.setUpstreamLinkProperties(lp);
-        provider.setLimit(mobileIface, mobileLimit);
+        mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
         waitForIdle();
         inOrder.verify(mHardware).getForwardedStats(ethernetIface);
         inOrder.verify(mHardware).stopOffloadControl();
@@ -587,7 +585,7 @@
 
         OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
         callback.onStoppedLimitReached();
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+        verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
     }
 
     @Test
@@ -691,7 +689,7 @@
         verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
         verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
         // TODO: verify the exact stats reported.
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+        verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
         verifyNoMoreInteractions(mTetherStatsProviderCb);
         verifyNoMoreInteractions(mHardware);
     }
@@ -756,7 +754,7 @@
         // Verify forwarded stats behaviour.
         verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
         verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
-        verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+        verify(mTetherStatsProviderCb, times(1)).notifyStatsUpdated(anyInt(), any(), any());
         verifyNoMoreInteractions(mTetherStatsProviderCb);
 
         // TODO: verify local prefixes and downstreams are also pushed to the HAL.
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index af7ad66..820c852 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -46,6 +46,8 @@
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+import static com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -53,7 +55,6 @@
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
@@ -188,6 +189,7 @@
     @Mock private NetworkRequest mNetworkRequest;
     @Mock private ConnectivityManager mCm;
     @Mock private EthernetManager mEm;
+    @Mock private TetheringNotificationUpdater mNotificationUpdater;
 
     private final MockIpServerDependencies mIpServerDependencies =
             spy(new MockIpServerDependencies());
@@ -207,6 +209,7 @@
     private PhoneStateListener mPhoneStateListener;
     private InterfaceConfigurationParcel mInterfaceConfiguration;
 
+
     private class TestContext extends BroadcastInterceptingContext {
         TestContext(Context base) {
             super(base);
@@ -249,11 +252,6 @@
             if (TelephonyManager.class.equals(serviceClass)) return Context.TELEPHONY_SERVICE;
             return super.getSystemServiceName(serviceClass);
         }
-
-        @Override
-        public Context createContextAsUser(UserHandle user, int flags) {
-            return mContext;
-        }
     }
 
     public class MockIpServerDependencies extends IpServer.Dependencies {
@@ -315,12 +313,10 @@
     public class MockTetheringDependencies extends TetheringDependencies {
         StateMachine mUpstreamNetworkMonitorMasterSM;
         ArrayList<IpServer> mIpv6CoordinatorNotifyList;
-        int mIsTetheringSupportedCalls;
 
         public void reset() {
             mUpstreamNetworkMonitorMasterSM = null;
             mIpv6CoordinatorNotifyList = null;
-            mIsTetheringSupportedCalls = 0;
         }
 
         @Override
@@ -354,7 +350,6 @@
 
         @Override
         public boolean isTetheringSupported() {
-            mIsTetheringSupportedCalls++;
             return true;
         }
 
@@ -384,6 +379,11 @@
             // TODO: add test for bluetooth tethering.
             return null;
         }
+
+        @Override
+        public TetheringNotificationUpdater getNotificationUpdater(Context ctx) {
+            return mNotificationUpdater;
+        }
     }
 
     private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -472,7 +472,6 @@
         when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats);
 
         mServiceContext = new TestContext(mContext);
-        when(mContext.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(null);
         mContentResolver = new MockContentResolver(mServiceContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         mIntents = new Vector<>();
@@ -605,7 +604,8 @@
         // it creates a IpServer and sends out a broadcast indicating that the
         // interface is "available".
         if (emulateInterfaceStatusChanged) {
-            assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls);
+            // There is 1 IpServer state change event: STATE_AVAILABLE
+            verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
             verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
             verify(mWifiManager).updateInterfaceIpState(
                     TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
@@ -689,9 +689,8 @@
         verifyNoMoreInteractions(mWifiManager);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
         verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
-        // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
-        // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
-        assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
+        // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY
+        verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
 
         // Emulate externally-visible WifiManager effects, when hotspot mode
         // is being torn down.
@@ -917,7 +916,8 @@
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
         mLooper.dispatchAll();
 
-        assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls);
+        // There is 1 IpServer state change event: STATE_AVAILABLE
+        verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
         verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
@@ -961,9 +961,9 @@
         // In tethering mode, in the default configuration, an explicit request
         // for a mobile network is also made.
         verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
-        // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
-        // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
-        assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
+        // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_TETHERED
+        verify(mNotificationUpdater, times(1)).onDownstreamChanged(DOWNSTREAM_NONE);
+        verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI));
 
         /////
         // We do not currently emulate any upstream being found.
@@ -1034,9 +1034,10 @@
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
-        // There are 3 state change event:
-        // AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
-        assertEquals(3, mTetheringDependencies.mIsTetheringSupportedCalls);
+        // There are 3 IpServer state change event:
+        //         STATE_AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
+        verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
+        verify(mNotificationUpdater, times(1)).onDownstreamChanged(eq(1 << TETHERING_WIFI));
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
         // This is called, but will throw.
         verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
@@ -1071,9 +1072,6 @@
         ural.onUserRestrictionsChanged();
 
         verify(mockTethering, times(expectedInteractionsWithShowNotification))
-                .showTetheredNotification(anyInt(), eq(false));
-
-        verify(mockTethering, times(expectedInteractionsWithShowNotification))
                 .untetherAll();
     }
 
@@ -1429,9 +1427,8 @@
         verifyNoMoreInteractions(mNetd);
         verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
         verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
-        // This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
-        // and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
-        assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
+        // There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY
+        verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
 
         assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME));
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 5d699c0..5d97d21 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -1232,6 +1232,7 @@
      */
     public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
             @NonNull Region outRegion) {
+        windowId = resolveParentWindowIdLocked(windowId);
         final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
         if (observer != null) {
             return observer.computePartialInteractiveRegionForWindowLocked(windowId, outRegion);
@@ -1436,6 +1437,7 @@
      */
     @Nullable
     public WindowInfo findWindowInfoByIdLocked(int windowId) {
+        windowId = resolveParentWindowIdLocked(windowId);
         final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
         if (observer != null) {
             return observer.findWindowInfoByIdLocked(windowId);
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 3376f2b..5de8171 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -62,7 +62,7 @@
  * side flow.
  *
  * <p>
- * This class is thread safe.
+ * This class should hold the same lock as {@link Session} as they call into each other.
  */
 final class InlineSuggestionSession {
 
@@ -105,12 +105,12 @@
     private boolean mImeInputViewStarted = false;
 
     InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
-            int userId, ComponentName componentName, Handler handler) {
+            int userId, ComponentName componentName, Handler handler, Object lock) {
         mInputMethodManagerInternal = inputMethodManagerInternal;
         mUserId = userId;
         mComponentName = componentName;
         mHandler = handler;
-        mLock = new Object();
+        mLock = lock;
         mImeStatusListener = new ImeStatusListener() {
             @Override
             public void onInputMethodStartInputView(AutofillId imeFieldId) {
@@ -261,29 +261,27 @@
             mHandler = handler;
             mTimeoutCallback = () -> {
                 Log.w(TAG, "Timed out waiting for IME callback InlineSuggestionsRequest.");
-                synchronized (mLock) {
-                    completeIfNotLocked(null);
-                }
+                completeIfNot(null);
             };
             mHandler.postDelayed(mTimeoutCallback, INLINE_REQUEST_TIMEOUT_MS);
         }
 
-        private void completeIfNotLocked(@Nullable ImeResponse response) {
-            if (mResponse.isDone()) {
-                return;
+        private void completeIfNot(@Nullable ImeResponse response) {
+            synchronized (mLock) {
+                if (mResponse.isDone()) {
+                    return;
+                }
+                mResponse.complete(response);
+                mRequestConsumer.accept(response == null ? null : response.mRequest);
+                mHandler.removeCallbacks(mTimeoutCallback);
             }
-            mResponse.complete(response);
-            mRequestConsumer.accept(response == null ? null : response.mRequest);
-            mHandler.removeCallbacks(mTimeoutCallback);
         }
 
         @BinderThread
         @Override
         public void onInlineSuggestionsUnsupported() throws RemoteException {
             if (sDebug) Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
-            synchronized (mLock) {
-                completeIfNotLocked(null);
-            }
+            completeIfNot(null);
         }
 
         @BinderThread
@@ -302,13 +300,9 @@
                 mImeStatusListener.onInputMethodFinishInputView(imeFieldId);
             }
             if (request != null && callback != null) {
-                synchronized (mLock) {
-                    completeIfNotLocked(new ImeResponse(request, callback));
-                }
+                completeIfNot(new ImeResponse(request, callback));
             } else {
-                synchronized (mLock) {
-                    completeIfNotLocked(null);
-                }
+                completeIfNot(null);
             }
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 5064663..de31118 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -756,7 +756,7 @@
         setClientLocked(client);
 
         mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId,
-                componentName, handler);
+                componentName, handler, mLock);
 
         mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 942d563..9e19ad2 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -98,9 +98,9 @@
         "android.hardware.power-V1.0-java",
         "android.hardware.tv.cec-V1.0-java",
         "android.hardware.vibrator-java",
+        "android.net.ipsec.ike.stubs.module_libs_api",
         "app-compat-annotations",
         "framework-tethering-stubs-module_libs_api",
-        "ike-stubs",
     ],
 
     required: [
@@ -128,7 +128,6 @@
         "android.hidl.manager-V1.2-java",
         "dnsresolver_aidl_interface-V2-java",
         "netd_event_listener_interface-java",
-        "ike-stubs",
         "overlayable_policy_aidl-java",
     ],
 
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index dadcd4e..9fddafb 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -991,4 +991,9 @@
      * @param enabled true if visibility blocks should be logged
      */
     public abstract void setVisibilityLogging(String packageName, boolean enabled);
+
+    /**
+     * Returns if a package name is a valid system package.
+     */
+    public abstract boolean isSystemPackage(@NonNull String packageName);
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ecf1f13..deae459 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2083,9 +2083,9 @@
     }
 
     private void enforceNetworkFactoryPermission() {
-        mContext.enforceCallingOrSelfPermission(
+        enforceAnyPermissionOf(
                 android.Manifest.permission.NETWORK_FACTORY,
-                "ConnectivityService");
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
     private boolean checkSettingsPermission() {
@@ -7817,7 +7817,7 @@
                 getMatchingPermissionedCallbacks(nai);
         for (final IConnectivityDiagnosticsCallback cb : results) {
             try {
-                cb.onConnectivityReport(report);
+                cb.onConnectivityReportAvailable(report);
             } catch (RemoteException ex) {
                 loge("Error invoking onConnectivityReport", ex);
             }
@@ -7892,8 +7892,15 @@
             return true;
         }
 
-        if (!mLocationPermissionChecker.checkLocationPermission(
-                callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+        // LocationPermissionChecker#checkLocationPermission can throw SecurityException if the uid
+        // and package name don't match. Throwing on the CS thread is not acceptable, so wrap the
+        // call in a try-catch.
+        try {
+            if (!mLocationPermissionChecker.checkLocationPermission(
+                    callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
+                return false;
+            }
+        } catch (SecurityException e) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 318a030..191a9bc 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -22,13 +22,9 @@
 import android.gsi.GsiProgress;
 import android.gsi.IGsiService;
 import android.gsi.IGsiServiceCallback;
-import android.gsi.IGsid;
 import android.os.Environment;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.image.IDynamicSystemService;
@@ -42,9 +38,8 @@
  * DynamicSystemService implements IDynamicSystemService. It provides permission check before
  * passing requests to gsid
  */
-public class DynamicSystemService extends IDynamicSystemService.Stub implements DeathRecipient {
+public class DynamicSystemService extends IDynamicSystemService.Stub {
     private static final String TAG = "DynamicSystemService";
-    private static final String NO_SERVICE_ERROR = "no gsiservice";
     private static final int GSID_ROUGH_TIMEOUT_MS = 8192;
     private static final String PATH_DEFAULT = "/data/gsi/";
     private Context mContext;
@@ -55,57 +50,12 @@
         mContext = context;
     }
 
-    private static IGsiService connect(DeathRecipient recipient) throws RemoteException {
-        IBinder binder = ServiceManager.getService("gsiservice");
-        if (binder == null) {
-            return null;
-        }
-        /**
-         * The init will restart gsiservice if it crashed and the proxy object will need to be
-         * re-initialized in this case.
-         */
-        binder.linkToDeath(recipient, 0);
-
-        IGsid gsid = IGsid.Stub.asInterface(binder);
-        return gsid.getClient();
-    }
-
-    /** implements DeathRecipient */
-    @Override
-    public void binderDied() {
-        Slog.w(TAG, "gsiservice died; reconnecting");
-        synchronized (this) {
-            mGsiService = null;
-        }
-    }
-
     private IGsiService getGsiService() throws RemoteException {
         checkPermission();
-
-        if (!"running".equals(SystemProperties.get("init.svc.gsid"))) {
-            SystemProperties.set("ctl.start", "gsid");
+        if (mGsiService != null) {
+            return mGsiService;
         }
-
-        for (int sleepMs = 64; sleepMs <= (GSID_ROUGH_TIMEOUT_MS << 1); sleepMs <<= 1) {
-            synchronized (this) {
-                if (mGsiService == null) {
-                    mGsiService = connect(this);
-                }
-                if (mGsiService != null) {
-                    return mGsiService;
-                }
-            }
-
-            try {
-                Slog.d(TAG, "GsiService is not ready, wait for " + sleepMs + "ms");
-                Thread.sleep(sleepMs);
-            } catch (InterruptedException e) {
-                Slog.e(TAG, "Interrupted when waiting for GSID");
-                return null;
-            }
-        }
-
-        throw new RemoteException(NO_SERVICE_ERROR);
+        return IGsiService.Stub.asInterface(waitForService("gsiservice"));
     }
 
     private void checkPermission() {
@@ -133,6 +83,7 @@
     @Override
     public boolean startInstallation(String dsuSlot) throws RemoteException {
         IGsiService service = getGsiService();
+        mGsiService = service;
         // priority from high to low: sysprop -> sdcard -> /data
         String path = SystemProperties.get("os.aot.path");
         if (path.isEmpty()) {
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index b464422..52a1b5a 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -354,6 +354,10 @@
      */
     public void onPackageFailure(List<VersionedPackage> packages,
             @FailureReasons int failureReason) {
+        if (packages == null) {
+            Slog.w(TAG, "Could not resolve a list of failing packages");
+            return;
+        }
         mLongTaskHandler.post(() -> {
             synchronized (mLock) {
                 if (mAllObservers.isEmpty()) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b7d050a..2c220d3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -154,7 +154,6 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.pm.Installer;
 import com.android.server.storage.AppFuseBridge;
 import com.android.server.storage.StorageSessionController;
@@ -233,6 +232,8 @@
     private static final String FUSE_ENABLED = "fuse_enabled";
     private static final boolean DEFAULT_FUSE_ENABLED = true;
 
+    private final Set<Integer> mFuseMountedUser = new ArraySet<>();
+
     public static class Lifecycle extends SystemService {
         private StorageManagerService mStorageManagerService;
 
@@ -1497,6 +1498,9 @@
 
     @GuardedBy("mLock")
     private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
+        if (vol.type == VolumeInfo.TYPE_EMULATED && newState != VolumeInfo.STATE_MOUNTED) {
+            mFuseMountedUser.remove(vol.getMountUserId());
+        }
         // Remember that we saw this volume so we're ready to accept user
         // metadata, or so we can annoy them when a private volume is ejected
         if (!TextUtils.isEmpty(vol.fsUuid)) {
@@ -2075,39 +2079,86 @@
         mount(vol);
     }
 
+    private void remountAppStorageDirs(Map<Integer, String> pidPkgMap, int userId) {
+        for (Entry<Integer, String> entry : pidPkgMap.entrySet()) {
+            final int pid = entry.getKey();
+            final String packageName = entry.getValue();
+            Slog.i(TAG, "Remounting storage for pid: " + pid);
+            final String[] sharedPackages =
+                    mPmInternal.getSharedUserPackagesForPackage(packageName, userId);
+            final int uid = mPmInternal.getPackageUidInternal(packageName, 0, userId);
+            final String[] packages =
+                    sharedPackages.length != 0 ? sharedPackages : new String[]{packageName};
+            try {
+                mVold.remountAppStorageDirs(uid, pid, packages);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        }
+    }
+
     private void mount(VolumeInfo vol) {
         try {
             // TODO(b/135341433): Remove paranoid logging when FUSE is stable
             Slog.i(TAG, "Mounting volume " + vol);
             mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() {
-                    @Override
-                    public boolean onVolumeChecking(FileDescriptor fd, String path,
-                            String internalPath) {
-                        vol.path = path;
-                        vol.internalPath = internalPath;
-                        ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
-                        try {
-                            mStorageSessionController.onVolumeMount(pfd, vol);
-                            return true;
-                        } catch (ExternalStorageServiceException e) {
-                            Slog.e(TAG, "Failed to mount volume " + vol, e);
+                @Override
+                public boolean onVolumeChecking(FileDescriptor fd, String path,
+                        String internalPath) {
+                    vol.path = path;
+                    vol.internalPath = internalPath;
+                    ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd);
+                    try {
+                        mStorageSessionController.onVolumeMount(pfd, vol);
+                        return true;
+                    } catch (ExternalStorageServiceException e) {
+                        Slog.e(TAG, "Failed to mount volume " + vol, e);
 
-                            int nextResetSeconds = REMOTE_TIMEOUT_SECONDS * 2;
-                            Slog.i(TAG, "Scheduling reset in " + nextResetSeconds + "s");
-                            mHandler.removeMessages(H_RESET);
-                            mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
-                                    TimeUnit.SECONDS.toMillis(nextResetSeconds));
-                            return false;
-                        } finally {
-                            try {
-                                pfd.close();
-                            } catch (Exception e) {
-                                Slog.e(TAG, "Failed to close FUSE device fd", e);
-                            }
+                        int nextResetSeconds = REMOTE_TIMEOUT_SECONDS * 2;
+                        Slog.i(TAG, "Scheduling reset in " + nextResetSeconds + "s");
+                        mHandler.removeMessages(H_RESET);
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET),
+                                TimeUnit.SECONDS.toMillis(nextResetSeconds));
+                        return false;
+                    } finally {
+                        try {
+                            pfd.close();
+                        } catch (Exception e) {
+                            Slog.e(TAG, "Failed to close FUSE device fd", e);
                         }
                     }
-                });
+                }
+            });
             Slog.i(TAG, "Mounted volume " + vol);
+            if (vol.type == VolumeInfo.TYPE_EMULATED) {
+                final int userId = vol.getMountUserId();
+                mFuseMountedUser.add(userId);
+                // Async remount app storage so it won't block the main thread.
+                new Thread(() -> {
+                    Map<Integer, String> pidPkgMap = null;
+                    // getProcessesWithPendingBindMounts() could fail when a new app process is
+                    // starting and it's not planning to mount storage dirs in zygote, but it's
+                    // rare, so we retry 5 times and hope we can get the result successfully.
+                    for (int i = 0; i < 5; i++) {
+                        try {
+                            pidPkgMap = LocalServices.getService(ActivityManagerInternal.class)
+                                    .getProcessesWithPendingBindMounts(vol.getMountUserId());
+                            break;
+                        } catch (IllegalStateException e) {
+                            Slog.i(TAG, "Some processes are starting, retry");
+                            // Wait 100ms and retry so hope the pending process is started.
+                            SystemClock.sleep(100);
+                        }
+                    }
+                    if (pidPkgMap != null) {
+                        remountAppStorageDirs(pidPkgMap, userId);
+                    } else {
+                        Slog.wtf(TAG, "Not able to getStorageNotOptimizedProcesses() after"
+                                + " 5 retries");
+                    }
+
+                }).start();
+            }
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
@@ -4309,7 +4360,8 @@
             pw.println();
             pw.println("mObbPathToStateMap:");
             pw.increaseIndent();
-            final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
+            final Iterator<Entry<String, ObbState>> maps =
+                    mObbPathToStateMap.entrySet().iterator();
             while (maps.hasNext()) {
                 final Entry<String, ObbState> e = maps.next();
                 pw.print(e.getKey());
@@ -4350,45 +4402,41 @@
         }
 
         /**
-         * Check if fuse is running in target user, if it's running then setup its obb directories.
-         * TODO: System server should store a list of active pids that obb is not mounted and use it.
+         * Check if fuse is running in target user, if it's running then setup its storage dirs.
+         * Return true if storage dirs are mounted.
          */
         @Override
-        public void prepareObbDirs(int userId, Set<String> packageList, String processName) {
-            String fuseRunningUsersList = SystemProperties.get("vold.fuse_running_users", "");
-            String[] fuseRunningUsers = fuseRunningUsersList.split(",");
-            boolean fuseReady = false;
-            String targetUserId = String.valueOf(userId);
-            for (String user : fuseRunningUsers) {
-                if (targetUserId.equals(user)) {
-                    fuseReady = true;
-                }
+        public boolean prepareStorageDirs(int userId, Set<String> packageList,
+                String processName) {
+            if (!mFuseMountedUser.contains(userId)) {
+                Slog.w(TAG, "User " + userId + " is not unlocked yet so skip mounting obb");
+                return false;
             }
-            if (fuseReady) {
-                try {
-                    final IVold vold = IVold.Stub.asInterface(
-                            ServiceManager.getServiceOrThrow("vold"));
-                    for (String pkg : packageList) {
-                        final String packageObbDir =
-                                String.format("/storage/emulated/%d/Android/obb/%s/", userId, pkg);
-                        final String packageDataDir =
-                                String.format("/storage/emulated/%d/Android/data/%s/",
-                                        userId, pkg);
+            try {
+                final IVold vold = IVold.Stub.asInterface(
+                        ServiceManager.getServiceOrThrow("vold"));
+                for (String pkg : packageList) {
+                    final String packageObbDir =
+                            String.format("/storage/emulated/%d/Android/obb/%s/", userId, pkg);
+                    final String packageDataDir =
+                            String.format("/storage/emulated/%d/Android/data/%s/",
+                                    userId, pkg);
 
-                        // Create package obb and data dir if it doesn't exist.
-                        File file = new File(packageObbDir);
-                        if (!file.exists()) {
-                            vold.setupAppDir(packageObbDir, mPmInternal.getPackage(pkg).getUid());
-                        }
-                        file = new File(packageDataDir);
-                        if (!file.exists()) {
-                            vold.setupAppDir(packageDataDir, mPmInternal.getPackage(pkg).getUid());
-                        }
+                    // Create package obb and data dir if it doesn't exist.
+                    File file = new File(packageObbDir);
+                    if (!file.exists()) {
+                        vold.setupAppDir(packageObbDir, mPmInternal.getPackage(pkg).getUid());
                     }
-                } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
-                    Slog.e(TAG, "Unable to create obb and data directories for " + processName, e);
+                    file = new File(packageDataDir);
+                    if (!file.exists()) {
+                        vold.setupAppDir(packageDataDir, mPmInternal.getPackage(pkg).getUid());
+                    }
                 }
+            } catch (ServiceManager.ServiceNotFoundException | RemoteException e) {
+                Slog.e(TAG, "Unable to create obb and data directories for " + processName,e);
+                return false;
             }
+            return true;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index 35a9802..f772a4a 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -53,6 +53,7 @@
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -230,6 +231,7 @@
             @Nullable LinkProperties lp,
             boolean isMetered,
             int callingUid,
+            @NonNull int[] administratorUids,
             @NonNull IBinder binder)
             throws RemoteException, SocketException {
         Objects.requireNonNull(looper, "missing Looper");
@@ -248,6 +250,7 @@
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
+        nc.setAdministratorUids(intArrayToList(administratorUids));
         if (!isMetered) {
             nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
         }
@@ -290,6 +293,14 @@
         return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder);
     }
 
+    private List<Integer> intArrayToList(@NonNull int[] array) {
+        final List<Integer> list = new ArrayList<>(array.length);
+        for (final int i : array) {
+            list.add(i);
+        }
+        return list;
+    }
+
     /**
      * Sets up a Network with extremely limited privileges, guarded by the MANAGE_TEST_NETWORKS
      * permission.
@@ -301,6 +312,7 @@
             @NonNull String iface,
             @Nullable LinkProperties lp,
             boolean isMetered,
+            @NonNull int[] administratorUids,
             @NonNull IBinder binder) {
         enforceTestNetworkPermissions(mContext);
 
@@ -335,6 +347,7 @@
                                             lp,
                                             isMetered,
                                             callingUid,
+                                            administratorUids,
                                             binder);
 
                             mTestNetworkTracker.put(agent.getNetwork().netId, agent);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 4c1740f..0ebb5bb 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -137,6 +137,10 @@
 
     private static final boolean SHOW_DUNGEON_NOTIFICATION = false;
 
+    //TODO: remove this when development is done.
+    private static final int DEBUG_FGS_ALLOW_WHILE_IN_USE = 0;
+    private static final int DEBUG_FGS_ENFORCE_TYPE = 1;
+
     // How long we wait for a service to finish executing.
     static final int SERVICE_TIMEOUT = 20*1000;
 
@@ -4915,10 +4919,20 @@
                 if (!r.isForeground) {
                     continue;
                 }
-                if (!r.mAllowWhileInUsePermissionInFgs
-                        && r.mInfoDenyWhileInUsePermissionInFgs != null) {
-                    final String msg = r.mInfoDenyWhileInUsePermissionInFgs
-                            + " affected while-in-use permission:"
+                if (mode == DEBUG_FGS_ALLOW_WHILE_IN_USE) {
+                    if (!r.mAllowWhileInUsePermissionInFgs
+                            && r.mInfoDenyWhileInUsePermissionInFgs != null) {
+                        final String msg = r.mInfoDenyWhileInUsePermissionInFgs
+                                + " affected while-in-use permission:"
+                                + AppOpsManager.opToPublicName(op);
+                        Slog.wtf(TAG, msg);
+                    }
+                } else if (mode == DEBUG_FGS_ENFORCE_TYPE) {
+                    final String msg =
+                            "FGS Missing foregroundServiceType in manifest file [callingPackage: "
+                            + r.mRecentCallingPackage
+                            + "; intent:" + r.intent.getIntent()
+                            + "] affected while-in-use permission:"
                             + AppOpsManager.opToPublicName(op);
                     Slog.wtf(TAG, msg);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f64272b..ebca1f7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2179,9 +2179,17 @@
 
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+                Process.enableFreezer(false);
+            }
+
             if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                     "meminfo", pw)) return;
             PriorityDump.dump(mPriorityDumper, fd, pw, args);
+
+            if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+                Process.enableFreezer(true);
+            }
         }
     }
 
@@ -2193,9 +2201,17 @@
 
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+                Process.enableFreezer(false);
+            }
+
             if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                     "gfxinfo", pw)) return;
             mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);
+
+            if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+                Process.enableFreezer(true);
+            }
         }
     }
 
@@ -2207,9 +2223,17 @@
 
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+                Process.enableFreezer(false);
+            }
+
             if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                     "dbinfo", pw)) return;
             mActivityManagerService.dumpDbInfo(fd, pw, args);
+
+            if (mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.useFreezer()) {
+                Process.enableFreezer(true);
+            }
         }
     }
 
@@ -17538,8 +17562,11 @@
 
     final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) {
         if (proc.thread != null && proc.baseProcessTracker != null) {
-            proc.baseProcessTracker.setState(
-                    proc.getReportedProcState(), memFactor, now, proc.pkgList.mPkgList);
+            final int procState = proc.getReportedProcState();
+            if (procState != PROCESS_STATE_NONEXISTENT) {
+                proc.baseProcessTracker.setState(
+                        procState, memFactor, now, proc.pkgList.mPkgList);
+            }
         }
     }
 
@@ -18714,6 +18741,11 @@
         }
 
         @Override
+        public Map<Integer, String> getProcessesWithPendingBindMounts(int userId) {
+            return mProcessList.getProcessesWithPendingBindMounts(userId);
+        }
+
+        @Override
         public boolean isSystemReady() {
             // no need to synchronize(this) just to read & return the value
             return mSystemReady;
@@ -19810,6 +19842,7 @@
 
         void setPermissions(@Nullable String[] permissions) {
             mPermissions = permissions;
+            PackageManager.invalidatePackageInfoCache();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
index ba89fce..03f4a54 100644
--- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -16,15 +16,20 @@
 
 package com.android.server.am;
 
+import static android.app.AppOpsManager.OP_NONE;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.app.Activity;
 import android.app.BroadcastOptions;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
+import android.os.BugreportManager;
+import android.os.BugreportParams;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -110,9 +115,17 @@
         options.setBackgroundActivityStartsAllowed(true);
         final long identity = Binder.clearCallingIdentity();
         try {
-            context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
+            // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to
+            // let ResultBroadcastReceiver know the handler app is available.
+            context.sendOrderedBroadcastAsUser(intent,
+                    UserHandle.of(handlerUser),
                     android.Manifest.permission.DUMP,
-                    options.toBundle());
+                    OP_NONE, options.toBundle(),
+                    new ResultBroadcastReceiver(),
+                    /* scheduler= */ null,
+                    Activity.RESULT_CANCELED,
+                    /* initialData= */ null,
+                    /* initialExtras= */ null);
         } catch (RuntimeException e) {
             Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
             return false;
@@ -176,4 +189,19 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    private static class ResultBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (getResultCode() == Activity.RESULT_OK) {
+                return;
+            }
+
+            Slog.w(TAG, "Request bug report because handler app seems to be not available.");
+            BugreportManager bugreportManager = context.getSystemService(BugreportManager.class);
+            bugreportManager.requestBugreport(
+                    new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE),
+                    /* shareTitle= */null, /* shareDescription= */ null);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 48ceba9..8527ae9 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -115,6 +115,10 @@
                 DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE,
                 WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class,
                 WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT));
+        sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
+                DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.FINGER_TO_CURSOR_DISTANCE,
+                WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE, int.class,
+                WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT));
         sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
                 DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES,
                 WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES, boolean.class,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index b584ea5..2b1534b 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -75,7 +75,6 @@
 import android.app.ApplicationExitInfo;
 import android.app.usage.UsageEvents;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledAfter;
 import android.content.Context;
 import android.content.pm.ServiceInfo;
@@ -149,12 +148,13 @@
      * capabilities.
      */
     @ChangeId
-    //TODO: change to @EnabledAfter when enforcing the feature.
-    @Disabled
+    @EnabledAfter(targetSdkVersion=android.os.Build.VERSION_CODES.Q)
     static final long CAMERA_MICROPHONE_CAPABILITY_CHANGE_ID = 136219221L;
 
     //TODO: remove this when development is done.
     private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
+    private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA = 1 << 30;
+    private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 29;
 
     /**
      * For some direct access we need to power manager.
@@ -1513,10 +1513,12 @@
                     if (enabled) {
                         capabilityFromFGS |=
                                 (fgsType & FOREGROUND_SERVICE_TYPE_CAMERA)
-                                        != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA : 0;
+                                        != 0 ? PROCESS_CAPABILITY_FOREGROUND_CAMERA
+                                        : TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA;
                         capabilityFromFGS |=
                                 (fgsType & FOREGROUND_SERVICE_TYPE_MICROPHONE)
-                                        != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE : 0;
+                                        != 0 ? PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
+                                        : TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
                     } else {
                         capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_CAMERA
                                 | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0e9970e..57bd42b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -99,6 +99,7 @@
 import android.system.Os;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.LongSparseArray;
 import android.util.Pair;
@@ -136,8 +137,11 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Activity manager code dealing with processes.
@@ -154,7 +158,8 @@
             "persist.sys.vold_app_data_isolation_enabled";
 
     // A device config to control the minimum target SDK to enable app data isolation
-    static final String ANDROID_APP_DATA_ISOLATION_MIN_SDK = "android_app_data_isolation_min_sdk";
+    static final String ANDROID_APP_DATA_ISOLATION_MIN_SDK =
+            "android_app_data_isolation_min_sdk";
 
     // The minimum time we allow between crashes, for us to consider this
     // application to be bad and stop and its services and reject broadcasts.
@@ -799,6 +804,31 @@
         }
     }
 
+    /**
+     * Get a map of pid and package name that process of that pid Android/data and Android/obb
+     * directory is not mounted to lowerfs to speed up access.
+     */
+    Map<Integer, String> getProcessesWithPendingBindMounts(int userId) {
+        final Map<Integer, String> pidPackageMap = new HashMap<>();
+        synchronized (mService) {
+            for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+                final ProcessRecord record = mLruProcesses.get(i);
+                if (record.userId != userId || !record.bindMountPending) {
+                    continue;
+                }
+                final int pid = record.pid;
+                // It can happen when app process is starting, but zygote work is not done yet so
+                // system does not this pid record yet.
+                if (pid == 0) {
+                    throw new IllegalStateException("Pending process is not started yet,"
+                            + "retry later");
+                }
+                pidPackageMap.put(pid, record.info.packageName);
+            }
+            return pidPackageMap;
+        }
+    }
+
     private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
         // Scale buckets from avail memory: at 300MB we use the lowest values to
         // 700MB or more for the top values.
@@ -1680,6 +1710,7 @@
         if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
             checkSlow(startTime, "startProcess: removing from pids map");
             mService.removePidLocked(app);
+            app.bindMountPending = false;
             mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             checkSlow(startTime, "startProcess: done removing from pids map");
             app.setPid(0);
@@ -2162,8 +2193,10 @@
             }
 
             final Map<String, Pair<String, Long>> pkgDataInfoMap;
+            boolean bindMountAppStorageDirs = false;
 
             if (shouldIsolateAppData(app)) {
+                bindMountAppStorageDirs = mVoldAppDataIsolationEnabled;
                 // Get all packages belongs to the same shared uid. sharedPackages is empty array
                 // if it doesn't have shared uid.
                 final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
@@ -2172,11 +2205,17 @@
                 pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0
                         ? new String[]{app.info.packageName} : sharedPackages, uid);
 
+                int userId = UserHandle.getUserId(uid);
                 if (mVoldAppDataIsolationEnabled) {
                     StorageManagerInternal storageManagerInternal = LocalServices.getService(
-                                StorageManagerInternal.class);
-                    storageManagerInternal.prepareObbDirs(UserHandle.getUserId(uid),
-                            pkgDataInfoMap.keySet(), app.processName);
+                            StorageManagerInternal.class);
+                    if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
+                            app.processName)) {
+                        // Cannot prepare Android/app and Android/obb directory,
+                        // so we won't mount it in zygote.
+                        app.bindMountPending = true;
+                        bindMountAppStorageDirs = false;
+                    }
                 }
             } else {
                 pkgDataInfoMap = null;
@@ -2197,7 +2236,7 @@
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
                         /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
-                        app.mDisabledCompatChanges, pkgDataInfoMap,
+                        app.mDisabledCompatChanges, pkgDataInfoMap, bindMountAppStorageDirs,
                         new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
@@ -2205,6 +2244,7 @@
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                         isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
+                        bindMountAppStorageDirs,
                         new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             }
             checkSlow(startTime, "startProcess: returned from zygote!");
@@ -2663,6 +2703,7 @@
             int pid = app.pid;
             if (pid > 0) {
                 mService.removePidLocked(app);
+                app.bindMountPending = false;
                 mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
                 mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
                 if (app.isolated) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index a78caac..e7f66bb 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -251,6 +251,7 @@
     int adjSourceProcState;     // Debugging: proc state of adjSource's process.
     Object adjTarget;           // Debugging: target component impacting oom_adj.
     Runnable crashHandler;      // Optional local handler to be invoked in the process crash.
+    boolean bindMountPending;   // True if Android/obb and Android/data need to be bind mount .
 
     // Cache of last retrieve memory info and uptime, to throttle how frequently
     // apps can requyest it.
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index f04c716..2e2241f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -41,6 +41,7 @@
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OpEventProxyInfo;
 import static android.app.AppOpsManager.RestrictionBypass;
+import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
 import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
@@ -244,9 +245,15 @@
 
     private static final int MAX_UNFORWARED_OPS = 10;
     private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
+    private static final int RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS = 300000;
 
     //TODO: remove this when development is done.
     private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31;
+    private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA = 1 << 30;
+    private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 29;
+    private static final int DEBUG_FGS_ALLOW_WHILE_IN_USE = 0;
+    private static final int DEBUG_FGS_ENFORCE_TYPE = 1;
+
 
     final Context mContext;
     final AtomicFile mFile;
@@ -549,7 +556,7 @@
                                 // The FGS has the location capability, but due to FGS BG start
                                 // restriction it lost the capability, use temp location capability
                                 // to mark this case.
-                                maybeShowWhileInUseDebugToast(op, mode);
+                                maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
                                 return MODE_IGNORED;
                             } else {
                                 return MODE_IGNORED;
@@ -557,15 +564,25 @@
                         case OP_CAMERA:
                             if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
                                 return MODE_ALLOWED;
+                            } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA)
+                                    != 0) {
+                                // CHANGE TO MODE_IGNORED when enforce this feature.
+                                maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+                                return MODE_ALLOWED;
                             } else {
-                                maybeShowWhileInUseDebugToast(op, mode);
+                                maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
                                 return MODE_IGNORED;
                             }
                         case OP_RECORD_AUDIO:
                             if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
                                 return MODE_ALLOWED;
+                            } else if  ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE)
+                                    != 0) {
+                                // CHANGE TO MODE_IGNORED when enforce this feature.
+                                maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+                                return MODE_ALLOWED;
                             } else {
-                                maybeShowWhileInUseDebugToast(op, mode);
+                                maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
                                 return MODE_IGNORED;
                             }
                         default:
@@ -580,15 +597,24 @@
                     case OP_CAMERA:
                         if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
                             return MODE_ALLOWED;
+                        } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
+                            // CHANGE TO MODE_IGNORED when enforce this feature.
+                            maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+                            return MODE_ALLOWED;
                         } else {
-                            maybeShowWhileInUseDebugToast(op, mode);
+                            maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
                             return MODE_IGNORED;
                         }
                     case OP_RECORD_AUDIO:
                         if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
                             return MODE_ALLOWED;
+                        } else if ((capability & TEMP_PROCESS_CAPABILITY_FOREGROUND_MICROPHONE)
+                                != 0) {
+                            // CHANGE TO MODE_IGNORED when enforce this feature.
+                            maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ENFORCE_TYPE);
+                            return MODE_ALLOWED;
                         } else {
-                            maybeShowWhileInUseDebugToast(op, mode);
+                            maybeShowWhileInUseDebugToast(op, DEBUG_FGS_ALLOW_WHILE_IN_USE);
                             return MODE_IGNORED;
                         }
                     default:
@@ -1657,17 +1683,14 @@
             }
         }, packageAddedFilter);
 
-        List<String> packageNames = getPackageNamesForSampling();
-        synchronized (this) {
-            resamplePackageAndAppOpLocked(packageNames);
-        }
-
-        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+        mHandler.postDelayed(new Runnable() {
             @Override
             public void run() {
+                List<String> packageNames = getPackageNamesForSampling();
+                resamplePackageAndAppOpLocked(packageNames);
                 initializeRarelyUsedPackagesList(new ArraySet<>(packageNames));
             }
-        });
+        }, RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS);
 
         PackageManagerInternal packageManagerInternal = LocalServices.getService(
                 PackageManagerInternal.class);
@@ -5618,7 +5641,7 @@
         int uid = Binder.getCallingUid();
         Objects.requireNonNull(packageName);
         synchronized (this) {
-            switchPackageIfRarelyUsedLocked(packageName);
+            switchPackageIfBootTimeOrRarelyUsedLocked(packageName);
             if (!packageName.equals(mSampledPackage)) {
                 return new MessageSamplingConfig(OP_NONE, 0,
                         Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
@@ -5648,7 +5671,7 @@
     private void reportRuntimeAppOpAccessMessageAsyncLocked(int uid,
             @NonNull String packageName, int opCode, @Nullable String attributionTag,
             @NonNull String message) {
-        switchPackageIfRarelyUsedLocked(packageName);
+        switchPackageIfBootTimeOrRarelyUsedLocked(packageName);
         if (!Objects.equals(mSampledPackage, packageName)) {
             return;
         }
@@ -5699,11 +5722,16 @@
 
     /**
      * Checks if package is in the list of rarely used package and starts watching the new package
-     * to collect incoming message.
+     * to collect incoming message or if collection is happening in first minutes since boot.
      * @param packageName
      */
-    private void switchPackageIfRarelyUsedLocked(@NonNull String packageName) {
-        if (mRarelyUsedPackages.contains(packageName)) {
+    private void switchPackageIfBootTimeOrRarelyUsedLocked(@NonNull String packageName) {
+        if (mSampledPackage == null) {
+            if (ThreadLocalRandom.current().nextFloat() < 0.1f) {
+                mSamplingStrategy = SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
+                resampleAppOpForPackageLocked(packageName);
+            }
+        } else if (mRarelyUsedPackages.contains(packageName)) {
             mRarelyUsedPackages.remove(packageName);
             if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
                 mSamplingStrategy = SAMPLING_STRATEGY_RARELY_USED;
@@ -5765,6 +5793,10 @@
                             }
                         }
                         synchronized (this) {
+                            int numPkgs = mRarelyUsedPackages.size();
+                            for (int i = 0; i < numPkgs; i++) {
+                                candidates.add(mRarelyUsedPackages.valueAt(i));
+                            }
                             mRarelyUsedPackages = candidates;
                         }
                     }
diff --git a/services/core/java/com/android/server/hdmi/TEST_MAPPING b/services/core/java/com/android/server/hdmi/TEST_MAPPING
new file mode 100644
index 0000000..7245ec4
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/TEST_MAPPING
@@ -0,0 +1,34 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.hdmi"
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.hdmi"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index c2d1364..44ab438 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -50,6 +50,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
@@ -73,6 +74,7 @@
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateEncodingException;
@@ -88,6 +90,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /** Implementation of {@link AppIntegrityManagerService}. */
 public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
@@ -242,6 +245,11 @@
         return new ParceledListSlice<>(rules);
     }
 
+    @Override
+    public List<String> getWhitelistedRuleProviders() throws RemoteException {
+        return getAllowedRuleProviders();
+    }
+
     private void handleIntegrityVerification(Intent intent) {
         int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
 
@@ -473,9 +481,9 @@
 
         SourceStampVerificationResult sourceStampVerificationResult;
         if (installationPath.isDirectory()) {
-            try {
+            try (Stream<Path> filesList = Files.list(installationPath.toPath())) {
                 List<String> apkFiles =
-                        Files.list(installationPath.toPath())
+                        filesList
                                 .map(path -> path.toAbsolutePath().toString())
                                 .collect(Collectors.toList());
                 sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c1c3760..1b4ec8a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -530,6 +530,11 @@
             return Settings.Global.getInt(contentResolver, keyName, defaultValue);
         }
 
+        public int settingsSecureGetInt(ContentResolver contentResolver, String keyName,
+                int defaultValue, int userId) {
+            return Settings.Secure.getIntForUser(contentResolver, keyName, defaultValue, userId);
+        }
+
         public @NonNull ManagedProfilePasswordCache getManagedProfilePasswordCache() {
             try {
                 java.security.KeyStore ks = java.security.KeyStore.getInstance("AndroidKeyStore");
@@ -1010,6 +1015,13 @@
         }
     }
 
+    private void enforceFrpResolved() {
+        if (mInjector.settingsSecureGetInt(mContext.getContentResolver(),
+                Settings.Secure.SECURE_FRP_MODE, 0, UserHandle.USER_SYSTEM) == 1) {
+            throw new SecurityException("Cannot change credential while FRP is not resolved yet");
+        }
+    }
+
     private final void checkWritePermission(int userId) {
         mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
     }
@@ -1572,6 +1584,7 @@
                     "This operation requires secure lock screen feature");
         }
         checkWritePermission(userId);
+        enforceFrpResolved();
 
         // When changing credential for profiles with unified challenge, some callers
         // will pass in empty credential while others will pass in the credential of
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index aad7203..5c97e90 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -127,8 +127,10 @@
     @Override
     public void requestCreateSession(long requestId, String packageName, String routeId,
             Bundle sessionHints) {
-        // Handle it as an internal transfer.
+
         transferToRoute(requestId, SYSTEM_SESSION_ID, routeId);
+        mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
+        //TODO: We should call after the session info is changed.
     }
 
     @Override
@@ -240,7 +242,6 @@
             builder.addTransferableRoute(route.getId());
         }
 
-
         RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
         if (Objects.equals(oldSessionInfo, newSessionInfo)) {
             return false;
@@ -261,6 +262,7 @@
         synchronized (mLock) {
             sessionInfo = mSessionInfos.get(0);
         }
+
         mCallback.onSessionUpdated(this, sessionInfo);
     }
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 563dcf7..48f1ddb 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -21,7 +21,7 @@
 import android.annotation.NonNull;
 import android.net.Network;
 import android.net.NetworkTemplate;
-import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProvider;
 import android.telephony.SubscriptionPlan;
 
 import java.util.Set;
@@ -130,8 +130,8 @@
             Set<String> packageNames, int userId);
 
     /**
-     *  Notifies that the specified {@link AbstractNetworkStatsProvider} has reached its quota
-     *  which was set through {@link AbstractNetworkStatsProvider#setLimit(String, long)}.
+     *  Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+     *  which was set through {@link NetworkStatsProvider#onSetLimit(String, long)}.
      *
      * @param tag the human readable identifier of the custom network stats provider.
      */
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0b1c91f..caf2914 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -75,7 +75,7 @@
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.net.netstats.provider.AbstractNetworkStatsProvider.QUOTA_UNLIMITED;
+import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
 import static android.os.Trace.TRACE_TAG_NETWORK;
 import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
 import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED;
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index 22b01be..75ffe35 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -229,7 +229,7 @@
                     entry.txPackets += reader.nextLong();
                 }
 
-                stats.addEntry(entry);
+                stats.insertEntry(entry);
                 reader.finishLine();
             }
         } catch (NullPointerException|NumberFormatException e) {
@@ -279,7 +279,7 @@
                 entry.txBytes = reader.nextLong();
                 entry.txPackets = reader.nextLong();
 
-                stats.addEntry(entry);
+                stats.insertEntry(entry);
                 reader.finishLine();
             }
         } catch (NullPointerException|NumberFormatException e) {
@@ -439,7 +439,7 @@
                 if ((limitIfaces == null || ArrayUtils.contains(limitIfaces, entry.iface))
                         && (limitUid == UID_ALL || limitUid == entry.uid)
                         && (limitTag == TAG_ALL || limitTag == entry.tag)) {
-                    stats.addEntry(entry);
+                    stats.insertEntry(entry);
                 }
 
                 reader.finishLine();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 4af31b0..10136b3 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -17,12 +17,14 @@
 package com.android.server.net;
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.NETWORK_STATS_PROVIDER;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_UID;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.ConnectivityManager.isNetworkTypeMobile;
 import static android.net.NetworkStack.checkNetworkStackPermission;
@@ -101,7 +103,7 @@
 import android.net.TrafficStats;
 import android.net.netstats.provider.INetworkStatsProvider;
 import android.net.netstats.provider.INetworkStatsProviderCallback;
-import android.net.netstats.provider.NetworkStatsProviderCallback;
+import android.net.netstats.provider.NetworkStatsProvider;
 import android.os.BestClock;
 import android.os.Binder;
 import android.os.DropBoxManager;
@@ -556,7 +558,7 @@
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
-        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setAlert(mGlobalAlertBytes));
+        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetAlert(mGlobalAlertBytes));
     }
 
     @Override
@@ -757,7 +759,7 @@
         final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
 
         final NetworkStats stats = new NetworkStats(end - start, 1);
-        stats.addEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
+        stats.insertEntry(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE,
                 METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, entry.rxBytes, entry.rxPackets,
                 entry.txBytes, entry.txPackets, entry.operations));
         return stats;
@@ -1374,7 +1376,8 @@
         Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate");
         final int registeredCallbackCount = mStatsProviderCbList.getRegisteredCallbackCount();
         mStatsProviderSem.drainPermits();
-        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */));
+        invokeForAllStatsProviderCallbacks(
+                (cb) -> cb.mProvider.onRequestStatsUpdate(0 /* unused */));
         try {
             mStatsProviderSem.tryAcquire(registeredCallbackCount,
                     MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS);
@@ -1549,7 +1552,7 @@
         @Override
         public void setStatsProviderLimitAsync(@NonNull String iface, long quota) {
             Slog.v(TAG, "setStatsProviderLimitAsync(" + iface + "," + quota + ")");
-            invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.setLimit(iface, quota));
+            invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetLimit(iface, quota));
         }
     }
 
@@ -1793,6 +1796,24 @@
         }
     }
 
+    // TODO: It is copied from ConnectivityService, consider refactor these check permission
+    //  functions to a proper util.
+    private boolean checkAnyPermissionOf(String... permissions) {
+        for (String permission : permissions) {
+            if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void enforceAnyPermissionOf(String... permissions) {
+        if (!checkAnyPermissionOf(permissions)) {
+            throw new SecurityException("Requires one of the following permissions: "
+                    + String.join(", ", permissions) + ".");
+        }
+    }
+
     /**
      * Registers a custom provider of {@link android.net.NetworkStats} to combine the network
      * statistics that cannot be seen by the kernel to system. To unregister, invoke the
@@ -1800,16 +1821,15 @@
      *
      * @param tag a human readable identifier of the custom network stats provider.
      * @param provider the {@link INetworkStatsProvider} binder corresponding to the
-     *                 {@link android.net.netstats.provider.AbstractNetworkStatsProvider} to be
-     *                 registered.
+     *                 {@link NetworkStatsProvider} to be registered.
      *
-     * @return a binder interface of
-     *         {@link android.net.netstats.provider.NetworkStatsProviderCallback}, which can be
-     *         used to report events to the system.
+     * @return a {@link INetworkStatsProviderCallback} binder
+     *         interface, which can be used to report events to the system.
      */
     public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider(
             @NonNull String tag, @NonNull INetworkStatsProvider provider) {
-        mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+        enforceAnyPermissionOf(NETWORK_STATS_PROVIDER,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         Objects.requireNonNull(provider, "provider is null");
         Objects.requireNonNull(tag, "tag is null");
         try {
@@ -1910,7 +1930,7 @@
         }
 
         @Override
-        public void onStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
+        public void notifyStatsUpdated(int token, @Nullable NetworkStats ifaceStats,
                 @Nullable NetworkStats uidStats) {
             // TODO: 1. Use token to map ifaces to correct NetworkIdentity.
             //       2. Store the difference and store it directly to the recorder.
@@ -1922,12 +1942,12 @@
         }
 
         @Override
-        public void onAlertReached() throws RemoteException {
+        public void notifyAlertReached() throws RemoteException {
             mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */);
         }
 
         @Override
-        public void onLimitReached() {
+        public void notifyLimitReached() {
             Log.d(TAG, mTag + ": onLimitReached");
             LocalServices.getService(NetworkPolicyManagerInternal.class)
                     .onStatsProviderLimitReached(mTag);
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index c9c8042..c96880c 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -15,9 +15,27 @@
 */
 package com.android.server.notification;
 
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.FLAG_BUBBLE;
+import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+
+import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
+import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Person;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.ArrayList;
+
 /**
  * Determines whether a bubble can be shown for this notification
  */
@@ -25,10 +43,15 @@
     private static final String TAG = "BubbleExtractor";
     private static final boolean DBG = false;
 
+    private BubbleChecker mBubbleChecker;
     private RankingConfig mConfig;
+    private ActivityManager mActivityManager;
+    private Context mContext;
 
-    public void initialize(Context ctx, NotificationUsageStats usageStats) {
+    public void initialize(Context context, NotificationUsageStats usageStats) {
         if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+        mContext = context;
+        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
     }
 
     public RankingReconsideration process(NotificationRecord record) {
@@ -41,6 +64,12 @@
             if (DBG) Slog.d(TAG, "missing config");
             return null;
         }
+
+        if (mBubbleChecker == null) {
+            if (DBG) Slog.d(TAG, "missing bubble checker");
+            return null;
+        }
+
         boolean appCanShowBubble =
                 mConfig.areBubblesAllowed(record.getSbn().getPackageName(), record.getSbn().getUid());
         if (!mConfig.bubblesEnabled() || !appCanShowBubble) {
@@ -52,7 +81,12 @@
                 record.setAllowBubble(appCanShowBubble);
             }
         }
-
+        final boolean applyFlag = mBubbleChecker.isNotificationAppropriateToBubble(record);
+        if (applyFlag) {
+            record.getNotification().flags |= FLAG_BUBBLE;
+        } else {
+            record.getNotification().flags &= ~FLAG_BUBBLE;
+        }
         return null;
     }
 
@@ -64,4 +98,185 @@
     @Override
     public void setZenHelper(ZenModeHelper helper) {
     }
+
+    /**
+     * Expected to be called after {@link #setConfig(RankingConfig)} has occurred.
+     */
+    void setShortcutHelper(ShortcutHelper helper) {
+        if (mConfig == null) {
+            if (DBG) Slog.d(TAG, "setting shortcut helper prior to setConfig");
+            return;
+        }
+        mBubbleChecker = new BubbleChecker(mContext, helper, mConfig, mActivityManager);
+    }
+
+    @VisibleForTesting
+    void setBubbleChecker(BubbleChecker checker) {
+        mBubbleChecker = checker;
+    }
+
+    /**
+     * Encapsulates special checks to see if a notification can be flagged as a bubble. This
+     * makes testing a bit easier.
+     */
+    public static class BubbleChecker {
+
+        private ActivityManager mActivityManager;
+        private RankingConfig mRankingConfig;
+        private Context mContext;
+        private ShortcutHelper mShortcutHelper;
+
+        BubbleChecker(Context context, ShortcutHelper helper, RankingConfig config,
+                ActivityManager activityManager) {
+            mContext = context;
+            mActivityManager = activityManager;
+            mShortcutHelper = helper;
+            mRankingConfig = config;
+        }
+
+        /**
+         * @return whether the provided notification record is allowed to be represented as a
+         * bubble, accounting for user choice & policy.
+         */
+        public boolean isNotificationAppropriateToBubble(NotificationRecord r) {
+            final String pkg = r.getSbn().getPackageName();
+            final int userId = r.getSbn().getUser().getIdentifier();
+            Notification notification = r.getNotification();
+            if (!canBubble(r, pkg, userId)) {
+                // no log: canBubble has its own
+                return false;
+            }
+
+            if (mActivityManager.isLowRamDevice()) {
+                logBubbleError(r.getKey(), "low ram device");
+                return false;
+            }
+
+            // At this point the bubble must fulfill communication policy
+
+            // Communication always needs a person
+            ArrayList<Person> peopleList = notification.extras != null
+                    ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
+                    : null;
+            // Message style requires a person & it's not included in the list
+            boolean isMessageStyle = Notification.MessagingStyle.class.equals(
+                    notification.getNotificationStyle());
+            if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
+                logBubbleError(r.getKey(), "Must have a person and be "
+                        + "Notification.MessageStyle or Notification.CATEGORY_CALL");
+                return false;
+            }
+
+            // Communication is a message or a call
+            boolean isCall = CATEGORY_CALL.equals(notification.category);
+            boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
+            if (hasForegroundService && !isCall) {
+                logBubbleError(r.getKey(),
+                        "foreground services must be Notification.CATEGORY_CALL to bubble");
+                return false;
+            }
+            if (isMessageStyle) {
+                return true;
+            } else if (isCall) {
+                if (hasForegroundService) {
+                    return true;
+                }
+                logBubbleError(r.getKey(), "calls require foreground service");
+                return false;
+            }
+            logBubbleError(r.getKey(), "Must be "
+                    + "Notification.MessageStyle or Notification.CATEGORY_CALL");
+            return false;
+        }
+
+        /**
+         * @return whether the user has enabled the provided notification to bubble, does not
+         * account for policy.
+         */
+        @VisibleForTesting
+        boolean canBubble(NotificationRecord r, String pkg, int userId) {
+            Notification notification = r.getNotification();
+            Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
+            if (metadata == null) {
+                // no log: no need to inform dev if they didn't attach bubble metadata
+                return false;
+            }
+            if (!mRankingConfig.bubblesEnabled()) {
+                logBubbleError(r.getKey(), "bubbles disabled for user: " + userId);
+                return false;
+            }
+            if (!mRankingConfig.areBubblesAllowed(pkg, userId)) {
+                logBubbleError(r.getKey(),
+                        "bubbles for package: " + pkg + " disabled for user: " + userId);
+                return false;
+            }
+            if (!r.getChannel().canBubble()) {
+                logBubbleError(r.getKey(),
+                        "bubbles for channel " + r.getChannel().getId() + " disabled");
+                return false;
+            }
+
+            String shortcutId = metadata.getShortcutId();
+            boolean shortcutValid = shortcutId != null
+                    && mShortcutHelper.hasValidShortcutInfo(shortcutId, pkg, r.getUser());
+            if (metadata.getBubbleIntent() == null && !shortcutValid) {
+                // Should have a shortcut if intent is null
+                logBubbleError(r.getKey(),
+                        "couldn't find valid shortcut for bubble with shortcutId: " + shortcutId);
+                return false;
+            }
+            if (shortcutValid) {
+                return true;
+            }
+            // no log: canLaunch method has the failure log
+            return canLaunchInActivityView(mContext, metadata.getBubbleIntent(), pkg);
+        }
+
+        /**
+         * Whether an intent is properly configured to display in an {@link
+         * android.app.ActivityView}.
+         *
+         * @param context       the context to use.
+         * @param pendingIntent the pending intent of the bubble.
+         * @param packageName   the notification package name for this bubble.
+         */
+        // Keep checks in sync with BubbleController#canLaunchInActivityView.
+        @VisibleForTesting
+        protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
+                String packageName) {
+            if (pendingIntent == null) {
+                Slog.w(TAG, "Unable to create bubble -- no intent");
+                return false;
+            }
+
+            Intent intent = pendingIntent.getIntent();
+
+            ActivityInfo info = intent != null
+                    ? intent.resolveActivityInfo(context.getPackageManager(), 0)
+                    : null;
+            if (info == null) {
+                FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
+                        packageName,
+                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
+                Slog.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
+                        + intent);
+                return false;
+            }
+            if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
+                FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED,
+                        packageName,
+                        BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
+                Slog.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
+                        + intent);
+                return false;
+            }
+            return true;
+        }
+
+        private void logBubbleError(String key, String failureMessage) {
+            if (DBG) {
+                Slog.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 69a5b35..20ad87a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -17,7 +17,6 @@
 package com.android.server.notification;
 
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
-import static android.app.Notification.CATEGORY_CALL;
 import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -51,9 +50,6 @@
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
 import static android.content.Context.BIND_NOT_PERCEPTIBLE;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.content.pm.PackageManager.MATCH_ALL;
@@ -93,8 +89,6 @@
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
-import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
-import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
@@ -131,8 +125,6 @@
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteInput;
 import android.app.StatsManager;
 import android.app.StatusBarManager;
 import android.app.UriGrantsManager;
@@ -152,7 +144,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.LauncherApps;
@@ -160,7 +151,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
-import android.content.pm.ShortcutInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -251,7 +241,6 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.TriPredicate;
@@ -296,7 +285,6 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map.Entry;
@@ -421,7 +409,7 @@
     private RoleObserver mRoleObserver;
     private UserManager mUm;
     private IPlatformCompat mPlatformCompat;
-    private LauncherApps mLauncherAppsService;
+    private ShortcutHelper mShortcutHelper;
 
     final IBinder mForegroundToken = new Binder();
     private WorkerHandler mHandler;
@@ -497,7 +485,8 @@
             "allow-secure-notifications-on-lockscreen";
     private static final String LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE = "value";
 
-    private RankingHelper mRankingHelper;
+    @VisibleForTesting
+    RankingHelper mRankingHelper;
     @VisibleForTesting
     PreferencesHelper mPreferencesHelper;
 
@@ -931,6 +920,8 @@
                         .setType(MetricsEvent.TYPE_ACTION)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, nv.rank)
                         .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_COUNT, nv.count));
+                mNotificationRecordLogger.log(
+                        NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLICKED, r);
                 EventLogTags.writeNotificationClicked(key,
                         r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
                         nv.rank, nv.count);
@@ -970,7 +961,8 @@
                                 generatedByAssistant ? 1 : 0)
                         .addTaggedData(MetricsEvent.NOTIFICATION_LOCATION,
                                 nv.location.toMetricsEventEnum()));
-
+                mNotificationRecordLogger.log(
+                        NotificationRecordLogger.NotificationEvent.NOTIFICATION_ACTION_CLICKED, r);
                 EventLogTags.writeNotificationActionClicked(key, actionIndex,
                         r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
                         nv.rank, nv.count);
@@ -1004,6 +996,8 @@
         public void onPanelRevealed(boolean clearEffects, int items) {
             MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL);
             MetricsLogger.histogram(getContext(), "note_load", items);
+            mNotificationRecordLogger.log(
+                    NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_OPEN);
             EventLogTags.writeNotificationPanelRevealed(items);
             if (clearEffects) {
                 clearEffects();
@@ -1014,6 +1008,8 @@
         @Override
         public void onPanelHidden() {
             MetricsLogger.hidden(getContext(), MetricsEvent.NOTIFICATION_PANEL);
+            mNotificationRecordLogger.log(
+                    NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_CLOSE);
             EventLogTags.writeNotificationPanelHidden();
             mAssistants.onPanelHidden();
         }
@@ -1103,6 +1099,10 @@
                         MetricsLogger.action(r.getItemLogMaker()
                                 .setType(expanded ? MetricsEvent.TYPE_DETAIL
                                         : MetricsEvent.TYPE_COLLAPSE));
+                        mNotificationRecordLogger.log(
+                                NotificationRecordLogger.NotificationEvent.fromExpanded(expanded,
+                                        userAction),
+                                r);
                     }
                     if (expanded && userAction) {
                         r.recordExpanded();
@@ -1124,6 +1124,9 @@
                     mMetricsLogger.write(r.getLogMaker()
                             .setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
                             .setType(MetricsEvent.TYPE_ACTION));
+                    mNotificationRecordLogger.log(
+                            NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
+                            r);
                     reportUserInteraction(r);
                     mAssistants.notifyAssistantNotificationDirectReplyLocked(r.getSbn());
                 }
@@ -1166,6 +1169,9 @@
                                     MetricsEvent.NOTIFICATION_SMART_REPLY_MODIFIED_BEFORE_SENDING,
                                     modifiedBeforeSending ? 1 : 0);
                     mMetricsLogger.write(logMaker);
+                    mNotificationRecordLogger.log(
+                            NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
+                            r);
                     // Treat clicking on a smart reply as a user interaction.
                     reportUserInteraction(r);
                     mAssistants.notifyAssistantSuggestedReplySent(
@@ -1186,13 +1192,30 @@
 
         @Override
         public void onNotificationBubbleChanged(String key, boolean isBubble) {
+            String pkg;
+            synchronized (mNotificationLock) {
+                NotificationRecord r = mNotificationsByKey.get(key);
+                pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null;
+            }
+            boolean isAppForeground = pkg != null
+                    && mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
-                    final StatusBarNotification n = r.getSbn();
-                    final int callingUid = n.getUid();
-                    final String pkg = n.getPackageName();
-                    applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isBubble);
+                    if (!isBubble) {
+                        // This happens if the user has dismissed the bubble but the notification
+                        // is still active in the shade, enqueuing would create a bubble since
+                        // the notification is technically allowed. Flip the flag so that
+                        // apps querying noMan will know that their notification is not showing
+                        // as a bubble.
+                        r.getNotification().flags &= ~FLAG_BUBBLE;
+                    } else {
+                        // Enqueue will trigger resort & if the flag is allowed to be true it'll
+                        // be applied there.
+                        r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+                        mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(),
+                                r, isAppForeground));
+                    }
                 }
             }
         }
@@ -1219,6 +1242,7 @@
                         flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
                     }
                     data.setFlags(flags);
+                    r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
                     mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r,
                             true /* isAppForeground */));
                 }
@@ -1310,6 +1334,9 @@
                             MetricsEvent.NOTIFICATION_SMART_REPLY_EDIT_BEFORE_SENDING,
                             r.getEditChoicesBeforeSending() ? 1 : 0);
             mMetricsLogger.write(logMaker);
+            mNotificationRecordLogger.log(
+                    NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLY_VISIBLE,
+                    r);
         }
     }
 
@@ -1595,80 +1622,6 @@
         }
     };
 
-    // Key: packageName Value: <shortcutId, notifId>
-    private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
-
-    private boolean mLauncherAppsCallbackRegistered;
-
-    // Bubbles can be created based on a shortcut, we need to listen for changes to
-    // that shortcut so that we may update the bubble appropriately.
-    private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() {
-        @Override
-        public void onPackageRemoved(String packageName, UserHandle user) {
-        }
-
-        @Override
-        public void onPackageAdded(String packageName, UserHandle user) {
-        }
-
-        @Override
-        public void onPackageChanged(String packageName, UserHandle user) {
-        }
-
-        @Override
-        public void onPackagesAvailable(String[] packageNames, UserHandle user,
-                boolean replacing) {
-        }
-
-        @Override
-        public void onPackagesUnavailable(String[] packageNames, UserHandle user,
-                boolean replacing) {
-        }
-
-        @Override
-        public void onShortcutsChanged(@NonNull String packageName,
-                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
-            HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName);
-            boolean isAppForeground = packageName != null
-                    && mActivityManager.getPackageImportance(packageName) == IMPORTANCE_FOREGROUND;
-            ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
-            if (shortcutBubbles != null) {
-                // If we can't find one of our bubbles in the shortcut list, that bubble needs
-                // to be removed.
-                for (String shortcutId : shortcutBubbles.keySet()) {
-                    boolean foundShortcut = false;
-                    for (int i = 0; i < shortcuts.size(); i++) {
-                        if (shortcuts.get(i).getId().equals(shortcutId)) {
-                            foundShortcut = true;
-                            break;
-                        }
-                    }
-                    if (!foundShortcut) {
-                        bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId));
-                    }
-                }
-            }
-
-            // Do the removals
-            for (int i = 0; i < bubbleKeysToRemove.size(); i++) {
-                // update flag bubble
-                String bubbleKey = bubbleKeysToRemove.get(i);
-                synchronized (mNotificationLock) {
-                    NotificationRecord r = mNotificationsByKey.get(bubbleKey);
-                    if (r != null) {
-                        final StatusBarNotification n = r.getSbn();
-                        final int callingUid = n.getUid();
-                        final String pkg = n.getPackageName();
-                        applyFlagBubble(r, pkg, callingUid, null /* oldEntry */, isAppForeground);
-                        mHandler.post(new EnqueueNotificationRunnable(user.getIdentifier(), r,
-                                false /* isAppForeground */));
-                    }
-                }
-            }
-        }
-    };
-
-
     private final class SettingsObserver extends ContentObserver {
         private final Uri NOTIFICATION_BADGING_URI
                 = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
@@ -1763,8 +1716,8 @@
     }
 
     @VisibleForTesting
-    void setLauncherApps(LauncherApps launcherApps) {
-        mLauncherAppsService = launcherApps;
+    ShortcutHelper getShortcutHelper() {
+        return mShortcutHelper;
     }
 
     @VisibleForTesting
@@ -2314,8 +2267,13 @@
             mRoleObserver = new RoleObserver(getContext().getSystemService(RoleManager.class),
                     mPackageManager, getContext().getMainExecutor());
             mRoleObserver.init();
-            mLauncherAppsService =
+            LauncherApps launcherApps =
                     (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
+            mShortcutHelper = new ShortcutHelper(launcherApps, mShortcutListener);
+            BubbleExtractor bubbsExtractor = mRankingHelper.findExtractor(BubbleExtractor.class);
+            if (bubbsExtractor != null) {
+                bubbsExtractor.setShortcutHelper(mShortcutHelper);
+            }
             registerNotificationPreferencesPullers();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             // This observer will force an update when observe is called, causing us to
@@ -3458,7 +3416,7 @@
             ArrayList<ConversationChannelWrapper> conversations =
                     mPreferencesHelper.getConversations(onlyImportant);
             for (ConversationChannelWrapper conversation : conversations) {
-                conversation.setShortcutInfo(getShortcutInfo(
+                conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
                         conversation.getNotificationChannel().getConversationId(),
                         conversation.getPkg(),
                         UserHandle.of(UserHandle.getUserId(conversation.getUid()))));
@@ -3481,7 +3439,7 @@
             ArrayList<ConversationChannelWrapper> conversations =
                     mPreferencesHelper.getConversations(pkg, uid);
             for (ConversationChannelWrapper conversation : conversations) {
-                conversation.setShortcutInfo(getShortcutInfo(
+                conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
                         conversation.getNotificationChannel().getConversationId(),
                         pkg,
                         UserHandle.of(UserHandle.getUserId(uid))));
@@ -5652,7 +5610,7 @@
             }
         }
 
-        r.setShortcutInfo(getShortcutInfo(notification.getShortcutId(), pkg, user));
+        r.setShortcutInfo(mShortcutHelper.getShortcutInfo(notification.getShortcutId(), pkg, user));
 
         if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
                 r.getSbn().getOverrideGroupKey() != null)) {
@@ -5780,16 +5738,12 @@
     }
 
     /**
-     * Updates the flags for this notification to reflect whether it is a bubble or not. Some
-     * bubble specific flags only work if the app is foreground, this will strip those flags
+     * Some bubble specific flags only work if the app is foreground, this will strip those flags
      * if the app wasn't foreground.
      */
-    private void updateNotificationBubbleFlags(NotificationRecord r, String pkg, int userId,
-            NotificationRecord oldRecord, boolean isAppForeground) {
-        Notification notification = r.getNotification();
-        applyFlagBubble(r, pkg, userId, oldRecord, true /* desiredFlag */);
-
+    private void updateNotificationBubbleFlags(NotificationRecord r, boolean isAppForeground) {
         // Remove any bubble specific flags that only work when foregrounded
+        Notification notification = r.getNotification();
         Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
         if (!isAppForeground && metadata != null) {
             int flags = metadata.getFlags();
@@ -5799,252 +5753,30 @@
         }
     }
 
-    /**
-     * Handles actually applying or removing {@link Notification#FLAG_BUBBLE}. Performs necessary
-     * checks for the provided record to see if it can actually be a bubble.
-     * Tracks shortcut based bubbles so that we can find out if they've changed or been removed.
-     */
-    private void applyFlagBubble(NotificationRecord r, String pkg, int userId,
-            NotificationRecord oldRecord, boolean desiredFlag) {
-        boolean applyFlag = desiredFlag
-                && isNotificationAppropriateToBubble(r, pkg, userId, oldRecord);
-        final String shortcutId = r.getNotification().getBubbleMetadata() != null
-                ? r.getNotification().getBubbleMetadata().getShortcutId()
-                : null;
-        if (applyFlag) {
-            if (shortcutId != null) {
-                // Must track shortcut based bubbles in case the shortcut is removed
-                HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
-                        r.getSbn().getPackageName());
-                if (packageBubbles == null) {
-                    packageBubbles = new HashMap<>();
+    private ShortcutHelper.ShortcutListener mShortcutListener =
+            new ShortcutHelper.ShortcutListener() {
+                @Override
+                public void onShortcutRemoved(String key) {
+                    String packageName;
+                    synchronized (mNotificationLock) {
+                        NotificationRecord r = mNotificationsByKey.get(key);
+                        packageName = r != null ? r.getSbn().getPackageName() : null;
+                    }
+                    boolean isAppForeground = packageName != null
+                            && mActivityManager.getPackageImportance(packageName)
+                            == IMPORTANCE_FOREGROUND;
+                    synchronized (mNotificationLock) {
+                        NotificationRecord r = mNotificationsByKey.get(key);
+                        if (r != null) {
+                            // Enqueue will trigger resort & flag is updated that way.
+                            r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+                            mHandler.post(
+                                    new NotificationManagerService.EnqueueNotificationRunnable(
+                                            r.getUser().getIdentifier(), r, isAppForeground));
+                        }
+                    }
                 }
-                packageBubbles.put(shortcutId, r.getKey());
-                mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles);
-                if (!mLauncherAppsCallbackRegistered) {
-                    mLauncherAppsService.registerCallback(mLauncherAppsCallback, mHandler);
-                    mLauncherAppsCallbackRegistered = true;
-                }
-            }
-            r.getNotification().flags |= FLAG_BUBBLE;
-        } else {
-            if (shortcutId != null) {
-                // No longer track shortcut
-                HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
-                        r.getSbn().getPackageName());
-                if (packageBubbles != null) {
-                    packageBubbles.remove(shortcutId);
-                }
-                if (packageBubbles != null && packageBubbles.isEmpty()) {
-                    mActiveShortcutBubbles.remove(r.getSbn().getPackageName());
-                }
-                if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
-                    mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
-                    mLauncherAppsCallbackRegistered = false;
-                }
-            }
-            r.getNotification().flags &= ~FLAG_BUBBLE;
-        }
-    }
-
-    /**
-     * @return whether the provided notification record is allowed to be represented as a bubble,
-     * accounting for user choice & policy.
-     */
-    private boolean isNotificationAppropriateToBubble(NotificationRecord r, String pkg, int userId,
-            NotificationRecord oldRecord) {
-        Notification notification = r.getNotification();
-        if (!canBubble(r, pkg, userId)) {
-            // no log: canBubble has its own
-            return false;
-        }
-
-        if (mActivityManager.isLowRamDevice()) {
-            logBubbleError(r.getKey(), "low ram device");
-            return false;
-        }
-
-        if (oldRecord != null && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0) {
-            // This is an update to an active bubble
-            return true;
-        }
-
-        // At this point the bubble must fulfill communication policy
-
-        // Communication always needs a person
-        ArrayList<Person> peopleList = notification.extras != null
-                ? notification.extras.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)
-                : null;
-        // Message style requires a person & it's not included in the list
-        boolean isMessageStyle = Notification.MessagingStyle.class.equals(
-                notification.getNotificationStyle());
-        if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
-            logBubbleError(r.getKey(), "Must have a person and be "
-                    + "Notification.MessageStyle or Notification.CATEGORY_CALL");
-            return false;
-        }
-
-        // Communication is a message or a call
-        boolean isCall = CATEGORY_CALL.equals(notification.category);
-        boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
-        if (hasForegroundService && !isCall) {
-            logBubbleError(r.getKey(),
-                    "foreground services must be Notification.CATEGORY_CALL to bubble");
-            return false;
-        }
-        if (isMessageStyle) {
-            if (hasValidRemoteInput(notification)) {
-                return true;
-            }
-            logBubbleError(r.getKey(), "messages require valid remote input");
-            return false;
-        } else if (isCall) {
-            if (hasForegroundService) {
-                return true;
-            }
-            logBubbleError(r.getKey(), "calls require foreground service");
-            return false;
-        }
-        logBubbleError(r.getKey(), "Must be "
-                + "Notification.MessageStyle or Notification.CATEGORY_CALL");
-        return false;
-    }
-
-    /**
-     * @return whether the user has enabled the provided notification to bubble, does not account
-     * for policy.
-     */
-    private boolean canBubble(NotificationRecord r, String pkg, int userId) {
-        Notification notification = r.getNotification();
-        Notification.BubbleMetadata metadata = notification.getBubbleMetadata();
-        if (metadata == null) {
-            // no log: no need to inform dev if they didn't attach bubble metadata
-            return false;
-        }
-        if (!mPreferencesHelper.bubblesEnabled()) {
-            logBubbleError(r.getKey(), "bubbles disabled for user: " + userId);
-            return false;
-        }
-        if (!mPreferencesHelper.areBubblesAllowed(pkg, userId)) {
-            logBubbleError(r.getKey(),
-                    "bubbles for package: " + pkg + " disabled for user: " + userId);
-            return false;
-        }
-        if (!r.getChannel().canBubble()) {
-            logBubbleError(r.getKey(),
-                    "bubbles for channel " + r.getChannel().getId() + " disabled");
-            return false;
-        }
-
-        String shortcutId = metadata.getShortcutId();
-        boolean shortcutValid = shortcutId != null
-                && hasValidShortcutInfo(shortcutId, pkg, r.getUser());
-        if (metadata.getBubbleIntent() == null && !shortcutValid) {
-            // Should have a shortcut if intent is null
-            logBubbleError(r.getKey(), "couldn't find shortcutId for bubble: " + shortcutId);
-            return false;
-        }
-        if (shortcutValid) {
-            return true;
-        }
-        // no log: canLaunch method has the failure log
-        return canLaunchInActivityView(getContext(), metadata.getBubbleIntent(), pkg);
-    }
-
-    private boolean hasValidRemoteInput(Notification n) {
-        // Also check for inline reply
-        Notification.Action[] actions = n.actions;
-        if (actions != null) {
-            // Get the remote inputs
-            for (int i = 0; i < actions.length; i++) {
-                Notification.Action action = actions[i];
-                RemoteInput[] inputs = action.getRemoteInputs();
-                if (inputs != null && inputs.length > 0) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            if (shortcutId == null || packageName == null || user == null) {
-                return null;
-            }
-            LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
-            if (packageName != null) {
-                query.setPackage(packageName);
-            }
-            if (shortcutId != null) {
-                query.setShortcutIds(Arrays.asList(shortcutId));
-            }
-            query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
-            List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
-            ShortcutInfo shortcutInfo = shortcuts != null && shortcuts.size() > 0
-                    ? shortcuts.get(0)
-                    : null;
-            return shortcutInfo;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    private boolean hasValidShortcutInfo(String shortcutId, String packageName, UserHandle user) {
-        ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
-        return shortcutInfo != null && shortcutInfo.isLongLived();
-    }
-
-    private void logBubbleError(String key, String failureMessage) {
-        if (DBG) {
-            Log.w(TAG, "Bubble notification: " + key + " failed: " + failureMessage);
-        }
-    }
-    /**
-     * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
-     *
-     * @param context       the context to use.
-     * @param pendingIntent the pending intent of the bubble.
-     * @param packageName   the notification package name for this bubble.
-     */
-    // Keep checks in sync with BubbleController#canLaunchInActivityView.
-    @VisibleForTesting
-    protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
-            String packageName) {
-        if (pendingIntent == null) {
-            Log.w(TAG, "Unable to create bubble -- no intent");
-            return false;
-        }
-
-        // Need escalated privileges to get the intent.
-        final long token = Binder.clearCallingIdentity();
-        Intent intent;
-        try {
-            intent = pendingIntent.getIntent();
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-
-        ActivityInfo info = intent != null
-                ? intent.resolveActivityInfo(context.getPackageManager(), 0)
-                : null;
-        if (info == null) {
-            FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                    BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
-            Log.w(TAG, "Unable to send as bubble -- couldn't find activity info for intent: "
-                    + intent);
-            return false;
-        }
-        if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
-            FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
-                    BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
-            Log.w(TAG, "Unable to send as bubble -- activity is not resizable for intent: "
-                    + intent);
-            return false;
-        }
-        return true;
-    }
+            };
 
     private void doChannelWarningToast(CharSequence toastText) {
         Binder.withCleanCallingIdentity(() -> {
@@ -6150,6 +5882,9 @@
                 MetricsLogger.action(r.getLogMaker()
                         .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
                         .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
+                mNotificationRecordLogger.log(
+                        NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
+                        r);
                 if (DBG) {
                     Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
                 }
@@ -6279,6 +6014,8 @@
                             mDuration)
                     .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA,
                             mSnoozeCriterionId == null ? 0 : 1));
+            mNotificationRecordLogger.log(
+                    NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED, r);
             reportUserInteraction(r);
             boolean wasPosted = removeFromNotificationListsLocked(r);
             cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
@@ -6406,6 +6143,8 @@
                     cancelGroupChildrenLocked(r, mCallingUid, mCallingPid, listenerName,
                             mSendDelete, childrenFlagChecker);
                     updateLightsLocked();
+                    mShortcutHelper.maybeListenForShortcutChangesForBubbles(r, true /* isRemoved */,
+                            mHandler);
                 } else {
                     // No notification was found, assume that it is snoozed and cancel it.
                     if (mReason != REASON_SNOOZED) {
@@ -6473,7 +6212,7 @@
                 final String tag = n.getTag();
 
                 // We need to fix the notification up a little for bubbles
-                updateNotificationBubbleFlags(r, pkg, callingUid, old, isAppForeground);
+                updateNotificationBubbleFlags(r, isAppForeground);
 
                 // Handle grouped notifications and bail out early if we
                 // can to avoid extracting signals.
@@ -6643,6 +6382,10 @@
                                 + n.getPackageName());
                     }
 
+                    mShortcutHelper.maybeListenForShortcutChangesForBubbles(r,
+                            false /* isRemoved */,
+                            mHandler);
+
                     maybeRecordInterruptionLocked(r);
 
                     // Log event to statsd
@@ -7402,6 +7145,7 @@
             int[] visibilities = new int[N];
             boolean[] showBadges = new boolean[N];
             boolean[] allowBubbles = new boolean[N];
+            boolean[] isBubble = new boolean[N];
             ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
             ArrayList<String> groupKeyBefore = new ArrayList<>(N);
             ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
@@ -7417,6 +7161,7 @@
                 visibilities[i] = r.getPackageVisibilityOverride();
                 showBadges[i] = r.canShowBadge();
                 allowBubbles[i] = r.canBubble();
+                isBubble[i] = r.getNotification().isBubbleNotification();
                 channelBefore.add(r.getChannel());
                 groupKeyBefore.add(r.getGroupKey());
                 overridePeopleBefore.add(r.getPeopleOverride());
@@ -7435,6 +7180,7 @@
                         || visibilities[i] != r.getPackageVisibilityOverride()
                         || showBadges[i] != r.canShowBadge()
                         || allowBubbles[i] != r.canBubble()
+                        || isBubble[i] != r.getNotification().isBubbleNotification()
                         || !Objects.equals(channelBefore.get(i), r.getChannel())
                         || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
                         || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
@@ -8597,7 +8343,8 @@
                     record.canBubble(),
                     record.isInterruptive(),
                     record.isConversation(),
-                    record.getShortcutInfo()
+                    record.getShortcutInfo(),
+                    record.getNotification().isBubbleNotification()
             );
             rankings.add(ranking);
         }
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index f4ee461..6c833f9 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -34,11 +34,14 @@
 import java.util.Objects;
 
 /**
- * Interface for writing NotificationReported atoms to statsd log.
+ * Interface for writing NotificationReported atoms to statsd log. Use NotificationRecordLoggerImpl
+ * in production.  Use NotificationRecordLoggerFake for testing.
  * @hide
  */
 public interface NotificationRecordLogger {
 
+    // The high-level interface used by clients.
+
     /**
      * May log a NotificationReported atom reflecting the posting or update of a notification.
      * @param r The new NotificationRecord. If null, no action is taken.
@@ -57,9 +60,11 @@
      * @param reason The reason the notification was canceled.
      * @param dismissalSurface The surface the notification was dismissed from.
      */
-    void logNotificationCancelled(@Nullable NotificationRecord r,
+    default void logNotificationCancelled(@Nullable NotificationRecord r,
             @NotificationListenerService.NotificationCancelReason int reason,
-            @NotificationStats.DismissalSurface int dismissalSurface);
+            @NotificationStats.DismissalSurface int dismissalSurface) {
+        log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r);
+    }
 
     /**
      * Logs a notification visibility change event using UiEventReported (event ids from the
@@ -67,7 +72,17 @@
      * @param r The NotificationRecord. If null, no action is taken.
      * @param visible True if the notification became visible.
      */
-    void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible);
+    default void logNotificationVisibility(@Nullable NotificationRecord r, boolean visible) {
+        log(NotificationEvent.fromVisibility(visible), r);
+    }
+
+    // The UiEventReported logging methods are implemented in terms of this lower-level interface.
+
+    /** Logs a UiEventReported event for the given notification. */
+    void log(UiEventLogger.UiEventEnum event, NotificationRecord r);
+
+    /** Logs a UiEventReported event that is not associated with any notification. */
+    void log(UiEventLogger.UiEventEnum event);
 
     /**
      * The UiEvent enums that this class can log.
@@ -204,7 +219,30 @@
         @UiEvent(doc = "Notification became visible.")
         NOTIFICATION_OPEN(197),
         @UiEvent(doc = "Notification stopped being visible.")
-        NOTIFICATION_CLOSE(198);
+        NOTIFICATION_CLOSE(198),
+        @UiEvent(doc = "Notification was snoozed.")
+        NOTIFICATION_SNOOZED(317),
+        @UiEvent(doc = "Notification was not posted because its app is snoozed.")
+        NOTIFICATION_NOT_POSTED_SNOOZED(319),
+        @UiEvent(doc = "Notification was clicked.")
+        NOTIFICATION_CLICKED(320),
+        @UiEvent(doc = "Notification action was clicked.")
+        NOTIFICATION_ACTION_CLICKED(321),
+        @UiEvent(doc = "Notification detail was expanded due to non-user action.")
+        NOTIFICATION_DETAIL_OPEN_SYSTEM(327),
+        @UiEvent(doc = "Notification detail was collapsed due to non-user action.")
+        NOTIFICATION_DETAIL_CLOSE_SYSTEM(328),
+        @UiEvent(doc = "Notification detail was expanded due to user action.")
+        NOTIFICATION_DETAIL_OPEN_USER(329),
+        @UiEvent(doc = "Notification detail was collapsed due to user action.")
+        NOTIFICATION_DETAIL_CLOSE_USER(330),
+        @UiEvent(doc = "Notification direct reply action was used.")
+        NOTIFICATION_DIRECT_REPLIED(331),
+        @UiEvent(doc = "Notification smart reply action was used.")
+        NOTIFICATION_SMART_REPLIED(332),
+        @UiEvent(doc = "Notification smart reply action was visible.")
+        NOTIFICATION_SMART_REPLY_VISIBLE(333),
+        ;
 
         private final int mId;
         NotificationEvent(int id) {
@@ -217,7 +255,29 @@
         public static NotificationEvent fromVisibility(boolean visible) {
             return visible ? NOTIFICATION_OPEN : NOTIFICATION_CLOSE;
         }
+        public static NotificationEvent fromExpanded(boolean expanded, boolean userAction) {
+            if (userAction) {
+                return expanded ? NOTIFICATION_DETAIL_OPEN_USER : NOTIFICATION_DETAIL_CLOSE_USER;
+            }
+            return expanded ? NOTIFICATION_DETAIL_OPEN_SYSTEM : NOTIFICATION_DETAIL_CLOSE_SYSTEM;
+        }
     }
+
+    enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Notification panel became visible.")
+        NOTIFICATION_PANEL_OPEN(325),
+        @UiEvent(doc = "Notification panel stopped being visible.")
+        NOTIFICATION_PANEL_CLOSE(326);
+
+        private final int mId;
+        NotificationPanelEvent(int id) {
+            mId = id;
+        }
+        @Override public int getId() {
+            return mId;
+        }
+    }
+
     /**
      * A helper for extracting logging information from one or two NotificationRecords.
      */
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 9fcac25..494ff31 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -65,20 +65,16 @@
     }
 
     @Override
-    public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
-        log(NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface), r);
-    }
-
-    @Override
-    public void logNotificationVisibility(NotificationRecord r, boolean visible) {
-        log(NotificationEvent.fromVisibility(visible), r);
-    }
-
-    void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
+    public void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
         if (r == null) {
             return;
         }
         mUiEventLogger.logWithInstanceId(event, r.getUid(), r.getSbn().getPackageName(),
                 r.getSbn().getInstanceId());
     }
+
+    @Override
+    public void log(UiEventLogger.UiEventEnum event) {
+        mUiEventLogger.log(event);
+    }
 }
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
new file mode 100644
index 0000000..7bbb3b1
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -0,0 +1,196 @@
+/*
+ * 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.notification;
+
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
+
+import android.annotation.NonNull;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Helper for querying shortcuts.
+ */
+class ShortcutHelper {
+
+    /**
+     * Listener to call when a shortcut we're tracking has been removed.
+     */
+    interface ShortcutListener {
+        void onShortcutRemoved(String key);
+    }
+
+    private LauncherApps mLauncherAppsService;
+    private ShortcutListener mShortcutListener;
+
+    // Key: packageName Value: <shortcutId, notifId>
+    private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
+    private boolean mLauncherAppsCallbackRegistered;
+
+    // Bubbles can be created based on a shortcut, we need to listen for changes to
+    // that shortcut so that we may update the bubble appropriately.
+    private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() {
+        @Override
+        public void onPackageRemoved(String packageName, UserHandle user) {
+        }
+
+        @Override
+        public void onPackageAdded(String packageName, UserHandle user) {
+        }
+
+        @Override
+        public void onPackageChanged(String packageName, UserHandle user) {
+        }
+
+        @Override
+        public void onPackagesAvailable(String[] packageNames, UserHandle user,
+                boolean replacing) {
+        }
+
+        @Override
+        public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+                boolean replacing) {
+        }
+
+        @Override
+        public void onShortcutsChanged(@NonNull String packageName,
+                @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+            HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName);
+            ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
+            if (shortcutBubbles != null) {
+                // If we can't find one of our bubbles in the shortcut list, that bubble needs
+                // to be removed.
+                for (String shortcutId : shortcutBubbles.keySet()) {
+                    boolean foundShortcut = false;
+                    for (int i = 0; i < shortcuts.size(); i++) {
+                        if (shortcuts.get(i).getId().equals(shortcutId)) {
+                            foundShortcut = true;
+                            break;
+                        }
+                    }
+                    if (!foundShortcut) {
+                        bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId));
+                    }
+                }
+            }
+
+            // Let NoMan know about the updates
+            for (int i = 0; i < bubbleKeysToRemove.size(); i++) {
+                // update flag bubble
+                String bubbleKey = bubbleKeysToRemove.get(i);
+                if (mShortcutListener != null) {
+                    mShortcutListener.onShortcutRemoved(bubbleKey);
+                }
+            }
+        }
+    };
+
+    ShortcutHelper(LauncherApps launcherApps, ShortcutListener listener) {
+        mLauncherAppsService = launcherApps;
+        mShortcutListener = listener;
+    }
+
+    @VisibleForTesting
+    void setLauncherApps(LauncherApps launcherApps) {
+        mLauncherAppsService = launcherApps;
+    }
+
+    ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
+        if (mLauncherAppsService == null) {
+            return null;
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (shortcutId == null || packageName == null || user == null) {
+                return null;
+            }
+            LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
+            query.setPackage(packageName);
+            query.setShortcutIds(Arrays.asList(shortcutId));
+            query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
+            List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
+            return shortcuts != null && shortcuts.size() > 0
+                    ? shortcuts.get(0)
+                    : null;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    boolean hasValidShortcutInfo(String shortcutId, String packageName,
+            UserHandle user) {
+        ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
+        return shortcutInfo != null && shortcutInfo.isLongLived();
+    }
+
+    /**
+     * Shortcut based bubbles require some extra work to listen for shortcut changes.
+     *
+     * @param r the notification record to check
+     * @param removedNotification true if this notification is being removed
+     * @param handler handler to register the callback with
+     */
+    void maybeListenForShortcutChangesForBubbles(NotificationRecord r, boolean removedNotification,
+            Handler handler) {
+        final String shortcutId = r.getNotification().getBubbleMetadata() != null
+                ? r.getNotification().getBubbleMetadata().getShortcutId()
+                : null;
+        if (shortcutId == null) {
+            return;
+        }
+        if (r.getNotification().isBubbleNotification() && !removedNotification) {
+            // Must track shortcut based bubbles in case the shortcut is removed
+            HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
+                    r.getSbn().getPackageName());
+            if (packageBubbles == null) {
+                packageBubbles = new HashMap<>();
+            }
+            packageBubbles.put(shortcutId, r.getKey());
+            mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles);
+            if (!mLauncherAppsCallbackRegistered) {
+                mLauncherAppsService.registerCallback(mLauncherAppsCallback, handler);
+                mLauncherAppsCallbackRegistered = true;
+            }
+        } else {
+            // No longer track shortcut
+            HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
+                    r.getSbn().getPackageName());
+            if (packageBubbles != null) {
+                packageBubbles.remove(shortcutId);
+            }
+            if (packageBubbles != null && packageBubbles.isEmpty()) {
+                mActiveShortcutBubbles.remove(r.getSbn().getPackageName());
+            }
+            if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
+                mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
+                mLauncherAppsCallbackRegistered = false;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f45e66e..2853956 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -141,6 +141,7 @@
         updateDefaultAutomaticRuleNames();
         mConfig = mDefaultConfig.copy();
         mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
+        mConsolidatedPolicy = mConfig.toNotificationPolicy();
 
         mSettingsObserver = new SettingsObserver(mHandler);
         mSettingsObserver.observe();
@@ -821,9 +822,6 @@
      * @return a copy of the zen mode consolidated policy
      */
     public Policy getConsolidatedNotificationPolicy() {
-        if (mConsolidatedPolicy == null) {
-            return null;
-        }
         return mConsolidatedPolicy.copy();
     }
 
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index d12e03d..c37ea8b 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -301,6 +301,14 @@
     public abstract boolean destroyDeSnapshots(int rollbackId);
 
     /**
+     * Deletes snapshots of the credential encrypted apex data directories for the specified user,
+     * where the rollback id is not included in {@code retainRollbackIds}.
+     *
+     * @return boolean true if the delete was successful
+     */
+    public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds);
+
+    /**
      * Dumps various state information to the provided {@link PrintWriter} object.
      *
      * @param pw the {@link PrintWriter} object to send information to.
@@ -745,6 +753,17 @@
             }
         }
 
+        @Override
+        public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
+            try {
+                mApexService.destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
+                return true;
+            } catch (Exception e) {
+                Slog.e(TAG, e.getMessage(), e);
+                return false;
+            }
+        }
+
         /**
          * Dump information about the packages contained in a particular cache
          * @param packagesCache the cache to print information about.
@@ -963,6 +982,11 @@
         }
 
         @Override
+        public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
+            return true;
+        }
+
+        @Override
         void dump(PrintWriter pw, String packageName) {
             // No-op
         }
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 79a4da2..690b9f7 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -481,6 +481,12 @@
                         mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
                     }
                 }
+                // if either package instruments the other, mark both as visible to one another
+                if (pkgInstruments(newPkgSetting, existingSetting)
+                        || pkgInstruments(existingSetting, newPkgSetting)) {
+                    mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
+                    mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
+                }
             }
 
             int existingSize = existingSettings.size();
@@ -715,19 +721,6 @@
                 Trace.endSection();
             }
 
-            if (callingPkgSetting != null) {
-                if (callingPkgInstruments(callingPkgSetting, targetPkgSetting, targetName)) {
-                    return false;
-                }
-            } else {
-                for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
-                    if (callingPkgInstruments(callingSharedPkgSettings.valueAt(i),
-                            targetPkgSetting, targetName)) {
-                        return false;
-                    }
-                }
-            }
-
             try {
                 Trace.beginSection("mOverlayReferenceMapper");
                 if (callingSharedPkgSettings != null) {
@@ -762,16 +755,16 @@
         }
     }
 
-    private static boolean callingPkgInstruments(PackageSetting callingPkgSetting,
-            PackageSetting targetPkgSetting,
-            String targetName) {
+    /** Returns {@code true} if the source package instruments the target package. */
+    private static boolean pkgInstruments(PackageSetting source, PackageSetting target) {
         try {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingPkgInstruments");
-            final List<ParsedInstrumentation> inst = callingPkgSetting.pkg.getInstrumentations();
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "pkgInstruments");
+            final String packageName = target.pkg.getPackageName();
+            final List<ParsedInstrumentation> inst = source.pkg.getInstrumentations();
             for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
-                if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) {
+                if (Objects.equals(inst.get(i).getTargetPackage(), packageName)) {
                     if (DEBUG_LOGGING) {
-                        log(callingPkgSetting, targetPkgSetting, "instrumentation");
+                        log(source, target, "instrumentation");
                     }
                     return true;
                 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9dddf7b..28d7c13 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -314,8 +314,41 @@
     @GuardedBy("mLock")
     private int mParentSessionId;
 
+    static class FileEntry {
+        private final int mIndex;
+        private final InstallationFile mFile;
+
+        FileEntry(int index, InstallationFile file) {
+            this.mIndex = index;
+            this.mFile = file;
+        }
+
+        int getIndex() {
+            return this.mIndex;
+        }
+
+        InstallationFile getFile() {
+            return this.mFile;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof FileEntry)) {
+                return false;
+            }
+            final FileEntry rhs = (FileEntry) obj;
+            return (mFile.getLocation() == rhs.mFile.getLocation()) && TextUtils.equals(
+                    mFile.getName(), rhs.mFile.getName());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFile.getLocation(), mFile.getName());
+        }
+    }
+
     @GuardedBy("mLock")
-    private ArrayList<InstallationFile> mFiles = new ArrayList<>();
+    private ArraySet<FileEntry> mFiles = new ArraySet<>();
 
     @GuardedBy("mLock")
     private boolean mStagedSessionApplied;
@@ -519,8 +552,12 @@
         this.mParentSessionId = parentSessionId;
 
         if (files != null) {
-            for (InstallationFile file : files) {
-                mFiles.add(file);
+            for (int i = 0, size = files.length; i < size; ++i) {
+                InstallationFile file = files[i];
+                if (!mFiles.add(new FileEntry(i, file))) {
+                    throw new IllegalArgumentException(
+                            "Trying to add a duplicate installation file");
+                }
             }
         }
 
@@ -754,9 +791,19 @@
             return result;
         }
 
-        String[] result = new String[mFiles.size()];
-        for (int i = 0, size = mFiles.size(); i < size; ++i) {
-            result[i] = mFiles.get(i).getName();
+        InstallationFile[] files = getInstallationFilesLocked();
+        String[] result = new String[files.length];
+        for (int i = 0, size = files.length; i < size; ++i) {
+            result[i] = files[i].getName();
+        }
+        return result;
+    }
+
+    @GuardedBy("mLock")
+    private InstallationFile[] getInstallationFilesLocked() {
+        final InstallationFile[] result = new InstallationFile[mFiles.size()];
+        for (FileEntry fileEntry : mFiles) {
+            result[fileEntry.getIndex()] = fileEntry.getFile();
         }
         return result;
     }
@@ -2448,7 +2495,10 @@
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("addFile");
 
-            mFiles.add(new InstallationFile(location, name, lengthBytes, metadata, signature));
+            if (!mFiles.add(new FileEntry(mFiles.size(),
+                    new InstallationFile(location, name, lengthBytes, metadata, signature)))) {
+                throw new IllegalArgumentException("File already added: " + name);
+            }
         }
     }
 
@@ -2466,7 +2516,10 @@
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("removeFile");
 
-            mFiles.add(new InstallationFile(location, getRemoveMarkerName(name), -1, null, null));
+            if (!mFiles.add(new FileEntry(mFiles.size(),
+                    new InstallationFile(location, getRemoveMarkerName(name), -1, null, null)))) {
+                throw new IllegalArgumentException("File already removed: " + name);
+            }
         }
     }
 
@@ -2486,7 +2539,8 @@
         final List<InstallationFileParcel> addedFiles = new ArrayList<>();
         final List<String> removedFiles = new ArrayList<>();
 
-        for (InstallationFile file : mFiles) {
+        final InstallationFile[] files = getInstallationFilesLocked();
+        for (InstallationFile file : files) {
             if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) {
                 addedFiles.add(file.getData());
                 continue;
@@ -3007,7 +3061,9 @@
                 writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
                 out.endTag(null, TAG_CHILD_SESSION);
             }
-            for (InstallationFile file : mFiles) {
+
+            final InstallationFile[] files = getInstallationFilesLocked();
+            for (InstallationFile file : getInstallationFilesLocked()) {
                 out.startTag(null, TAG_SESSION_FILE);
                 writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
                 writeStringAttribute(out, ATTR_NAME, file.getName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 221e7dc..24ebd32 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24192,6 +24192,12 @@
             }
             mAppsFilter.getFeatureConfig().enableLogging(pkg.appId, enable);
         }
+
+        @Override
+        public boolean isSystemPackage(@NonNull String packageName) {
+            return packageName.equals(
+                    PackageManagerService.this.ensureSystemPackageName(packageName));
+        }
     }
 
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index be17dd8..8a9f1b3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3017,9 +3017,11 @@
 
     private int doAddFiles(int sessionId, ArrayList<String> args, long sessionSizeBytes,
             boolean isApex) throws RemoteException {
-        PackageInstaller.Session session = new PackageInstaller.Session(
-                mInterface.getPackageInstaller().openSession(sessionId));
+        PackageInstaller.Session session = null;
         try {
+            session = new PackageInstaller.Session(
+                    mInterface.getPackageInstaller().openSession(sessionId));
+
             // 1. Single file from stdin.
             if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
                 final String name = "base." + (isApex ? "apex" : "apk");
@@ -3043,6 +3045,10 @@
                 }
             }
             return 0;
+        } catch (IllegalArgumentException e) {
+            getErrPrintWriter().println("Failed to add file(s), reason: " + e);
+            getOutPrintWriter().println("Failure [failed to add file(s)]");
+            return 1;
         } finally {
             IoUtils.closeQuietly(session);
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index df3c83a..8280a61 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -455,11 +455,13 @@
 
         @Override
         public void onFinished(int id, Bundle extras) {
-            try {
-                mContext.startIntentSender(mTarget, null, 0, 0, 0);
-            } catch (IntentSender.SendIntentException e) {
-                Slog.e(LOG_TAG, "Failed to start the target in the callback", e);
-            }
+            mHandler.post(() -> {
+                try {
+                    mContext.startIntentSender(mTarget, null, 0, 0, 0);
+                } catch (IntentSender.SendIntentException e) {
+                    Slog.e(LOG_TAG, "Failed to start the target in the callback", e);
+                }
+            });
         }
     }
 
@@ -1300,14 +1302,15 @@
 
     @Override
     public boolean hasBadge(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasBadge");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasBadge");
         final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
         return userTypeDetails != null && userTypeDetails.hasBadge();
     }
 
     @Override
     public @StringRes int getUserBadgeLabelResId(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeLabelResId");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+                "getUserBadgeLabelResId");
         final UserInfo userInfo = getUserInfoNoChecks(userId);
         final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
         if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
@@ -1320,7 +1323,8 @@
 
     @Override
     public @ColorRes int getUserBadgeColorResId(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeColorResId");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+                "getUserBadgeColorResId");
         final UserInfo userInfo = getUserInfoNoChecks(userId);
         final UserTypeDetails userTypeDetails = getUserTypeDetails(userInfo);
         if (userInfo == null || userTypeDetails == null || !userTypeDetails.hasBadge()) {
@@ -1333,7 +1337,7 @@
 
     @Override
     public @DrawableRes int getUserIconBadgeResId(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserIconBadgeResId");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserIconBadgeResId");
         final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
         if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
             Slog.e(LOG_TAG, "Requested icon badge for non-badged user " + userId);
@@ -1344,7 +1348,7 @@
 
     @Override
     public @DrawableRes int getUserBadgeResId(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserBadgeResId");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserBadgeResId");
         final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
         if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
             Slog.e(LOG_TAG, "Requested badge for non-badged user " + userId);
@@ -1355,7 +1359,7 @@
 
     @Override
     public @DrawableRes int getUserBadgeNoBackgroundResId(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId,
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
                 "getUserBadgeNoBackgroundResId");
         final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId);
         if (userTypeDetails == null || !userTypeDetails.hasBadge()) {
@@ -1367,7 +1371,7 @@
 
     @Override
     public boolean isProfile(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isProfile");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isProfile");
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
             return userInfo != null && userInfo.isProfile();
@@ -1376,7 +1380,7 @@
 
     @Override
     public boolean isManagedProfile(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isManagedProfile");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isManagedProfile");
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
             return userInfo != null && userInfo.isManagedProfile();
@@ -1385,19 +1389,20 @@
 
     @Override
     public boolean isUserUnlockingOrUnlocked(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserUnlockingOrUnlocked");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId,
+                "isUserUnlockingOrUnlocked");
         return mLocalService.isUserUnlockingOrUnlocked(userId);
     }
 
     @Override
     public boolean isUserUnlocked(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserUnlocked");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isUserUnlocked");
         return mLocalService.isUserUnlocked(userId);
     }
 
     @Override
     public boolean isUserRunning(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "isUserRunning");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isUserRunning");
         return mLocalService.isUserRunning(userId);
     }
 
@@ -1437,7 +1442,7 @@
         }
     }
 
-    private void checkManageOrInteractPermIfCallerInOtherProfileGroup(@UserIdInt int userId,
+    private void checkManageOrInteractPermissionIfCallerInOtherProfileGroup(@UserIdInt int userId,
             String name) {
         final int callingUserId = UserHandle.getCallingUserId();
         if (callingUserId == userId || isSameProfileGroupNoChecks(callingUserId, userId) ||
@@ -1466,11 +1471,7 @@
 
     @Override
     public boolean isPreCreated(@UserIdInt int userId) {
-        final int callingUserId = UserHandle.getCallingUserId();
-        if (callingUserId != userId && !hasManageUsersPermission()) {
-            throw new SecurityException("You need MANAGE_USERS permission to query if u=" + userId
-                    + " is pre-created");
-        }
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "isPreCreated");
         synchronized (mUsersLock) {
             UserInfo userInfo = getUserInfoLU(userId);
             return userInfo != null && userInfo.preCreated;
@@ -1826,7 +1827,7 @@
     /** @return a specific user restriction that's in effect currently. */
     @Override
     public boolean hasUserRestriction(String restrictionKey, @UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "hasUserRestriction");
         return mLocalService.hasUserRestriction(restrictionKey, userId);
     }
 
@@ -1951,7 +1952,7 @@
      */
     @Override
     public Bundle getUserRestrictions(@UserIdInt int userId) {
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
+        checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
         return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
     }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 64edacd..1e12565 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2139,8 +2139,10 @@
         }
 
         // check if user has enabled this operation. SecurityException will be thrown if this app
-        // has not been allowed by the user
-        final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName);
+        // has not been allowed by the user. The reason to use "noteOp" (instead of checkOp) is to
+        // make sure the usage is logged.
+        final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName,
+                null /* featureId */, "check-add");
         switch (mode) {
             case AppOpsManager.MODE_ALLOWED:
             case AppOpsManager.MODE_IGNORED:
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index beba106..8b6e9a4 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -16,11 +16,13 @@
 package com.android.server.power.batterysaver;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
 import android.hardware.power.V1_0.PowerHint;
 import android.os.BatteryManager;
 import android.os.BatterySaverPolicyConfig;
@@ -35,6 +37,7 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -49,6 +52,7 @@
 
 import java.util.ArrayList;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
  * Responsible for battery saver mode transition logic.
@@ -112,6 +116,14 @@
      */
     private final Plugin[] mPlugins;
 
+    /**
+     * Package name that will receive an explicit manifest broadcast for
+     * {@link PowerManager#ACTION_POWER_SAVE_MODE_CHANGED}. It's {@code null} if it hasn't been
+     * retrieved yet.
+     */
+    @Nullable
+    private Optional<String> mPowerSaveModeChangedListenerPackage;
+
     public static final int REASON_PERCENTAGE_AUTOMATIC_ON = 0;
     public static final int REASON_PERCENTAGE_AUTOMATIC_OFF = 1;
     public static final int REASON_MANUAL_ON = 2;
@@ -494,6 +506,14 @@
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
 
+            // Send the broadcast to a manifest-registered receiver that is specified in the config.
+            if (getPowerSaveModeChangedListenerPackage().isPresent()) {
+                intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
+                        .setPackage(getPowerSaveModeChangedListenerPackage().get())
+                        .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            }
+
             // Send internal version that requires signature permission.
             intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED_INTERNAL);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -508,6 +528,20 @@
         }
     }
 
+    private Optional<String> getPowerSaveModeChangedListenerPackage() {
+        if (mPowerSaveModeChangedListenerPackage == null) {
+            String configPowerSaveModeChangedListenerPackage =
+                    mContext.getString(R.string.config_powerSaveModeChangedListenerPackage);
+            mPowerSaveModeChangedListenerPackage =
+                    LocalServices
+                            .getService(PackageManagerInternal.class)
+                            .isSystemPackage(configPowerSaveModeChangedListenerPackage)
+                            ? Optional.of(configPowerSaveModeChangedListenerPackage)
+                            : Optional.empty();
+        }
+        return mPowerSaveModeChangedListenerPackage;
+    }
+
     private void updateBatterySavingStats() {
         final PowerManager pm = getPowerManager();
         if (pm == null) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 42fada1..b50c22e 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -63,6 +63,7 @@
 import com.android.server.PackageWatchdog;
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
+import com.android.server.pm.ApexManager;
 import com.android.server.pm.Installer;
 
 import java.io.File;
@@ -485,6 +486,8 @@
             }
 
             latch.countDown();
+
+            destroyCeSnapshotsForExpiredRollbacks(userId);
         });
 
         try {
@@ -495,6 +498,15 @@
     }
 
     @WorkerThread
+    private void destroyCeSnapshotsForExpiredRollbacks(int userId) {
+        int[] rollbackIds = new int[mRollbacks.size()];
+        for (int i = 0; i < rollbackIds.length; i++) {
+            rollbackIds[i] = mRollbacks.get(i).info.getRollbackId();
+        }
+        ApexManager.getInstance().destroyCeSnapshotsNotSpecified(userId, rollbackIds);
+    }
+
+    @WorkerThread
     private void updateRollbackLifetimeDurationInMillis() {
         mRollbackLifetimeDurationInMillis = DeviceConfig.getLong(
                 DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 856a40f..2b793c8 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -81,22 +81,18 @@
 
     /** Returns whether the file has fs-verity enabled. */
     public static boolean hasFsverity(@NonNull String filePath) {
-        // NB: only measure but not check the actual measurement here. As long as this succeeds,
-        // the file is on readable if the measurement can be verified against a trusted key, and
-        // this is good enough for installed apps.
-        int errno = measureFsverityNative(filePath);
-        if (errno != 0) {
-            if (errno != OsConstants.ENODATA) {
-                Slog.e(TAG, "Failed to measure fs-verity, errno " + errno + ": " + filePath);
-            }
+        int retval = statxForFsverityNative(filePath);
+        if (retval < 0) {
+            Slog.e(TAG, "Failed to check whether fs-verity is enabled, errno " + -retval + ": "
+                    + filePath);
             return false;
         }
-        return true;
+        return (retval == 1);
     }
 
     private static native int enableFsverityNative(@NonNull String filePath,
             @NonNull byte[] pkcs7Signature);
-    private static native int measureFsverityNative(@NonNull String filePath);
+    private static native int statxForFsverityNative(@NonNull String filePath);
 
     /**
      * Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped.
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index b43d8b7..25585b3 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -70,6 +70,9 @@
     private TvInputManager mManager;
     private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints();
 
+    // An internal resource request count to help generate resource handle.
+    private int mResourceRequestCount = 0;
+
     // Used to synchronize the access to the service.
     private final Object mLock = new Object();
 
@@ -96,7 +99,7 @@
         public void registerClientProfile(@NonNull ResourceClientProfile profile,
                 @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId)
                 throws RemoteException {
-            enforceAccessPermission();
+            enforceTrmAccessPermission("registerClientProfile");
             if (profile == null) {
                 throw new RemoteException("ResourceClientProfile can't be null");
             }
@@ -120,7 +123,7 @@
 
         @Override
         public void unregisterClientProfile(int clientId) throws RemoteException {
-            enforceAccessPermission();
+            enforceTrmAccessPermission("unregisterClientProfile");
             synchronized (mLock) {
                 if (!checkClientExists(clientId)) {
                     Slog.e(TAG, "Unregistering non exists client:" + clientId);
@@ -132,7 +135,7 @@
 
         @Override
         public boolean updateClientPriority(int clientId, int priority, int niceValue) {
-            enforceAccessPermission();
+            enforceTrmAccessPermission("updateClientPriority");
             synchronized (mLock) {
                 return updateClientPriorityInternal(clientId, priority, niceValue);
             }
@@ -140,7 +143,7 @@
 
         @Override
         public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException {
-            enforceAccessPermission();
+            enforceTrmAccessPermission("setFrontendInfoList");
             if (infos == null) {
                 throw new RemoteException("TunerFrontendInfo can't be null");
             }
@@ -151,6 +154,7 @@
 
         @Override
         public void updateCasInfo(int casSystemId, int maxSessionNum) {
+            enforceTrmAccessPermission("updateCasInfo");
             if (DEBUG) {
                 Slog.d(TAG,
                         "updateCasInfo(casSystemId=" + casSystemId
@@ -160,6 +164,7 @@
 
         @Override
         public void setLnbInfoList(int[] lnbIds) {
+            enforceTrmAccessPermission("setLnbInfoList");
             if (DEBUG) {
                 for (int i = 0; i < lnbIds.length; i++) {
                     Slog.d(TAG, "updateLnbInfo(lnbId=" + lnbIds[i] + ")");
@@ -169,14 +174,15 @@
 
         @Override
         public boolean requestFrontend(@NonNull TunerFrontendRequest request,
-                @NonNull int[] frontendId) throws RemoteException {
-            enforceAccessPermission();
-            if (frontendId == null) {
-                throw new RemoteException("frontendId can't be null");
+                @NonNull int[] frontendHandle) throws RemoteException {
+            enforceTunerAccessPermission("requestFrontend");
+            enforceTrmAccessPermission("requestFrontend");
+            if (frontendHandle == null) {
+                throw new RemoteException("frontendHandle can't be null");
             }
             synchronized (mLock) {
                 try {
-                    return requestFrontendInternal(request, frontendId);
+                    return requestFrontendInternal(request, frontendHandle);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -185,6 +191,8 @@
 
         @Override
         public void shareFrontend(int selfClientId, int targetClientId) {
+            enforceTunerAccessPermission("shareFrontend");
+            enforceTrmAccessPermission("shareFrontend");
             if (DEBUG) {
                 Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId);
             }
@@ -192,25 +200,34 @@
 
         @Override
         public boolean requestDemux(@NonNull TunerDemuxRequest request,
-                    @NonNull int[] demuxHandle) {
-            if (DEBUG) {
-                Slog.d(TAG, "requestDemux(request=" + request + ")");
+                    @NonNull int[] demuxHandle)  throws RemoteException {
+            enforceTunerAccessPermission("requestDemux");
+            enforceTrmAccessPermission("requestDemux");
+            if (demuxHandle == null) {
+                throw new RemoteException("demuxHandle can't be null");
             }
-            return true;
+            synchronized (mLock) {
+                return requestDemuxInternal(request, demuxHandle);
+            }
         }
 
         @Override
         public boolean requestDescrambler(@NonNull TunerDescramblerRequest request,
-                    @NonNull int[] descrambleHandle) {
-            if (DEBUG) {
-                Slog.d(TAG, "requestDescrambler(request=" + request + ")");
+                    @NonNull int[] descrambleHandle) throws RemoteException {
+            enforceDescramblerAccessPermission("requestDescrambler");
+            enforceTrmAccessPermission("requestDescrambler");
+            if (descrambleHandle == null) {
+                throw new RemoteException("descrambleHandle can't be null");
             }
-            return true;
+            synchronized (mLock) {
+                return requestDescramblerInternal(request, descrambleHandle);
+            }
         }
 
         @Override
         public boolean requestCasSession(
-                @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) {
+                @NonNull CasSessionRequest request, @NonNull int[] sessionResourceHandle) {
+            enforceTrmAccessPermission("requestCasSession");
             if (DEBUG) {
                 Slog.d(TAG, "requestCasSession(request=" + request + ")");
             }
@@ -219,7 +236,9 @@
         }
 
         @Override
-        public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbId) {
+        public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) {
+            enforceTunerAccessPermission("requestLnb");
+            enforceTrmAccessPermission("requestLnb");
             if (DEBUG) {
                 Slog.d(TAG, "requestLnb(request=" + request + ")");
             }
@@ -228,6 +247,8 @@
 
         @Override
         public void releaseFrontend(int frontendId) {
+            enforceTunerAccessPermission("releaseFrontend");
+            enforceTrmAccessPermission("releaseFrontend");
             if (DEBUG) {
                 Slog.d(TAG, "releaseFrontend(id=" + frontendId + ")");
             }
@@ -235,6 +256,8 @@
 
         @Override
         public void releaseDemux(int demuxHandle) {
+            enforceTunerAccessPermission("releaseDemux");
+            enforceTrmAccessPermission("releaseDemux");
             if (DEBUG) {
                 Slog.d(TAG, "releaseDemux(demuxHandle=" + demuxHandle + ")");
             }
@@ -242,6 +265,8 @@
 
         @Override
         public void releaseDescrambler(int descramblerHandle) {
+            enforceTunerAccessPermission("releaseDescrambler");
+            enforceTrmAccessPermission("releaseDescrambler");
             if (DEBUG) {
                 Slog.d(TAG, "releaseDescrambler(descramblerHandle=" + descramblerHandle + ")");
             }
@@ -249,6 +274,7 @@
 
         @Override
         public void releaseCasSession(int sessionResourceId) {
+            enforceTrmAccessPermission("releaseCasSession");
             if (DEBUG) {
                 Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")");
             }
@@ -256,6 +282,8 @@
 
         @Override
         public void releaseLnb(int lnbId) {
+            enforceTunerAccessPermission("releaseLnb");
+            enforceTrmAccessPermission("releaseLnb");
             if (DEBUG) {
                 Slog.d(TAG, "releaseLnb(lnbId=" + lnbId + ")");
             }
@@ -264,6 +292,7 @@
         @Override
         public boolean isHigherPriority(
                 ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) {
+            enforceTrmAccessPermission("isHigherPriority");
             if (DEBUG) {
                 Slog.d(TAG,
                         "isHigherPriority(challengerProfile=" + challengerProfile
@@ -371,13 +400,13 @@
     }
 
     @VisibleForTesting
-    protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendId)
+    protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle)
             throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "requestFrontend(request=" + request + ")");
         }
 
-        frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
+        frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         if (!checkClientExists(request.getClientId())) {
             Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId());
             return false;
@@ -413,17 +442,20 @@
 
         // Grant frontend when there is unused resource.
         if (grantingFrontendId > -1) {
-            frontendId[0] = grantingFrontendId;
-            updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+            frontendHandle[0] = generateResourceHandle(
+                    TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, grantingFrontendId);
+            updateFrontendClientMappingOnNewGrant(grantingFrontendId, request.getClientId());
             return true;
         }
 
         // When all the resources are occupied, grant the lowest priority resource if the
         // request client has higher priority.
         if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
-            frontendId[0] = inUseLowestPriorityFrId;
-            reclaimFrontendResource(getFrontendResource(frontendId[0]).getOwnerClientId());
-            updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId());
+            frontendHandle[0] = generateResourceHandle(
+                    TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, inUseLowestPriorityFrId);
+            reclaimFrontendResource(getFrontendResource(
+                    inUseLowestPriorityFrId).getOwnerClientId());
+            updateFrontendClientMappingOnNewGrant(inUseLowestPriorityFrId, request.getClientId());
             return true;
         }
 
@@ -431,6 +463,25 @@
     }
 
     @VisibleForTesting
+    boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
+        if (DEBUG) {
+            Slog.d(TAG, "requestDemux(request=" + request + ")");
+        }
+        demuxHandle[0] = generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
+        return true;
+    }
+
+    @VisibleForTesting
+    boolean requestDescramblerInternal(TunerDescramblerRequest request, int[] descramblerHandle) {
+        if (DEBUG) {
+            Slog.d(TAG, "requestDescrambler(request=" + request + ")");
+        }
+        descramblerHandle[0] =
+                generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DESCRAMBLER, 0);
+        return true;
+    }
+
+    @VisibleForTesting
     protected class ResourcesReclaimListenerRecord implements IBinder.DeathRecipient {
         private final IResourcesReclaimListener mListener;
         private final int mClientId;
@@ -592,8 +643,25 @@
         return mClientProfiles.keySet().contains(clientId);
     }
 
-    private void enforceAccessPermission() {
-        getContext().enforceCallingOrSelfPermission(
-                "android.permission.TUNER_RESOURCE_ACCESS", TAG);
+    private int generateResourceHandle(
+            @TunerResourceManager.TunerResourceType int resourceType, int resourceId) {
+        return (resourceType & 0x000000ff) << 24
+                | (resourceId << 16)
+                | (mResourceRequestCount++ & 0xffff);
+    }
+
+    private void enforceTrmAccessPermission(String apiName) {
+        getContext().enforceCallingPermission("android.permission.TUNER_RESOURCE_ACCESS",
+                TAG + ": " + "apiName");
+    }
+
+    private void enforceTunerAccessPermission(String apiName) {
+        getContext().enforceCallingPermission("android.Manifest.permission.ACCESS_TV_TUNER",
+                TAG + ": " + "apiName");
+    }
+
+    private void enforceDescramblerAccessPermission(String apiName) {
+        getContext().enforceCallingPermission("android.Manifest.permission.ACCESS_TV_DESCRAMBLER",
+                TAG + ": " + "apiName");
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e73c928..c4545fa 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1351,11 +1351,16 @@
                 mLetterbox.attachInput(w);
             }
             getPosition(mTmpPoint);
-            // Get the bounds of the "space-to-fill". In multi-window mode, the task-level
-            // represents this. In fullscreen-mode, the stack does (since the orientation letterbox
-            // is also applied to the task).
-            Rect spaceToFill = (inMultiWindowMode() || getStack() == null)
-                    ? task.getDisplayedBounds() : getStack().getDisplayedBounds();
+            // Get the bounds of the "space-to-fill". The transformed bounds have the highest
+            // priority because the activity is launched in a rotated environment. In multi-window
+            // mode, the task-level represents this. In fullscreen-mode, the task container does
+            // (since the orientation letterbox is also applied to the task).
+            final Rect transformedBounds = getFixedRotationTransformDisplayBounds();
+            final Rect spaceToFill = transformedBounds != null
+                    ? transformedBounds
+                    : inMultiWindowMode()
+                            ? task.getDisplayedBounds()
+                            : getRootTask().getParent().getDisplayedBounds();
             mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
         } else if (mLetterbox != null) {
             mLetterbox.hide();
@@ -6362,33 +6367,17 @@
         if (mCompatDisplayInsets != null) {
             resolveSizeCompatModeConfiguration(newParentConfiguration);
         } else {
-            // We ignore activities' requested orientation in multi-window modes. Task level may
-            // take them into consideration when calculating bounds.
             if (inMultiWindowMode()) {
+                // We ignore activities' requested orientation in multi-window modes. Task level may
+                // take them into consideration when calculating bounds.
                 resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED;
-            }
-            final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
-            final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
-            // Use tmp bounds to calculate aspect ratio so we can know whether the activity should
-            // use restricted size (resolvedBounds may be the requested override bounds).
-            mTmpBounds.setEmpty();
-            applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds);
-            // If the out bounds is not empty, it means the activity cannot fill parent's app
-            // bounds, then the relative configuration (e.g. screen size, layout) needs to be
-            // resolved according to the bounds.
-            if (!mTmpBounds.isEmpty()) {
-                final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
-                resolvedBounds.set(mTmpBounds);
-                // Exclude the horizontal decor area because the activity will be centered
-                // horizontally in parent's app bounds to balance the visual appearance.
-                resolvedBounds.left = parentAppBounds.left;
-                task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
-                        getFixedRotationTransformDisplayInfo());
-                final int offsetX = getHorizontalCenterOffset(
-                        parentAppBounds.width(), resolvedBounds.width());
-                if (offsetX > 0) {
-                    offsetBounds(resolvedConfig, offsetX - resolvedBounds.left, 0 /* offsetY */);
+                // If the activity has requested override bounds, the configuration needs to be
+                // computed accordingly.
+                if (!matchParentBounds()) {
+                    task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
                 }
+            } else {
+                resolveFullscreenConfiguration(newParentConfiguration);
             }
         }
 
@@ -6400,6 +6389,43 @@
     }
 
     /**
+     * Resolves the configuration of activity in fullscreen mode. If the bounds are restricted by
+     * aspect ratio, the position will be centered horizontally in parent's app bounds to balance
+     * the visual appearance. The policy of aspect ratio has higher priority than the requested
+     * override bounds.
+     */
+    private void resolveFullscreenConfiguration(Configuration newParentConfiguration) {
+        final Configuration resolvedConfig = getResolvedOverrideConfiguration();
+        final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+        final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
+        final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+        // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
+        // restricted size (resolved bounds may be the requested override bounds).
+        mTmpBounds.setEmpty();
+        applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds);
+        // If the out bounds is not empty, it means the activity cannot fill parent's app bounds,
+        // then there is space to be centered.
+        final boolean needToBeCentered = !mTmpBounds.isEmpty();
+        if (needToBeCentered) {
+            resolvedBounds.set(mTmpBounds);
+            // Exclude the horizontal decor area.
+            resolvedBounds.left = parentAppBounds.left;
+        }
+        if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
+            // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
+            // restrict, the bounds should be the requested override bounds.
+            task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
+                    getFixedRotationTransformDisplayInfo());
+        }
+        if (needToBeCentered) {
+            // Offset to center relative to parent's app bounds.
+            final int offsetX = getHorizontalCenterOffset(
+                    parentAppBounds.width(), resolvedBounds.width());
+            offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */);
+        }
+    }
+
+    /**
      * Resolves consistent screen configuration for orientation and rotation changes without
      * inheriting the parent bounds.
      */
@@ -6507,7 +6533,7 @@
         // Above coordinates are in "@" space, now place "*" and "#" to screen space.
         final int screenPosX = parentAppBounds.left + offsetX;
         final int screenPosY = parentBounds.top;
-        if (screenPosX > 0 || screenPosY > 0) {
+        if (screenPosX != 0 || screenPosY != 0) {
             if (mSizeCompatBounds != null) {
                 mSizeCompatBounds.offset(screenPosX, screenPosY);
             }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 4ebb423..e8bfe8e 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1510,7 +1510,7 @@
      */
     @StackVisibility
     int getVisibility(ActivityRecord starting) {
-        if (!isAttached() || mForceHidden) {
+        if (!isAttached() || isForceHidden()) {
             return STACK_VISIBILITY_INVISIBLE;
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 6d7f8fb..57f357d 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -75,6 +75,7 @@
 import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
 import static com.android.server.wm.RootWindowContainer.TAG_STATES;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED;
@@ -1565,9 +1566,9 @@
              * stopping list by handling the idle.
              */
             stack.cancelAnimation();
-            stack.mForceHidden = true;
+            stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
             stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
-            stack.mForceHidden = false;
+            stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
             activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
                     true /* processPausingActivities */, null /* configuration */);
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 98e3d07..aad242d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -375,7 +375,7 @@
      */
     final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
 
-    /** @see #computeCompatSmallestWidth(boolean, int, int, int, DisplayCutout) */
+    /** @see #computeCompatSmallestWidth(boolean, int, int, int) */
     private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
 
     /**
@@ -1814,7 +1814,7 @@
 
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
         outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw,
-                dh, displayCutout);
+                dh);
     }
 
     /**
@@ -1922,8 +1922,7 @@
         mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
     }
 
-    private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
-            DisplayCutout displayCutout) {
+    private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh) {
         mTmpDisplayMetrics.setTo(mDisplayMetrics);
         final DisplayMetrics tmpDm = mTmpDisplayMetrics;
         final int unrotDw, unrotDh;
@@ -1934,19 +1933,21 @@
             unrotDw = dw;
             unrotDh = dh;
         }
-        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh,
-                displayCutout);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw,
-                displayCutout);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh,
-                displayCutout);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw,
-                displayCutout);
+        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw,
+                unrotDh);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh,
+                unrotDw);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw,
+                unrotDh);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh,
+                unrotDw);
         return sw;
     }
 
     private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
-            DisplayMetrics dm, int dw, int dh, DisplayCutout displayCutout) {
+            DisplayMetrics dm, int dw, int dh) {
+        final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
+                rotation).getDisplayCutout();
         dm.noncompatWidthPixels = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
                 displayCutout);
         dm.noncompatHeightPixels = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
@@ -1987,20 +1988,20 @@
             return;
         }
         int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
-                displayInfo.displayCutout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode,
-                displayInfo.displayCutout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode,
-                displayInfo.displayCutout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode,
-                displayInfo.displayCutout);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode);
         outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
         outConfig.screenLayout = sl;
     }
 
     private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
-            int uiMode, DisplayCutout displayCutout) {
+            int uiMode) {
+        // Get the display cutout at this rotation.
+        final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(
+                rotation).getDisplayCutout();
+
         // Get the app screen size at this rotation.
         int w = mDisplayPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayCutout);
         int h = mDisplayPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayCutout);
@@ -6523,6 +6524,7 @@
             lastReparentedStack.postReparent();
         }
         releaseSelfIfNeeded();
+        mDisplayPolicy.release();
 
         if (!mAllSleepTokens.isEmpty()) {
             mRootWindowContainer.mSleepTokens.removeAll(mAllSleepTokens);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index ba61667..38a406e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -28,6 +28,7 @@
 import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
@@ -179,6 +180,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.util.function.TriConsumer;
@@ -259,7 +261,9 @@
     @Px
     private int mBottomGestureAdditionalInset;
     @Px
-    private int mSideGestureInset;
+    private int mLeftGestureInset;
+    @Px
+    private int mRightGestureInset;
 
     StatusBarManagerInternal getStatusBarManagerInternal() {
         synchronized (mServiceAcquireLock) {
@@ -427,6 +431,8 @@
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
 
+    private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
+
     private class PolicyHandler extends Handler {
 
         PolicyHandler(Looper looper) {
@@ -651,6 +657,16 @@
         mRefreshRatePolicy = new RefreshRatePolicy(mService,
                 mDisplayContent.getDisplayInfo(),
                 mService.mHighRefreshRateBlacklist);
+
+        mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler,
+                mContext, () -> {
+            synchronized (mLock) {
+                onConfigurationChanged();
+                mSystemGestures.onConfigurationChanged();
+                mDisplayContent.updateSystemGestureExclusion();
+            }
+        });
+        mHandler.post(mGestureNavigationSettingsObserver::register);
     }
 
     void systemReady() {
@@ -723,7 +739,7 @@
     }
 
     boolean hasSideGestures() {
-        return mHasNavigationBar && mSideGestureInset > 0;
+        return mHasNavigationBar && (mLeftGestureInset > 0 || mRightGestureInset > 0);
     }
 
     public boolean navigationBarCanMove() {
@@ -1009,6 +1025,13 @@
             case TYPE_STATUS_BAR_PANEL:
                 return WindowManagerGlobal.ADD_INVALID_TYPE;
         }
+
+        if (attrs.providesInsetsTypes != null) {
+            mContext.enforcePermission(
+                    android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+                    "DisplayPolicy");
+            enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
+        }
         return ADD_OKAY;
     }
 
@@ -1076,11 +1099,12 @@
                             inOutFrame.left = 0;
                             inOutFrame.top = 0;
                             inOutFrame.bottom = displayFrames.mDisplayHeight;
-                            inOutFrame.right = displayFrames.mUnrestricted.left + mSideGestureInset;
+                            inOutFrame.right = displayFrames.mUnrestricted.left + mLeftGestureInset;
                         });
                 mDisplayContent.setInsetProvider(ITYPE_RIGHT_GESTURES, win,
                         (displayFrames, windowState, inOutFrame) -> {
-                            inOutFrame.left = displayFrames.mUnrestricted.right - mSideGestureInset;
+                            inOutFrame.left = displayFrames.mUnrestricted.right
+                                    - mRightGestureInset;
                             inOutFrame.top = 0;
                             inOutFrame.bottom = displayFrames.mDisplayHeight;
                             inOutFrame.right = displayFrames.mDisplayWidth;
@@ -1094,6 +1118,28 @@
                         });
                 if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
                 break;
+            default:
+                if (attrs.providesInsetsTypes != null) {
+                    for (int insetsType : attrs.providesInsetsTypes) {
+                        mDisplayContent.setInsetProvider(insetsType, win, null);
+                    }
+                }
+                break;
+        }
+    }
+
+    private static void enforceSingleInsetsTypeCorrespondingToWindowType(int[] insetsTypes) {
+        int count = 0;
+        for (int insetsType : insetsTypes) {
+            switch (insetsType) {
+                case ITYPE_NAVIGATION_BAR:
+                case ITYPE_STATUS_BAR:
+                case ITYPE_CAPTION_BAR:
+                    if (++count > 1) {
+                        throw new IllegalArgumentException(
+                                "Multiple InsetsTypes corresponding to Window type");
+                    }
+            }
         }
     }
 
@@ -2819,7 +2865,8 @@
         }
 
         mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode);
-        mSideGestureInset = res.getDimensionPixelSize(R.dimen.config_backGestureInset);
+        mLeftGestureInset = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
+        mRightGestureInset = mGestureNavigationSettingsObserver.getRightSensitivity(res);
         mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough);
         mNavigationBarAlwaysShowOnSideGesture =
                 res.getBoolean(R.bool.config_navBarAlwaysShowOnSideEdgeGesture);
@@ -3953,6 +4000,10 @@
         return false;
     }
 
+    void release() {
+        mHandler.post(mGestureNavigationSettingsObserver::unregister);
+    }
+
     @VisibleForTesting
     static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) {
         if (navBarWindow == null || !navBarWindow.isVisibleLw()
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index fc358ce..bd5666d 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -846,10 +846,9 @@
     @VisibleForTesting
     Set<Integer> getProfileIds(int userId) {
         Set<Integer> userIds = new ArraySet<>();
-        final List<UserInfo> profiles = mService.getUserManager().getProfiles(userId,
-                false /* enabledOnly */);
-        for (int i = profiles.size() - 1; i >= 0; --i) {
-            userIds.add(profiles.get(i).id);
+        int[] profileIds = mService.getUserManager().getProfileIds(userId, false /* enabledOnly */);
+        for (int i = 0; i < profileIds.length; i++) {
+            userIds.add(Integer.valueOf(profileIds[i]));
         }
         return userIds;
     }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 15a49a7..ebf1bc9 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2695,7 +2695,9 @@
                 return true;
             }
         } else {
-            if (r.intent.getComponent().equals(cls)) {
+            // Compare the target component instead of intent component so we don't miss if the
+            // activity uses alias.
+            if (r.mActivityComponent.equals(cls)) {
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 19f8ca9..aa817fd 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -36,6 +36,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.function.Supplier;
 
 /**
  * A class that can run animations on objects that have a set of child surfaces. We do this by
@@ -145,7 +146,7 @@
         if (mLeash == null) {
             mLeash = createAnimationLeash(mAnimatable, surface, t, type,
                     mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
-                    0 /* y */, hidden);
+                    0 /* y */, hidden, mService.mTransactionFactory);
             mAnimatable.onAnimationLeashCreated(t, mLeash);
         }
         mAnimatable.onLeashAnimationStarting(t, mLeash);
@@ -374,13 +375,21 @@
 
     static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
             Transaction t, @AnimationType int type, int width, int height, int x, int y,
-            boolean hidden) {
+            boolean hidden, Supplier<Transaction> transactionFactory) {
         if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
         final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
                 .setParent(animatable.getAnimationLeashParent())
-                .setHidden(hidden)
                 .setName(surface + " - animation-leash");
         final SurfaceControl leash = builder.build();
+        if (!hidden) {
+            // TODO(b/151665759) Defer reparent calls
+            // We want the leash to be visible immediately but we want to set the effects on
+            // the layer. Since the transaction used in this function may be deferred, we apply
+            // another transaction immediately with the correct visibility and effects.
+            // If this doesn't work, you will can see the 2/3 button nav bar flicker during
+            // seamless rotation.
+            transactionFactory.get().unsetColor(leash).show(leash).apply();
+        }
         t.setWindowCrop(leash, width, height);
         t.setPosition(leash, x, y);
         t.show(leash);
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 36861aa..8ab5043 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -75,7 +75,8 @@
 
         mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
                 t, ANIMATION_TYPE_SCREEN_ROTATION, startBounds.width(), startBounds.height(),
-                startBounds.left, startBounds.top, false /* hidden */);
+                startBounds.left, startBounds.top, false /* hidden */,
+                mWmService.mTransactionFactory);
         mAnimatable.onAnimationLeashCreated(t, mLeash);
 
         SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget();
@@ -103,7 +104,7 @@
      */
     void unfreeze(SurfaceControl.Transaction t) {
         if (mSnapshot != null) {
-            mSnapshot.destroy(t);
+            mSnapshot.cancelAnimation(t, false /* restarting */);
         }
         if (mLeash == null) {
             return;
@@ -212,7 +213,7 @@
          * @param t The transaction to use for all cancelling surface operations.
          * @param restarting Whether we are restarting the animation.
          */
-        private void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
+        void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
             final SurfaceControl leash = mSurfaceControl;
             final AnimationAdapter animation = mAnimation;
             final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback =
@@ -229,7 +230,6 @@
                 }
             }
             if (!restarting) {
-                // TODO: do we need to destroy?
                 destroy(t);
             }
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 5ab5fbc..f826deb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -431,7 +431,10 @@
     private boolean mForceShowForAllUsers;
 
     /** When set, will force the task to report as invisible. */
-    boolean mForceHidden = false;
+    static final int FLAG_FORCE_HIDDEN_FOR_PINNED_TASK = 1;
+    static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
+    private int mForceHiddenFlags = 0;
+
 
     SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
 
@@ -2127,14 +2130,26 @@
         intersectWithInsetsIfFits(outStableBounds, mTmpBounds, mTmpInsets);
     }
 
+    /**
+     * Forces the app bounds related configuration can be computed by
+     * {@link #computeConfigResourceOverrides(Configuration, Configuration, DisplayInfo,
+     * ActivityRecord.CompatDisplayInsets)}.
+     */
+    private static void invalidateAppBoundsConfig(@NonNull Configuration inOutConfig) {
+        final Rect appBounds = inOutConfig.windowConfiguration.getAppBounds();
+        if (appBounds != null) {
+            appBounds.setEmpty();
+        }
+        inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+        inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+    }
+
     void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
             @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) {
         if (overrideDisplayInfo != null) {
             // Make sure the screen related configs can be computed by the provided display info.
-            inOutConfig.windowConfiguration.setAppBounds(null);
             inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED;
-            inOutConfig.screenWidthDp = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-            inOutConfig.screenHeightDp = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+            invalidateAppBoundsConfig(inOutConfig);
         }
         computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo,
                 null /* compatInsets */);
@@ -2149,6 +2164,10 @@
     void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
             @NonNull Configuration parentConfig,
             @Nullable ActivityRecord.CompatDisplayInsets compatInsets) {
+        if (compatInsets != null) {
+            // Make sure the app bounds can be computed by the compat insets.
+            invalidateAppBoundsConfig(inOutConfig);
+        }
         computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */,
                 compatInsets);
     }
@@ -3031,7 +3050,7 @@
      */
     @VisibleForTesting
     boolean isTranslucent(ActivityRecord starting) {
-        if (!isAttached() || mForceHidden) {
+        if (!isAttached() || isForceHidden()) {
             return true;
         }
         final PooledPredicate p = PooledLambda.obtainPredicate(Task::isOpaqueActivity,
@@ -3370,6 +3389,9 @@
         } else {
             info.pictureInPictureParams = mPictureInPictureParams;
         }
+        info.topActivityInfo = mReuseActivitiesReport.top != null
+                ? mReuseActivitiesReport.top.info
+                : null;
     }
 
     /**
@@ -4026,17 +4048,17 @@
             return;
         }
         // Let the old organizer know it has lost control.
-        if (mTaskOrganizer != null) {
-            sendTaskVanished();
-        }
+        sendTaskVanished();
         mTaskOrganizer = organizer;
         sendTaskAppeared();
+        onTaskOrganizerChanged();
     }
 
     // Called on Binder death.
     void taskOrganizerDied() {
         mTaskOrganizer = null;
         mLastTaskOrganizerWindowingMode = -1;
+        onTaskOrganizerChanged();
     }
 
     /**
@@ -4071,6 +4093,14 @@
         mLastTaskOrganizerWindowingMode = windowingMode;
     }
 
+    private void onTaskOrganizerChanged() {
+        if (mTaskOrganizer == null) {
+            // If this task is no longer controlled by a task organizer, then reset the force hidden
+            // state
+            setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */);
+        }
+    }
+
     @Override
     void setSurfaceControl(SurfaceControl sc) {
         super.setSurfaceControl(sc);
@@ -4181,6 +4211,31 @@
         c.recycle();
     }
 
+    /**
+     * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
+     * @return Whether the force hidden state changed
+     */
+    boolean setForceHidden(int flags, boolean set) {
+        int newFlags = mForceHiddenFlags;
+        if (set) {
+            newFlags |= flags;
+        } else {
+            newFlags &= ~flags;
+        }
+        if (mForceHiddenFlags == newFlags) {
+            return false;
+        }
+        mForceHiddenFlags = newFlags;
+        return true;
+    }
+
+    /**
+     * Returns whether this task is currently forced to be hidden for any reason.
+     */
+    protected boolean isForceHidden() {
+        return mForceHiddenFlags != 0;
+    }
+
     @Override
     long getProtoFieldId() {
         return TASK;
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 9cbc9ee..8f09f3f 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -23,6 +23,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -475,6 +476,7 @@
         if (!(container instanceof Task)) {
             throw new RuntimeException("Invalid token in task transaction");
         }
+        final Task task = (Task) container;
         // The "client"-facing API should prevent bad changes; however, just in case, sanitize
         // masks here.
         int configMask = change.getConfigSetMask();
@@ -498,6 +500,11 @@
                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
             }
         }
+        if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
+            if (task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, change.getHidden())) {
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
+            }
+        }
         return effects;
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f83b052..0f5cafe 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -352,9 +352,19 @@
         }
         task.getBounds(mTmpRect);
         mTmpRect.offsetTo(0, 0);
+
+        SurfaceControl[] excludeLayers;
+        final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
+        if (imeWindow != null) {
+            excludeLayers = new SurfaceControl[1];
+            excludeLayers[0] = imeWindow.getSurfaceControl();
+        } else {
+            excludeLayers = new SurfaceControl[0];
+        }
         final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
-                SurfaceControl.captureLayers(
-                        task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
+                SurfaceControl.captureLayersExcluding(
+                        task.getSurfaceControl(), mTmpRect, scaleFraction,
+                        pixelFormat, excludeLayers);
         if (outTaskSize != null) {
             outTaskSize.x = mTmpRect.width();
             outTaskSize.y = mTmpRect.height();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 164d3e0..45023ac 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -99,10 +99,10 @@
 
         if (lowResTaskSnapshotScale > 0) {
             mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale;
-            setEnableLowResSnapshots(true);
+            mEnableLowResSnapshots = true;
         } else {
             mLowResScaleFactor = 0;
-            setEnableLowResSnapshots(false);
+            mEnableLowResSnapshots = false;
         }
 
         mUse16BitFormat = service.mContext.getResources().getBoolean(
@@ -175,14 +175,6 @@
     }
 
     /**
-     * Not to be used. Only here for testing.
-     */
-    @VisibleForTesting
-    void setEnableLowResSnapshots(boolean enabled) {
-        mEnableLowResSnapshots = enabled;
-    }
-
-    /**
      * Return if task snapshots are stored in 16 bit pixel format.
      *
      * @return true if task snapshots are stored in 16 bit pixel format.
@@ -405,7 +397,7 @@
                 return false;
             }
 
-            if (!enableLowResSnapshots()) {
+            if (!mEnableLowResSnapshots) {
                 swBitmap.recycle();
                 return true;
             }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index a60db94..0b11dd2 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -96,8 +96,8 @@
         mAnimationFrameCallback = frameTimeNs -> {
             synchronized (mService.mGlobalLock) {
                 mAnimationFrameCallbackScheduled = false;
+                animate(frameTimeNs);
             }
-            animate(frameTimeNs);
         };
     }
 
@@ -115,110 +115,94 @@
         mInitialized = true;
     }
 
-    /**
-     * DO NOT HOLD THE WINDOW MANAGER LOCK WHILE CALLING THIS METHOD. Reason: the method closes
-     * an animation transaction, that might be blocking until the next sf-vsync, so we want to make
-     * sure other threads can make progress if this happens.
-     */
     private void animate(long frameTimeNs) {
-
-        synchronized (mService.mGlobalLock) {
-            if (!mInitialized) {
-                return;
-            }
-
-            // Schedule next frame already such that back-pressure happens continuously
-            scheduleAnimation();
+        if (!mInitialized) {
+            return;
         }
 
-        synchronized (mService.mGlobalLock) {
-            mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
-            mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
-            if (DEBUG_WINDOW_TRACE) {
-                Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
+        // Schedule next frame already such that back-pressure happens continuously.
+        scheduleAnimation();
+
+        mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
+        mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
+        if (DEBUG_WINDOW_TRACE) {
+            Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
+        }
+
+        ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
+        mService.openSurfaceTransaction();
+        try {
+            final AccessibilityController accessibilityController =
+                    mService.mAccessibilityController;
+            final int numDisplays = mDisplayContentsAnimators.size();
+            for (int i = 0; i < numDisplays; i++) {
+                final int displayId = mDisplayContentsAnimators.keyAt(i);
+                final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+                // Update animations of all applications, including those associated with
+                // exiting/removed apps.
+                dc.updateWindowsForAnimator();
+                dc.prepareSurfaces();
             }
 
-            ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
-            mService.openSurfaceTransaction();
-            try {
-                final AccessibilityController accessibilityController =
-                        mService.mAccessibilityController;
-                final int numDisplays = mDisplayContentsAnimators.size();
-                for (int i = 0; i < numDisplays; i++) {
-                    final int displayId = mDisplayContentsAnimators.keyAt(i);
-                    final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-                    // Update animations of all applications, including those
-                    // associated with exiting/removed apps
-                    dc.updateWindowsForAnimator();
-                    dc.prepareSurfaces();
+            for (int i = 0; i < numDisplays; i++) {
+                final int displayId = mDisplayContentsAnimators.keyAt(i);
+                final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+
+                dc.checkAppWindowsReadyToShow();
+                if (accessibilityController != null) {
+                    accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
+                            mTransaction);
                 }
-
-                for (int i = 0; i < numDisplays; i++) {
-                    final int displayId = mDisplayContentsAnimators.keyAt(i);
-                    final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
-
-                    dc.checkAppWindowsReadyToShow();
-                    if (accessibilityController != null) {
-                        accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
-                                mTransaction);
-                    }
-                }
-
-                cancelAnimation();
-
-                if (mService.mWatermark != null) {
-                    mService.mWatermark.drawIfNeeded();
-                }
-
-                SurfaceControl.mergeToGlobalTransaction(mTransaction);
-            } catch (RuntimeException e) {
-                Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
-            } finally {
-                mService.closeSurfaceTransaction("WindowAnimator");
-                ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
             }
 
-            boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
-            boolean doRequest = false;
-            if (mBulkUpdateParams != 0) {
-                doRequest = mService.mRoot.copyAnimToLayoutParams();
+            cancelAnimation();
+
+            if (mService.mWatermark != null) {
+                mService.mWatermark.drawIfNeeded();
             }
 
-            if (hasPendingLayoutChanges || doRequest) {
-                mService.mWindowPlacerLocked.requestTraversal();
-            }
+            SurfaceControl.mergeToGlobalTransaction(mTransaction);
+        } catch (RuntimeException e) {
+            Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
+        } finally {
+            mService.closeSurfaceTransaction("WindowAnimator");
+            ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
+        }
 
-            final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN);
-            if (rootAnimating && !mLastRootAnimating) {
+        final boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
+        final boolean doRequest = mBulkUpdateParams != 0 && mService.mRoot.copyAnimToLayoutParams();
+        if (hasPendingLayoutChanges || doRequest) {
+            mService.mWindowPlacerLocked.requestTraversal();
+        }
 
-                // Usually app transitions but quite a load onto the system already (with all the
-                // things happening in app), so pause task snapshot persisting to not increase the
-                // load.
-                mService.mTaskSnapshotController.setPersisterPaused(true);
-                Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
-            }
-            if (!rootAnimating && mLastRootAnimating) {
-                mService.mWindowPlacerLocked.requestTraversal();
-                mService.mTaskSnapshotController.setPersisterPaused(false);
-                Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
-            }
+        final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN);
+        if (rootAnimating && !mLastRootAnimating) {
+            // Usually app transitions but quite a load onto the system already (with all the things
+            // happening in app), so pause task snapshot persisting to not increase the load.
+            mService.mTaskSnapshotController.setPersisterPaused(true);
+            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+        }
+        if (!rootAnimating && mLastRootAnimating) {
+            mService.mWindowPlacerLocked.requestTraversal();
+            mService.mTaskSnapshotController.setPersisterPaused(false);
+            Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+        }
 
-            mLastRootAnimating = rootAnimating;
+        mLastRootAnimating = rootAnimating;
 
-            if (mRemoveReplacedWindows) {
-                mService.mRoot.removeReplacedWindows();
-                mRemoveReplacedWindows = false;
-            }
+        if (mRemoveReplacedWindows) {
+            mService.mRoot.removeReplacedWindows();
+            mRemoveReplacedWindows = false;
+        }
 
-            mService.destroyPreservedSurfaceLocked();
+        mService.destroyPreservedSurfaceLocked();
 
-            executeAfterPrepareSurfacesRunnables();
+        executeAfterPrepareSurfacesRunnables();
 
-            if (DEBUG_WINDOW_TRACE) {
-                Slog.i(TAG, "!!! animate: exit"
-                        + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
-                        + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
-            }
+        if (DEBUG_WINDOW_TRACE) {
+            Slog.i(TAG, "!!! animate: exit"
+                    + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+                    + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ecbbb03..7400d17 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -186,7 +186,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.WorkSource;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
@@ -1007,9 +1006,7 @@
     void openSurfaceTransaction() {
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
-            synchronized (mGlobalLock) {
-                SurfaceControl.openTransaction();
-            }
+            SurfaceControl.openTransaction();
         } finally {
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
@@ -1022,10 +1019,8 @@
     void closeSurfaceTransaction(String where) {
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
-            synchronized (mGlobalLock) {
-                SurfaceControl.closeTransaction();
-                mWindowTracing.logState(where);
-            }
+            SurfaceControl.closeTransaction();
+            mWindowTracing.logState(where);
         } finally {
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
@@ -1166,9 +1161,7 @@
         mAnimator = new WindowAnimator(this);
         mRoot = new RootWindowContainer(this);
 
-        mUseBLAST = DeviceConfig.getBoolean(
-                    DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
-                    WM_USE_BLAST_ADAPTER_FLAG, false);
+        mUseBLAST = true;
 
         mWindowPlacerLocked = new WindowSurfacePlacer(this);
         mTaskSnapshotController = new TaskSnapshotController(this);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a69d259..3617570 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2938,8 +2938,9 @@
         // and add the window only if the permission was granted. Therefore, if
         // the mode is MODE_DEFAULT we want the op to succeed as the window is
         // shown.
-        final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp,
-                getOwningUid(), getOwningPackage(), true);
+        final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, getOwningUid(),
+                getOwningPackage(), true /* startIfModeDefault */, null /* featureId */,
+                "init-default-visibility");
         if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
             setAppOpVisibilityLw(false);
         }
@@ -2947,7 +2948,8 @@
 
     void resetAppOpsState() {
         if (mAppOp != OP_NONE && mAppOpVisibility) {
-            mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
+            mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage(),
+                    null /* featureId */);
         }
     }
 
@@ -2962,11 +2964,12 @@
             // as this would mean we will get another change callback and will reconcile.
             int mode = mWmService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
             if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
-                mWmService.mAppOps.finishOp(mAppOp, uid, packageName);
+                mWmService.mAppOps.finishOp(mAppOp, uid, packageName, null /* featureId */);
                 setAppOpVisibilityLw(false);
             }
         } else {
-            final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
+            final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName,
+                    true /* startIfModeDefault */, null /* featureId */, "attempt-to-be-visible");
             if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
                 setAppOpVisibilityLw(true);
             }
@@ -5693,6 +5696,9 @@
     @Override
     boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
             int waitingId) {
+        if (!isVisible()) {
+            return false;
+        }
         mWaitingListener = waitingListener;
         mWaitingSyncId = waitingId;
         mUsingBLASTSyncTransaction = true;
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index b96fbf5..29b2cd6 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -364,13 +364,8 @@
         }
     }
     void onDestroy() final {
-        ALOGE("Sending EXIT to server.");
-        sendRequest(mOutFd, EXIT);
         // Make sure the receiver thread stopped.
         CHECK(!mReceiverThread.joinable());
-
-        mInFd.reset();
-        mOutFd.reset();
     }
 
     // Installation.
@@ -568,13 +563,6 @@
 
     // Streaming.
     bool initStreaming(unique_fd inout) {
-        mInFd.reset(dup(inout));
-        mOutFd.reset(dup(inout));
-        if (mInFd < 0 || mOutFd < 0) {
-            ALOGE("Failed to dup FDs.");
-            return false;
-        }
-
         mEventFd.reset(eventfd(0, EFD_CLOEXEC));
         if (mEventFd < 0) {
             ALOGE("Failed to create eventfd.");
@@ -583,7 +571,7 @@
 
         // Awaiting adb handshake.
         char okay_buf[OKAY.size()];
-        if (!android::base::ReadFully(mInFd, okay_buf, OKAY.size())) {
+        if (!android::base::ReadFully(inout, okay_buf, OKAY.size())) {
             ALOGE("Failed to receive OKAY. Abort.");
             return false;
         }
@@ -593,13 +581,23 @@
             return false;
         }
 
-        mReceiverThread = std::thread([this]() { receiver(); });
+        {
+            std::lock_guard lock{mOutFdLock};
+            mOutFd.reset(::dup(inout));
+            if (mOutFd < 0) {
+                ALOGE("Failed to create streaming fd.");
+            }
+        }
+
+        mReceiverThread =
+                std::thread([this, io = std::move(inout)]() mutable { receiver(std::move(io)); });
         ALOGI("Started streaming...");
         return true;
     }
 
     // IFS callbacks.
     void onPendingReads(dataloader::PendingReads pendingReads) final {
+        std::lock_guard lock{mOutFdLock};
         CHECK(mIfs);
         for (auto&& pendingRead : pendingReads) {
             const android::dataloader::FileId& fileId = pendingRead.id;
@@ -625,12 +623,12 @@
         }
     }
 
-    void receiver() {
+    void receiver(unique_fd inout) {
         std::vector<uint8_t> data;
         std::vector<IncFsDataBlock> instructions;
         std::unordered_map<FileIdx, unique_fd> writeFds;
         while (!mStopReceiving) {
-            const int res = waitForDataOrSignal(mInFd, mEventFd);
+            const int res = waitForDataOrSignal(inout, mEventFd);
             if (res == 0) {
                 continue;
             }
@@ -640,10 +638,11 @@
                 break;
             }
             if (res == mEventFd) {
-                ALOGE("Received stop signal. Exit.");
+                ALOGE("Received stop signal. Sending EXIT to server.");
+                sendRequest(inout, EXIT);
                 break;
             }
-            if (!readChunk(mInFd, data)) {
+            if (!readChunk(inout, data)) {
                 ALOGE("Failed to read a message. Abort.");
                 mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
                 break;
@@ -656,7 +655,7 @@
                     ALOGI("Stop signal received. Sending exit command (remaining bytes: %d).",
                           int(remainingData.size()));
 
-                    sendRequest(mOutFd, EXIT);
+                    sendRequest(inout, EXIT);
                     mStopReceiving = true;
                     break;
                 }
@@ -699,6 +698,11 @@
             writeInstructions(instructions);
         }
         writeInstructions(instructions);
+
+        {
+            std::lock_guard lock{mOutFdLock};
+            mOutFd.reset();
+        }
     }
 
     void writeInstructions(std::vector<IncFsDataBlock>& instructions) {
@@ -742,7 +746,7 @@
     std::string mArgs;
     android::dataloader::FilesystemConnectorPtr mIfs = nullptr;
     android::dataloader::StatusListenerPtr mStatusListener = nullptr;
-    android::base::unique_fd mInFd;
+    std::mutex mOutFdLock;
     android::base::unique_fd mOutFd;
     android::base::unique_fd mEventFd;
     std::thread mReceiverThread;
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 239a101..fe05d4e 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -178,6 +178,92 @@
     return isOk;
 }
 
+enum class HalSupport {
+    UNKNOWN = 0,
+    ON,
+    OFF,
+};
+
+static void setPowerBoostWithHandle(sp<IPowerAidl> handle, Boost boost, int32_t durationMs) {
+    // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
+    // Need to increase the array size if more boost supported.
+    static std::array<std::atomic<HalSupport>,
+                      static_cast<int32_t>(Boost::DISPLAY_UPDATE_IMMINENT) + 1>
+            boostSupportedArray = {HalSupport::UNKNOWN};
+
+    // Quick return if boost is not supported by HAL
+    if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
+        boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
+        ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str());
+        return;
+    }
+
+    if (boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
+        bool isSupported = false;
+        handle->isBoostSupported(boost, &isSupported);
+        boostSupportedArray[static_cast<int32_t>(boost)] =
+            isSupported ? HalSupport::ON : HalSupport::OFF;
+        if (!isSupported) {
+            ALOGV("Skipped setPowerBoost %s because HAL doesn't support it",
+                  toString(boost).c_str());
+            return;
+        }
+    }
+
+    auto ret = handle->setBoost(boost, durationMs);
+    processPowerHalReturn(ret.isOk(), "setPowerBoost");
+}
+
+static void setPowerBoost(Boost boost, int32_t durationMs) {
+    std::unique_lock<std::mutex> lock(gPowerHalMutex);
+    if (connectPowerHalLocked() != HalVersion::AIDL) {
+        ALOGV("Power HAL AIDL not available");
+        return;
+    }
+    sp<IPowerAidl> handle = gPowerHalAidl_;
+    lock.unlock();
+    setPowerBoostWithHandle(handle, boost, durationMs);
+}
+
+static void setPowerModeWithHandle(sp<IPowerAidl> handle, Mode mode, bool enabled) {
+    // Android framework only sends mode upto DISPLAY_INACTIVE.
+    // Need to increase the array if more mode supported.
+    static std::array<std::atomic<HalSupport>, static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1>
+            modeSupportedArray = {HalSupport::UNKNOWN};
+
+    // Quick return if mode is not supported by HAL
+    if (mode > Mode::DISPLAY_INACTIVE ||
+        modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
+        ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
+        return;
+    }
+
+    if (modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
+        bool isSupported = false;
+        handle->isModeSupported(mode, &isSupported);
+        modeSupportedArray[static_cast<int32_t>(mode)] =
+            isSupported ? HalSupport::ON : HalSupport::OFF;
+        if (!isSupported) {
+            ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
+            return;
+        }
+    }
+
+    auto ret = handle->setMode(mode, enabled);
+    processPowerHalReturn(ret.isOk(), "setPowerMode");
+}
+
+static void setPowerMode(Mode mode, bool enabled) {
+    std::unique_lock<std::mutex> lock(gPowerHalMutex);
+    if (connectPowerHalLocked() != HalVersion::AIDL) {
+        ALOGV("Power HAL AIDL not available");
+        return;
+    }
+    sp<IPowerAidl> handle = gPowerHalAidl_;
+    lock.unlock();
+    setPowerModeWithHandle(handle, mode, enabled);
+}
+
 static void sendPowerHint(PowerHint hintId, uint32_t data) {
     std::unique_lock<std::mutex> lock(gPowerHalMutex);
     switch (connectPowerHalLocked()) {
@@ -201,14 +287,28 @@
             if (hintId == PowerHint::INTERACTION) {
                 sp<IPowerAidl> handle = gPowerHalAidl_;
                 lock.unlock();
-                auto ret = handle->setBoost(Boost::INTERACTION, data);
-                processPowerHalReturn(ret.isOk(), "setBoost");
+                setPowerBoostWithHandle(handle, Boost::INTERACTION, data);
                 break;
             } else if (hintId == PowerHint::LAUNCH) {
                 sp<IPowerAidl> handle = gPowerHalAidl_;
                 lock.unlock();
-                auto ret = handle->setMode(Mode::LAUNCH, static_cast<bool>(data));
-                processPowerHalReturn(ret.isOk(), "setMode");
+                setPowerModeWithHandle(handle, Mode::LAUNCH, static_cast<bool>(data));
+                break;
+            } else if (hintId == PowerHint::LOW_POWER) {
+                sp<IPowerAidl> handle = gPowerHalAidl_;
+                lock.unlock();
+                setPowerModeWithHandle(handle, Mode::LOW_POWER, static_cast<bool>(data));
+                break;
+            } else if (hintId == PowerHint::SUSTAINED_PERFORMANCE) {
+                sp<IPowerAidl> handle = gPowerHalAidl_;
+                lock.unlock();
+                setPowerModeWithHandle(handle, Mode::SUSTAINED_PERFORMANCE,
+                                       static_cast<bool>(data));
+                break;
+            } else if (hintId == PowerHint::VR_MODE) {
+                sp<IPowerAidl> handle = gPowerHalAidl_;
+                lock.unlock();
+                setPowerModeWithHandle(handle, Mode::VR, static_cast<bool>(data));
                 break;
             } else {
                 ALOGE("Unsupported power hint: %s.", toString(hintId).c_str());
@@ -223,87 +323,6 @@
     SurfaceComposerClient::notifyPowerHint(static_cast<int32_t>(hintId));
 }
 
-enum class HalSupport {
-    UNKNOWN = 0,
-    ON,
-    OFF,
-};
-
-static void setPowerBoost(Boost boost, int32_t durationMs) {
-    // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
-    // Need to increase the array size if more boost supported.
-    static std::array<std::atomic<HalSupport>,
-                      static_cast<int32_t>(Boost::DISPLAY_UPDATE_IMMINENT) + 1>
-        boostSupportedArray = {HalSupport::UNKNOWN};
-
-    // Quick return if boost is not supported by HAL
-    if (boost > Boost::DISPLAY_UPDATE_IMMINENT ||
-        boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) {
-        ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str());
-        return;
-    }
-
-    std::unique_lock<std::mutex> lock(gPowerHalMutex);
-    if (connectPowerHalLocked() != HalVersion::AIDL) {
-        ALOGV("Power HAL AIDL not available");
-        return;
-    }
-    sp<IPowerAidl> handle = gPowerHalAidl_;
-    lock.unlock();
-
-    if (boostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) {
-        bool isSupported = false;
-        handle->isBoostSupported(boost, &isSupported);
-        boostSupportedArray[static_cast<int32_t>(boost)] =
-            isSupported ? HalSupport::ON : HalSupport::OFF;
-        if (!isSupported) {
-            ALOGV("Skipped setPowerBoost %s because HAL doesn't support it",
-                  toString(boost).c_str());
-            return;
-        }
-    }
-
-    auto ret = handle->setBoost(boost, durationMs);
-    processPowerHalReturn(ret.isOk(), "setPowerBoost");
-}
-
-static void setPowerMode(Mode mode, bool enabled) {
-    // Android framework only sends mode upto DISPLAY_INACTIVE.
-    // Need to increase the array if more mode supported.
-    static std::array<std::atomic<HalSupport>,
-                      static_cast<int32_t>(Mode::DISPLAY_INACTIVE) + 1>
-        modeSupportedArray = {HalSupport::UNKNOWN};
-
-    // Quick return if mode is not supported by HAL
-    if (mode > Mode::DISPLAY_INACTIVE ||
-        modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) {
-        ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
-        return;
-    }
-
-    std::unique_lock<std::mutex> lock(gPowerHalMutex);
-    if (connectPowerHalLocked() != HalVersion::AIDL) {
-        ALOGV("Power HAL AIDL not available");
-        return;
-    }
-    sp<IPowerAidl> handle = gPowerHalAidl_;
-    lock.unlock();
-
-    if (modeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) {
-        bool isSupported = false;
-        handle->isModeSupported(mode, &isSupported);
-        modeSupportedArray[static_cast<int32_t>(mode)] =
-            isSupported ? HalSupport::ON : HalSupport::OFF;
-        if (!isSupported) {
-            ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str());
-            return;
-        }
-    }
-
-    auto ret = handle->setMode(mode, enabled);
-    processPowerHalReturn(ret.isOk(), "setPowerMode");
-}
-
 void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
     if (gPowerManagerServiceObj) {
         // Throttle calls into user activity by event type.
@@ -426,8 +445,7 @@
         case HalVersion::AIDL: {
             sp<IPowerAidl> handle = gPowerHalAidl_;
             lock.unlock();
-            auto ret = handle->setMode(Mode::INTERACTIVE, enable);
-            processPowerHalReturn(ret.isOk(), "setMode");
+            setPowerModeWithHandle(handle, Mode::INTERACTIVE, enable);
             return;
         }
         default: {
@@ -481,8 +499,9 @@
             return;
         }
         case HalVersion::AIDL: {
-            auto ret = gPowerHalAidl_->setMode(Mode::DOUBLE_TAP_TO_WAKE, static_cast<bool>(data));
-            processPowerHalReturn(ret.isOk(), "setMode");
+            sp<IPowerAidl> handle = gPowerHalAidl_;
+            lock.unlock();
+            setPowerModeWithHandle(handle, Mode::DOUBLE_TAP_TO_WAKE, static_cast<bool>(data));
             return;
         }
         default: {
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index be11b86..0277f16 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -25,17 +25,14 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/fsverity.h>
+#include <linux/stat.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <type_traits>
-
 #include <android-base/unique_fd.h>
 
-const int kSha256Bytes = 32;
-
 namespace android {
 
 namespace {
@@ -69,30 +66,28 @@
     return 0;
 }
 
-int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
-    using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kSha256Bytes>;
-
-    Storage bytes;
-    fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
-    data->digest_size = kSha256Bytes;  // the only input/output parameter
-
+// Returns whether the file has fs-verity enabled.
+// 0 if it is not present, 1 if is present, and -errno if there was an error.
+int statxForFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath) {
     ScopedUtfChars path(env, filePath);
-    if (path.c_str() == nullptr) {
-        return EINVAL;
+
+    struct statx out = {};
+    if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) {
+        return -errno;
     }
-    ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
-    if (rfd.get() < 0) {
-        return errno;
+
+    // Sanity check.
+    if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) {
+        ALOGE("Unexpected, STATX_ATTR_VERITY not supported by kernel");
+        return -ENOSYS;
     }
-    if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
-        return errno;
-    }
-    return 0;
+
+    return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
 }
 
 const JNINativeMethod sMethods[] = {
-    { "enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity },
-    { "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity },
+        {"enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity},
+        {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity},
 };
 
 }  // namespace
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f5d2c6a..fc9044a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -393,6 +393,7 @@
     private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
 
     private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
+    private static final long MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD = 3 * MS_PER_DAY;
 
     private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION =
             "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION";
@@ -15849,6 +15850,12 @@
             // DO shouldn't be able to use this method.
             enforceProfileOwnerOfOrganizationOwnedDevice(admin);
             enforceHandlesCheckPolicyComplianceIntent(userId, admin.info.getPackageName());
+            Preconditions.checkArgument(timeoutMillis >= 0, "Timeout must be non-negative.");
+            // Ensure the timeout is long enough to avoid having bad user experience.
+            if (timeoutMillis > 0 && timeoutMillis < MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD
+                    && !isAdminTestOnlyLocked(who, userId)) {
+                timeoutMillis = MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD;
+            }
             if (admin.mProfileMaximumTimeOffMillis == timeoutMillis) {
                 return;
             }
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 602e4e1..25ab5d3 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -27,7 +27,7 @@
         "services.net",
     ],
 
-    libs: ["ike-stubs"],
+    libs: ["android.net.ipsec.ike.stubs.system"],
 }
 
 //##################################################################
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 5160eae..32587ac 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -29,7 +29,7 @@
         "services.net",
     ],
 
-    libs: ["ike-stubs"],
+    libs: ["android.net.ipsec.ike.stubs.system"],
 }
 
 //##################################################################
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 0551f2e..a587029 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -538,6 +538,7 @@
     }
 
     @Test
+    @Ignore("b/151150320")
     public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
         HdmiCecMessage message =
                 HdmiCecMessageBuilder.buildSystemAudioModeRequest(
@@ -587,6 +588,7 @@
     }
 
     @Test
+    @Ignore("b/151150320")
     public void handleRoutingChange_currentActivePortIsHome() {
         HdmiCecMessage message =
                 HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, SELF_PHYSICAL_ADDRESS);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 4a1af51..f72d622 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -139,6 +139,7 @@
     }
 
     @Test
+    @Ignore("b/151147315")
     public void doNotWakeUpOnHotPlug_PlugIn() {
         mWokenUp = false;
         mHdmiCecLocalDevicePlayback.onHotplug(0, true);
@@ -146,6 +147,7 @@
     }
 
     @Test
+    @Ignore("b/151147315")
     public void doNotWakeUpOnHotPlug_PlugOut() {
         mWokenUp = false;
         mHdmiCecLocalDevicePlayback.onHotplug(0, false);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 81545d4..d3a3e7e 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -480,6 +480,13 @@
         assertThat(mService.getCurrentRules().getList()).containsExactly(rule);
     }
 
+    @Test
+    public void getWhitelistedRuleProviders() throws Exception {
+        whitelistUsAsRuleProvider();
+
+        assertThat(mService.getWhitelistedRuleProviders()).containsExactly(TEST_FRAMEWORK_PACKAGE);
+    }
+
     private void whitelistUsAsRuleProvider() {
         Resources mockResources = mock(Resources.class);
         when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
index 70a927c..c5e924b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
@@ -15,16 +15,23 @@
  */
 package com.android.server.locksettings;
 
+import android.content.ContentResolver;
+import android.os.UserHandle;
 import android.provider.Settings;
 
 public class FakeSettings {
 
     private int mDeviceProvisioned;
+    private int mSecureFrpMode;
 
     public void setDeviceProvisioned(boolean provisioned) {
         mDeviceProvisioned = provisioned ? 1 : 0;
     }
 
+    public void setSecureFrpMode(boolean secure) {
+        mSecureFrpMode = secure ? 1 : 0;
+    }
+
     public int globalGetInt(String keyName) {
         switch (keyName) {
             case Settings.Global.DEVICE_PROVISIONED:
@@ -33,4 +40,12 @@
                 throw new IllegalArgumentException("Unhandled global settings: " + keyName);
         }
     }
+
+    public int secureGetInt(ContentResolver contentResolver, String keyName, int defaultValue,
+            int userId) {
+        if (Settings.Secure.SECURE_FRP_MODE.equals(keyName) && userId == UserHandle.USER_SYSTEM) {
+            return mSecureFrpMode;
+        }
+        return defaultValue;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 1ff451b..4e1454b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -124,6 +124,12 @@
         }
 
         @Override
+        public int settingsSecureGetInt(ContentResolver contentResolver, String keyName,
+                int defaultValue, int userId) {
+            return mSettings.secureGetInt(contentResolver, keyName, defaultValue, userId);
+        }
+
+        @Override
         public UserManagerInternal getUserManagerInternal() {
             return mUserManagerInternal;
         }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 684bbd4..661ce11 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -416,6 +416,15 @@
                         eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID));
     }
 
+    @Test
+    public void testCredentialChangeNotPossibleInSecureFrpMode() {
+        mSettings.setSecureFrpMode(true);
+        try {
+            mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID);
+            fail("Password shouldn't be changeable before FRP unlock");
+        } catch (SecurityException e) { }
+    }
+
     private void testCreateCredential(int userId, LockscreenCredential credential)
             throws RemoteException {
         assertTrue(mService.setLockCredential(credential, nonePassword(), userId));
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 7e3cfc8..128177b 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1084,7 +1084,7 @@
 
         // pretend that 512 bytes total have happened
         stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
+                .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
         when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
                 .thenReturn(stats.getTotalBytes());
 
@@ -1276,11 +1276,11 @@
             history.recordData(start, end,
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
             stats.clear();
-            stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.addEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
 
             reset(mNotifManager);
@@ -1304,9 +1304,9 @@
             history.recordData(start, end,
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
             stats.clear();
-            stats.addEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
-            stats.addEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
+            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
                     DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
 
             reset(mNotifManager);
@@ -1338,7 +1338,7 @@
         // bring up wifi network with metered policy
         state = new NetworkState[] { buildWifi() };
         stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
 
         {
             when(mConnManager.getAllNetworkState()).thenReturn(state);
@@ -1769,7 +1769,7 @@
         final int CYCLE_DAY = 15;
 
         final NetworkStats stats = new NetworkStats(0L, 1);
-        stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+        stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
                 2999, 1, 2000, 1, 0);
         when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
                 .thenReturn(stats.getTotalBytes());
@@ -1793,7 +1793,7 @@
         reset(mStatsService);
 
         // Increase the usage.
-        stats.addEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
+        stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
                 1000, 1, 999, 1, 0);
         when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
                 .thenReturn(stats.getTotalBytes());
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 5d5c714..22591c6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -31,6 +31,7 @@
 import android.content.pm.Signature;
 import android.content.pm.parsing.ParsingPackage;
 import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedProvider;
 import android.os.Build;
@@ -135,6 +136,13 @@
                 .addActivity(activity);
     }
 
+    private static ParsingPackage pkgWithInstrumentation(
+            String packageName, String instrumentationTargetPackage) {
+        ParsedInstrumentation instrumentation = new ParsedInstrumentation();
+        instrumentation.setTargetPackage(instrumentationTargetPackage);
+        return pkg(packageName).addInstrumentation(instrumentation);
+    }
+
     private static ParsingPackage pkgWithProvider(String packageName, String authority) {
         ParsedProvider provider = new ParsedProvider();
         provider.setPackageName(packageName);
@@ -608,6 +616,25 @@
         assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
     }
 
+    @Test
+    public void testInstrumentation_DoesntFilter() {
+        final AppsFilter appsFilter =
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
+
+
+        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
+                DUMMY_TARGET_UID);
+        PackageSetting instrumentation = simulateAddPackage(appsFilter,
+                pkgWithInstrumentation("com.some.other.package", "com.some.package"),
+                DUMMY_CALLING_UID);
+
+        assertFalse(
+                appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, instrumentation, target, 0));
+        assertFalse(
+                appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, target, instrumentation, 0));
+    }
+
     private interface WithSettingBuilder {
         PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
     }
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 8da3bdf..155c6dd 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -28,6 +28,8 @@
 import android.media.tv.tuner.frontend.FrontendSettings;
 import android.media.tv.tunerresourcemanager.IResourcesReclaimListener;
 import android.media.tv.tunerresourcemanager.ResourceClientProfile;
+import android.media.tv.tunerresourcemanager.TunerDemuxRequest;
+import android.media.tv.tunerresourcemanager.TunerDescramblerRequest;
 import android.media.tv.tunerresourcemanager.TunerFrontendInfo;
 import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
@@ -92,6 +94,13 @@
             }
         };
 
+    private static int getResourceIdFromHandle(int resourceHandle) {
+        if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+            return resourceHandle;
+        }
+        return (resourceHandle & 0x00ff0000) >> 16;
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -229,14 +238,15 @@
     public void requestFrontendTest_ClientNotRegistered() {
         TunerFrontendRequest request =
                 new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendId = new int[1];
+        int[] frontendHandle = new int[1];
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isFalse();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isFalse();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+        assertThat(getResourceIdFromHandle(frontendHandle[0]))
+                .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
     }
 
     @Test
@@ -256,14 +266,15 @@
 
         TunerFrontendRequest request =
                 new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendId = new int[1];
+        int[] frontendHandle = new int[1];
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isFalse();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isFalse();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID);
+        assertThat(getResourceIdFromHandle(frontendHandle[0]))
+                .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
     }
 
     @Test
@@ -287,14 +298,14 @@
 
         TunerFrontendRequest request =
                 new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendId = new int[1];
+        int[] frontendHandle = new int[1];
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isTrue();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isTrue();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        assertThat(frontendId[0]).isEqualTo(0);
+        assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(0);
     }
 
     @Test
@@ -322,26 +333,26 @@
                 new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
-        int[] frontendId = new int[1];
+        int[] frontendHandle = new int[1];
         TunerFrontendRequest request =
                 new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isTrue();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isTrue();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+        assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
 
         request =
                 new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isTrue();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isTrue();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+        assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[1].getId());
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
                 .isInUse()).isTrue();
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].getId())
@@ -382,10 +393,10 @@
 
         TunerFrontendRequest request =
                 new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendId = new int[1];
+        int[] frontendHandle = new int[1];
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isTrue();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isTrue();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -393,8 +404,8 @@
         request =
                 new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isFalse();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isFalse();
             assertThat(listener.isRelaimed()).isFalse();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -403,8 +414,8 @@
         request =
                 new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isFalse();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isFalse();
             assertThat(listener.isRelaimed()).isFalse();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -444,24 +455,24 @@
 
         TunerFrontendRequest request =
                 new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendId = new int[1];
+        int[] frontendHandle = new int[1];
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isTrue();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isTrue();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+        assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
 
         request =
                 new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isTrue();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isTrue();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        assertThat(frontendId[0]).isEqualTo(infos[1].getId());
+        assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[1].getId());
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
                 .isInUse()).isTrue();
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
@@ -493,14 +504,14 @@
 
         TunerFrontendRequest request =
                 new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
-        int[] frontendId = new int[1];
+        int[] frontendHandle = new int[1];
         try {
-            assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId))
-                    .isTrue();
+            assertThat(mTunerResourceManagerService
+                    .requestFrontendInternal(request, frontendHandle)).isTrue();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        assertThat(frontendId[0]).isEqualTo(infos[0].getId());
+        assertThat(getResourceIdFromHandle(frontendHandle[0])).isEqualTo(infos[0].getId());
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
                 .isInUse()).isTrue();
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
@@ -515,4 +526,38 @@
         assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse();
 
     }
+
+    @Test
+    public void requestDemuxTest() {
+        // Register client
+        ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        int[] clientId = new int[1];
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profile, null /*listener*/, clientId);
+        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+        int[] demuxHandle = new int[1];
+        TunerDemuxRequest request = new TunerDemuxRequest(clientId[0]);
+        assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle))
+                .isTrue();
+        assertThat(getResourceIdFromHandle(demuxHandle[0])).isEqualTo(0);
+    }
+
+    @Test
+    public void requestDescramblerTest() {
+        // Register client
+        ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        int[] clientId = new int[1];
+        mTunerResourceManagerService.registerClientProfileInternal(
+                profile, null /*listener*/, clientId);
+        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+
+        int[] desHandle = new int[1];
+        TunerDescramblerRequest request = new TunerDescramblerRequest(clientId[0]);
+        assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
+                .isTrue();
+        assertThat(getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 7af3ec6..88f368e 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -20,7 +20,7 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -103,7 +103,8 @@
         aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
                 REASON_MAIN_USAGE);
         aih.setAppStandbyBucket(PACKAGE_3, USER_ID, 2500, STANDBY_BUCKET_RESTRICTED,
-                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+                REASON_MAIN_FORCED_BY_SYSTEM
+                        | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
         aih.setAppStandbyBucket(PACKAGE_4, USER_ID, 2750, STANDBY_BUCKET_RESTRICTED,
                 REASON_MAIN_FORCED_BY_USER);
         aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
@@ -114,7 +115,8 @@
         assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
         assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
-                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+                REASON_MAIN_FORCED_BY_SYSTEM
+                        | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
         assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
                 REASON_MAIN_FORCED_BY_USER);
 
@@ -133,7 +135,8 @@
         assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
         assertEquals(aih.getAppStandbyBucket(PACKAGE_3, USER_ID, 3000), STANDBY_BUCKET_RESTRICTED);
         assertEquals(aih.getAppStandbyReason(PACKAGE_3, USER_ID, 3000),
-                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_RESTRICT_BACKGROUND_RESOURCE_USAGE);
+                REASON_MAIN_FORCED_BY_SYSTEM
+                        | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
         assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
                 REASON_MAIN_FORCED_BY_USER);
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 387e62d..f070bff 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -28,6 +28,10 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
+import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
@@ -434,6 +438,11 @@
                 true);
     }
 
+    private int getStandbyBucketReason(String packageName) {
+        return mController.getAppStandbyBucketReason(packageName, USER_ID,
+                mInjector.mElapsedRealtime);
+    }
+
     private void assertBucket(int bucket) {
         assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
     }
@@ -810,7 +819,7 @@
     }
 
     @Test
-    public void testPredictionRaiseFromRestrictedTimeout() {
+    public void testPredictionRaiseFromRestrictedTimeout_highBucket() {
         reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
 
         // Way past all timeouts. App times out into RESTRICTED bucket.
@@ -823,6 +832,24 @@
         mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
                 REASON_MAIN_PREDICTED);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+    }
+
+    @Test
+    public void testPredictionRaiseFromRestrictedTimeout_lowBucket() {
+        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+
+        // Way past all timeouts. App times out into RESTRICTED bucket.
+        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+        // Prediction into a low bucket means no expectation of the app being used, so we shouldn't
+        // elevate the app from RESTRICTED.
+        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_MAIN_PREDICTED);
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
     }
 
     @Test
@@ -987,6 +1014,79 @@
     }
 
     @Test
+    public void testSystemForcedFlags_NotAddedForUserForce() throws Exception {
+        final int expectedReason = REASON_MAIN_FORCED_BY_USER;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_USER);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+        assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1));
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+        assertEquals(expectedReason, getStandbyBucketReason(PACKAGE_1));
+    }
+
+    @Test
+    public void testSystemForcedFlags_AddedForSystemForce() throws Exception {
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_MAIN_DEFAULT);
+        mInjector.mElapsedRealtime += 4 * RESTRICTED_THRESHOLD;
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM
+                        | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+        assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+                        | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
+                getStandbyBucketReason(PACKAGE_1));
+
+        mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+        // Flags should be combined
+        assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+                | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE
+                | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE, getStandbyBucketReason(PACKAGE_1));
+    }
+
+    @Test
+    public void testSystemForcedFlags_SystemForceChangesBuckets() throws Exception {
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_MAIN_DEFAULT);
+        mInjector.mElapsedRealtime += 4 * RESTRICTED_THRESHOLD;
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_MAIN_FORCED_BY_SYSTEM
+                        | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
+        assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+        assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+                        | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE,
+                getStandbyBucketReason(PACKAGE_1));
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE);
+        assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
+        // Flags should be combined
+        assertEquals(REASON_MAIN_FORCED_BY_SYSTEM
+                        | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE
+                        | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
+                getStandbyBucketReason(PACKAGE_1));
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
+        // Flags should be combined
+        assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY,
+                getStandbyBucketReason(PACKAGE_1));
+
+        mController.restrictApp(PACKAGE_1, USER_ID, REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED);
+        assertEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
+        // Flags should not be combined since the bucket changed.
+        assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_UNDEFINED,
+                getStandbyBucketReason(PACKAGE_1));
+    }
+
+    @Test
     public void testAddActiveDeviceAdmin() {
         assertActiveAdmins(USER_ID, (String[]) null);
         assertActiveAdmins(USER_ID2, (String[]) null);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
new file mode 100644
index 0000000..9636342
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
@@ -0,0 +1,257 @@
+/*
+ * 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.notification;
+
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BubbleCheckerTest extends UiServiceTestCase {
+
+    private static final String SHORTCUT_ID = "shortcut";
+    private static final String PKG = "pkg";
+    private static final String KEY = "key";
+    private static final int USER_ID = 1;
+
+    @Mock
+    ActivityManager mActivityManager;
+    @Mock
+    RankingConfig mRankingConfig;
+    @Mock
+    ShortcutHelper mShortcutHelper;
+
+    @Mock
+    NotificationRecord mNr;
+    @Mock
+    UserHandle mUserHandle;
+    @Mock
+    Notification mNotif;
+    @Mock
+    StatusBarNotification mSbn;
+    @Mock
+    NotificationChannel mChannel;
+    @Mock
+    Notification.BubbleMetadata mBubbleMetadata;
+    @Mock
+    PendingIntent mPendingIntent;
+    @Mock
+    Intent mIntent;
+
+    BubbleExtractor.BubbleChecker mBubbleChecker;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mNr.getKey()).thenReturn(KEY);
+        when(mNr.getSbn()).thenReturn(mSbn);
+        when(mNr.getUser()).thenReturn(mUserHandle);
+        when(mUserHandle.getIdentifier()).thenReturn(USER_ID);
+        when(mNr.getChannel()).thenReturn(mChannel);
+        when(mSbn.getPackageName()).thenReturn(PKG);
+        when(mSbn.getUser()).thenReturn(mUserHandle);
+        when(mNr.getNotification()).thenReturn(mNotif);
+        when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata);
+
+        mBubbleChecker = new BubbleExtractor.BubbleChecker(mContext,
+                mShortcutHelper,
+                mRankingConfig,
+                mActivityManager);
+    }
+
+    void setUpIntentBubble() {
+        when(mPendingIntent.getIntent()).thenReturn(mIntent);
+        when(mBubbleMetadata.getBubbleIntent()).thenReturn(mPendingIntent);
+        when(mBubbleMetadata.getShortcutId()).thenReturn(null);
+    }
+
+    void setUpShortcutBubble(boolean isValid) {
+        when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
+        when(mShortcutHelper.hasValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
+                .thenReturn(isValid);
+        when(mBubbleMetadata.getBubbleIntent()).thenReturn(null);
+    }
+
+    void setUpBubblesEnabled(boolean feature, boolean app, boolean channel) {
+        when(mRankingConfig.bubblesEnabled()).thenReturn(feature);
+        when(mRankingConfig.areBubblesAllowed(PKG, USER_ID)).thenReturn(app);
+        when(mChannel.canBubble()).thenReturn(channel);
+    }
+
+    void setUpActivityIntent(boolean isResizable) {
+        when(mPendingIntent.getIntent()).thenReturn(mIntent);
+        ActivityInfo info = new ActivityInfo();
+        info.resizeMode = isResizable
+                ? RESIZE_MODE_RESIZEABLE
+                : RESIZE_MODE_UNRESIZEABLE;
+        when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(info);
+    }
+
+    //
+    // canBubble
+    //
+
+    @Test
+    public void testCanBubble_true_intentBubble() {
+        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+        setUpIntentBubble();
+        setUpActivityIntent(true /* isResizable */);
+        when(mActivityManager.isLowRamDevice()).thenReturn(false);
+        assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+    }
+
+    @Test
+    public void testCanBubble_true_shortcutBubble() {
+        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+        setUpShortcutBubble(true /* isValid */);
+        assertTrue(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+    }
+
+    @Test
+    public void testCanBubble_false_noIntentInvalidShortcut() {
+        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+        setUpShortcutBubble(false /* isValid */);
+        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+    }
+
+    @Test
+    public void testCanBubble_false_noIntentNoShortcut() {
+        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+        when(mBubbleMetadata.getBubbleIntent()).thenReturn(null);
+        when(mBubbleMetadata.getShortcutId()).thenReturn(null);
+        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+    }
+
+    @Test
+    public void testCanBubbble_false_noMetadata() {
+        setUpBubblesEnabled(true/* feature */, true /* app */, true /* channel */);
+        when(mNotif.getBubbleMetadata()).thenReturn(null);
+        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+    }
+
+    @Test
+    public void testCanBubble_false_bubblesNotEnabled() {
+        setUpBubblesEnabled(false /* feature */, true /* app */, true /* channel */);
+        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+    }
+
+    @Test
+    public void testCanBubble_false_packageNotAllowed() {
+        setUpBubblesEnabled(true /* feature */, false /* app */, true /* channel */);
+        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+    }
+
+    @Test
+    public void testCanBubble_false_channelNotAllowed() {
+        setUpBubblesEnabled(true /* feature */, true /* app */, false /* channel */);
+        assertFalse(mBubbleChecker.canBubble(mNr, PKG, USER_ID));
+    }
+
+    //
+    // canLaunchInActivityView
+    //
+
+    @Test
+    public void testCanLaunchInActivityView_true() {
+        setUpActivityIntent(true /* resizable */);
+        assertTrue(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+    }
+
+    @Test
+    public void testCanLaunchInActivityView_false_noIntent() {
+        when(mPendingIntent.getIntent()).thenReturn(null);
+        assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+    }
+
+    @Test
+    public void testCanLaunchInActivityView_false_noInfo() {
+        when(mPendingIntent.getIntent()).thenReturn(mIntent);
+        when(mIntent.resolveActivityInfo(any(), anyInt())).thenReturn(null);
+        assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+    }
+
+    @Test
+    public void testCanLaunchInActivityView_false_notResizable() {
+        setUpActivityIntent(false  /* resizable */);
+        assertFalse(mBubbleChecker.canLaunchInActivityView(mContext, mPendingIntent, PKG));
+    }
+
+    //
+    // isNotificationAppropriateToBubble
+    //
+
+    @Test
+    public void testIsNotifAppropriateToBubble_true() {
+        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+        setUpIntentBubble();
+        when(mActivityManager.isLowRamDevice()).thenReturn(false);
+        setUpActivityIntent(true /* resizable */);
+        doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle();
+
+        assertTrue(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
+    }
+
+    @Test
+    public void testIsNotifAppropriateToBubble_false_lowRam() {
+        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+        when(mActivityManager.isLowRamDevice()).thenReturn(true);
+        setUpActivityIntent(true /* resizable */);
+        doReturn(Notification.MessagingStyle.class).when(mNotif).getNotificationStyle();
+
+        assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
+    }
+
+    @Test
+    public void testIsNotifAppropriateToBubble_false_notMessageStyle() {
+        setUpBubblesEnabled(true /* feature */, true /* app */, true /* channel */);
+        when(mActivityManager.isLowRamDevice()).thenReturn(false);
+        setUpActivityIntent(true /* resizable */);
+        doReturn(Notification.BigPictureStyle.class).when(mNotif).getNotificationStyle();
+
+        assertFalse(mBubbleChecker.isNotificationAppropriateToBubble(mNr));
+    }
+
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
index 7459c4b..c7cef05 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
@@ -21,6 +21,7 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
@@ -46,6 +47,7 @@
 public class BubbleExtractorTest extends UiServiceTestCase {
 
     @Mock RankingConfig mConfig;
+    BubbleExtractor mBubbleExtractor;
 
     private String mPkg = "com.android.server.notification";
     private int mId = 1001;
@@ -57,6 +59,10 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mBubbleExtractor = new BubbleExtractor();
+        mBubbleExtractor.initialize(mContext, mock(NotificationUsageStats.class));
+        mBubbleExtractor.setConfig(mConfig);
+        mBubbleExtractor.setShortcutHelper(mock(ShortcutHelper.class));
     }
 
     private NotificationRecord getNotificationRecord(boolean allow, int importanceHigh) {
@@ -83,70 +89,55 @@
 
     @Test
     public void testAppYesChannelNo() {
-        BubbleExtractor extractor = new BubbleExtractor();
-        extractor.setConfig(mConfig);
-
         when(mConfig.bubblesEnabled()).thenReturn(true);
         when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
         NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
 
-        extractor.process(r);
+        mBubbleExtractor.process(r);
 
         assertFalse(r.canBubble());
     }
 
     @Test
     public void testAppNoChannelYes() throws Exception {
-        BubbleExtractor extractor = new BubbleExtractor();
-        extractor.setConfig(mConfig);
-
         when(mConfig.bubblesEnabled()).thenReturn(true);
         when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);
         NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
 
-        extractor.process(r);
+        mBubbleExtractor.process(r);
 
         assertFalse(r.canBubble());
     }
 
     @Test
     public void testAppYesChannelYes() {
-        BubbleExtractor extractor = new BubbleExtractor();
-        extractor.setConfig(mConfig);
-
         when(mConfig.bubblesEnabled()).thenReturn(true);
         when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
         NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
 
-        extractor.process(r);
+        mBubbleExtractor.process(r);
 
         assertTrue(r.canBubble());
     }
 
     @Test
     public void testAppNoChannelNo() {
-        BubbleExtractor extractor = new BubbleExtractor();
-        extractor.setConfig(mConfig);
-
         when(mConfig.bubblesEnabled()).thenReturn(true);
         when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(false);
         NotificationRecord r = getNotificationRecord(false, IMPORTANCE_UNSPECIFIED);
 
-        extractor.process(r);
+        mBubbleExtractor.process(r);
 
         assertFalse(r.canBubble());
     }
 
     @Test
     public void testAppYesChannelYesUserNo() {
-        BubbleExtractor extractor = new BubbleExtractor();
-        extractor.setConfig(mConfig);
-
         when(mConfig.bubblesEnabled()).thenReturn(false);
         when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
         NotificationRecord r = getNotificationRecord(true, IMPORTANCE_HIGH);
 
-        extractor.process(r);
+        mBubbleExtractor.process(r);
 
         assertFalse(r.canBubble());
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index dc8d010..8e78047 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -35,7 +35,6 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.PendingIntent;
-import android.app.Person;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -191,7 +190,8 @@
                 tweak.canBubble(),
                 tweak.visuallyInterruptive(),
                 tweak.isConversation(),
-                tweak.getShortcutInfo()
+                tweak.getShortcutInfo(),
+                tweak.isBubble()
         );
         assertNotEquals(nru, nru2);
     }
@@ -270,7 +270,8 @@
                     canBubble(i),
                     visuallyInterruptive(i),
                     isConversation(i),
-                    getShortcutInfo(i)
+                    getShortcutInfo(i),
+                    isBubble(i)
             );
             rankings[i] = ranking;
         }
@@ -394,6 +395,10 @@
         return si;
     }
 
+    private boolean isBubble(int index) {
+        return index % 4 == 0;
+    }
+
     private void assertActionsEqual(
             List<Notification.Action> expecteds, List<Notification.Action> actuals) {
         assertEquals(expecteds.size(), actuals.size());
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 64d481a..f179840 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -41,8 +41,6 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -204,6 +202,8 @@
     private TestableNotificationManagerService mService;
     private INotificationManager mBinderService;
     private NotificationManagerInternal mInternalService;
+    private TestableBubbleChecker mTestableBubbleChecker;
+    private ShortcutHelper mShortcutHelper;
     @Mock
     private IPackageManager mPackageManager;
     @Mock
@@ -286,6 +286,10 @@
             super(context, logger, notificationInstanceIdSequence);
         }
 
+        RankingHelper getRankingHelper() {
+            return mRankingHelper;
+        }
+
         @Override
         protected boolean isCallingUidSystem() {
             countSystemChecks++;
@@ -337,6 +341,14 @@
         interface NotificationAssistantAccessGrantedCallback {
             void onGranted(ComponentName assistant, int userId, boolean granted);
         }
+    }
+
+    private class TestableBubbleChecker extends BubbleExtractor.BubbleChecker {
+
+        TestableBubbleChecker(Context context, ShortcutHelper helper, RankingConfig config,
+                ActivityManager manager) {
+            super(context, helper, config, manager);
+        }
 
         @Override
         protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
@@ -448,7 +460,16 @@
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         mService.setAudioManager(mAudioManager);
-        mService.setLauncherApps(mLauncherApps);
+
+        mShortcutHelper = mService.getShortcutHelper();
+        mShortcutHelper.setLauncherApps(mLauncherApps);
+
+        // Set the testable bubble extractor
+        RankingHelper rankingHelper = mService.getRankingHelper();
+        BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class);
+        mTestableBubbleChecker = new TestableBubbleChecker(mContext, mShortcutHelper,
+                mService.mPreferencesHelper, mActivityManager);
+        extractor.setBubbleChecker(mTestableBubbleChecker);
 
         // Tests call directly into the Binder.
         mBinderService = mService.getBinderService();
@@ -462,6 +483,15 @@
     }
 
     @After
+    public void assertNotificationRecordLoggerCallsValid() {
+        for (NotificationRecordLoggerFake.CallRecord call : mNotificationRecordLogger.getCalls()) {
+            if (call.wasLogged) {
+                assertNotNull(call.event);
+            }
+        }
+    }
+
+    @After
     public void tearDown() throws Exception {
         if (mFile != null) mFile.delete();
         clearDeviceConfig();
@@ -1150,10 +1180,10 @@
         mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
-        assertEquals(1, mNotificationRecordLogger.getCalls().size());
+        assertEquals(1, mNotificationRecordLogger.numCalls());
 
         NotificationRecordLoggerFake.CallRecord call = mNotificationRecordLogger.get(0);
-        assertTrue(call.shouldLogReported);
+        assertTrue(call.wasLogged);
         assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
                 call.event);
         assertNotNull(call.r);
@@ -1179,18 +1209,18 @@
                 .setCategory(Notification.CATEGORY_ALARM).build();
         mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, update, 0);
         waitForIdle();
-        assertEquals(2, mNotificationRecordLogger.getCalls().size());
+        assertEquals(2, mNotificationRecordLogger.numCalls());
 
-        assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+        assertTrue(mNotificationRecordLogger.get(0).wasLogged);
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
-                mNotificationRecordLogger.get(0).event);
+                mNotificationRecordLogger.event(0));
         assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
 
-        assertTrue(mNotificationRecordLogger.get(1).shouldLogReported);
+        assertTrue(mNotificationRecordLogger.get(1).wasLogged);
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED,
-                mNotificationRecordLogger.get(1).event);
+                mNotificationRecordLogger.event(1));
         // Instance ID doesn't change on update of an active notification
         assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
     }
@@ -1203,13 +1233,13 @@
         mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0,
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
-        assertEquals(2, mNotificationRecordLogger.getCalls().size());
-        assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+        assertEquals(2, mNotificationRecordLogger.numCalls());
+        assertTrue(mNotificationRecordLogger.get(0).wasLogged);
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
-                mNotificationRecordLogger.get(0).event);
-        assertFalse(mNotificationRecordLogger.get(1).shouldLogReported);
-        assertNull(mNotificationRecordLogger.get(1).event);
+                mNotificationRecordLogger.event(0));
+        assertFalse(mNotificationRecordLogger.get(1).wasLogged);
+        assertNull(mNotificationRecordLogger.event(1));
     }
 
     @Test
@@ -1222,11 +1252,11 @@
         notif.extras.putString(Notification.EXTRA_TITLE, "Changed title");
         mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notif, 0);
         waitForIdle();
-        assertEquals(2, mNotificationRecordLogger.getCalls().size());
+        assertEquals(2, mNotificationRecordLogger.numCalls());
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
-                mNotificationRecordLogger.get(0).event);
-        assertNull(mNotificationRecordLogger.get(1).event);
+                mNotificationRecordLogger.event(0));
+        assertNull(mNotificationRecordLogger.event(1));
     }
 
     @Test
@@ -1241,23 +1271,23 @@
         waitForIdle();
         mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notification, 0);
         waitForIdle();
-        assertEquals(3, mNotificationRecordLogger.getCalls().size());
+        assertEquals(3, mNotificationRecordLogger.numCalls());
 
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
-                mNotificationRecordLogger.get(0).event);
-        assertTrue(mNotificationRecordLogger.get(0).shouldLogReported);
+                mNotificationRecordLogger.event(0));
+        assertTrue(mNotificationRecordLogger.get(0).wasLogged);
         assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
 
         assertEquals(
                 NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL,
-                mNotificationRecordLogger.get(1).event);
+                mNotificationRecordLogger.event(1));
         assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
 
         assertEquals(
                 NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
-                mNotificationRecordLogger.get(2).event);
-        assertTrue(mNotificationRecordLogger.get(2).shouldLogReported);
+                mNotificationRecordLogger.event(2));
+        assertTrue(mNotificationRecordLogger.get(2).wasLogged);
         // New instance ID because notification was canceled before re-post
         assertEquals(2, mNotificationRecordLogger.get(2).getInstanceId());
     }
@@ -1269,7 +1299,7 @@
         waitForIdle();
         // The notification record logger doesn't even get called when a nonexistent notification
         // is cancelled, because that happens very frequently and is not interesting.
-        assertEquals(0, mNotificationRecordLogger.getCalls().size());
+        assertEquals(0, mNotificationRecordLogger.numCalls());
     }
 
     @Test
@@ -2527,6 +2557,13 @@
         // only snooze the one notification
         verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
         assertTrue(nonGrouped.getStats().hasSnoozed());
+
+        assertEquals(2, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+                mNotificationRecordLogger.event(0));
+        assertEquals(
+                NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+                mNotificationRecordLogger.event(1));
     }
 
     @Test
@@ -2569,6 +2606,12 @@
 
         // only snooze the one child
         verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
+
+        assertEquals(2, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+                mNotificationRecordLogger.event(0));
+        assertEquals(NotificationRecordLogger.NotificationCancelledEvent
+                        .NOTIFICATION_CANCEL_SNOOZED, mNotificationRecordLogger.event(1));
     }
 
     @Test
@@ -2588,6 +2631,18 @@
 
         // snooze child and summary
         verify(mSnoozeHelper, times(2)).snooze(any(NotificationRecord.class), anyLong());
+
+        assertEquals(4, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+                mNotificationRecordLogger.event(0));
+        assertEquals(
+                NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+                mNotificationRecordLogger.event(1));
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+                mNotificationRecordLogger.event(2));
+        assertEquals(
+                NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+                mNotificationRecordLogger.event(3));
     }
 
     @Test
@@ -2603,6 +2658,13 @@
 
         // snooze child only
         verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
+
+        assertEquals(2, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SNOOZED,
+                mNotificationRecordLogger.event(0));
+        assertEquals(
+                NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_SNOOZED,
+                mNotificationRecordLogger.event(1));
     }
 
     @Test
@@ -3386,6 +3448,10 @@
         mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
         verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.getSbn()));
+
+        assertEquals(1, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
+                mNotificationRecordLogger.event(0));
     }
 
     @Test
@@ -3404,6 +3470,12 @@
         verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
                 eq((false)));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+
+        assertEquals(2, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DETAIL_OPEN_USER,
+                mNotificationRecordLogger.event(0));
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DETAIL_CLOSE_USER,
+                mNotificationRecordLogger.event(1));
     }
 
     @Test
@@ -3465,11 +3537,11 @@
 
         // Using mService.addNotification() does not generate a NotificationRecordLogger log,
         // so we only get the cancel notification.
-        assertEquals(1, mNotificationRecordLogger.getCalls().size());
+        assertEquals(1, mNotificationRecordLogger.numCalls());
 
         assertEquals(
                 NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD,
-                mNotificationRecordLogger.get(0).event);
+                mNotificationRecordLogger.event(0));
         assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
     }
 
@@ -4351,9 +4423,9 @@
                 {NotificationVisibility.obtain(r.getKey(), 1, 1, true)},
                 new NotificationVisibility[]{});
 
-        assertEquals(1, mNotificationRecordLogger.getCalls().size());
+        assertEquals(1, mNotificationRecordLogger.numCalls());
         assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_OPEN,
-                mNotificationRecordLogger.get(0).event);
+                mNotificationRecordLogger.event(0));
         assertEquals(1, mNotificationRecordLogger.get(0).getInstanceId());
 
         mService.mNotificationDelegate.onNotificationVisibilityChanged(
@@ -4362,9 +4434,9 @@
                         {NotificationVisibility.obtain(r.getKey(), 1, 1, true)}
         );
 
-        assertEquals(2, mNotificationRecordLogger.getCalls().size());
+        assertEquals(2, mNotificationRecordLogger.numCalls());
         assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLOSE,
-                mNotificationRecordLogger.get(1).event);
+                mNotificationRecordLogger.event(1));
         assertEquals(1, mNotificationRecordLogger.get(1).getInstanceId());
     }
 
@@ -4788,6 +4860,12 @@
 
         mService.mNotificationDelegate.onPanelHidden();
         verify(mAssistants, times(1)).onPanelHidden();
+
+        assertEquals(2, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_OPEN,
+                mNotificationRecordLogger.event(0));
+        assertEquals(NotificationRecordLogger.NotificationPanelEvent.NOTIFICATION_PANEL_CLOSE,
+                mNotificationRecordLogger.event(1));
     }
 
     @Test
@@ -4806,6 +4884,9 @@
                 modifiedBeforeSending);
         verify(mAssistants).notifyAssistantSuggestedReplySent(
                 eq(r.getSbn()), eq(reply), eq(generatedByAssistant));
+        assertEquals(1, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
+                mNotificationRecordLogger.event(0));
     }
 
     @Test
@@ -4825,6 +4906,10 @@
                 generatedByAssistant);
         verify(mAssistants).notifyAssistantActionClicked(
                 eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant));
+
+        assertEquals(1, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_ACTION_CLICKED,
+                mNotificationRecordLogger.event(0));
     }
 
     @Test
@@ -5489,6 +5574,10 @@
         StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifs.length);
         assertEquals(1, mService.getNotificationRecordCount());
+
+        assertEquals(1, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationCancelledEvent
+                .NOTIFICATION_CANCEL_LISTENER_CANCEL, mNotificationRecordLogger.event(0));
     }
 
     @Test
@@ -5710,6 +5799,9 @@
 
     @Test
     public void testOnBubbleNotificationSuppressionChanged() throws Exception {
+        // Bubbles are allowed!
+        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+
         // Bubble notification
         NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, "tag");
 
@@ -6111,8 +6203,10 @@
         assertTrue(notif.isBubbleNotification());
 
         // Test: Remove the shortcut
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
         launcherAppsCallback.getValue().onShortcutsChanged(PKG, Collections.emptyList(),
                 new UserHandle(mUid));
+        waitForIdle();
 
         // Verify:
 
@@ -6125,6 +6219,58 @@
         assertFalse(notif2.isBubbleNotification());
     }
 
+
+    @Test
+    public void testNotificationBubbles_shortcut_stopListeningWhenNotifRemoved()
+            throws RemoteException {
+        // Bubbles are allowed!
+        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+
+        ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback =
+                ArgumentCaptor.forClass(LauncherApps.Callback.class);
+
+        // Messaging notification with shortcut info
+        Notification.BubbleMetadata metadata =
+                getBubbleMetadataBuilder().createShortcutBubble("someshortcutId").build();
+        Notification.Builder nb = getMessageStyleNotifBuilder(false /* addDefaultMetadata */,
+                null /* groupKey */, false /* isSummary */);
+        nb.setBubbleMetadata(metadata);
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "tag", mUid, 0, nb.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        // Pretend the shortcut exists
+        List<ShortcutInfo> shortcutInfos = new ArrayList<>();
+        ShortcutInfo info = mock(ShortcutInfo.class);
+        when(info.isLongLived()).thenReturn(true);
+        shortcutInfos.add(info);
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+
+        // Test: Send the bubble notification
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        // Verify:
+
+        // Make sure we register the callback for shortcut changes
+        verify(mLauncherApps, times(1)).registerCallback(launcherAppsCallback.capture(), any());
+
+        // yes allowed, yes messaging w/shortcut, yes bubble
+        Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
+        assertTrue(notif.isBubbleNotification());
+
+        // Test: Remove the notification
+        mBinderService.cancelNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        // Verify:
+
+        // Make sure callback is unregistered
+        verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
+    }
+
     @Test
     public void testNotificationBubbles_bubbleChildrenStay_whenGroupSummaryDismissed()
             throws Exception {
@@ -6168,6 +6314,17 @@
         // The bubble should still exist
         StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsAfter.length);
+
+        // Check we got the click log and associated dismissal logs
+        assertEquals(6, mNotificationRecordLogger.numCalls());
+        // Skip the notification-creation logs
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLICKED,
+                mNotificationRecordLogger.event(3));
+        assertEquals(NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_CLICK,
+                mNotificationRecordLogger.event(4));
+        assertEquals(NotificationRecordLogger.NotificationCancelledEvent
+                        .NOTIFICATION_CANCEL_GROUP_SUMMARY_CANCELED,
+                mNotificationRecordLogger.event(5));
     }
 
     @Test
@@ -6188,6 +6345,11 @@
         // THEN the bubble should still exist
         StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsAfter.length);
+
+        // Check we got the click log
+        assertEquals(1, mNotificationRecordLogger.numCalls());
+        assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_CLICKED,
+                mNotificationRecordLogger.event(0));
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
index 2a17bae..6b18cc6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
@@ -31,25 +31,29 @@
         // The following fields are only relevant to maybeLogNotificationPosted() calls.
         static final int INVALID = -1;
         public int position = INVALID, buzzBeepBlink = INVALID;
-        public boolean shouldLogReported;
+        public boolean wasLogged;
 
         CallRecord(NotificationRecord r, NotificationRecord old, int position,
                 int buzzBeepBlink) {
             super(r, old);
             this.position = position;
             this.buzzBeepBlink = buzzBeepBlink;
-            shouldLogReported = shouldLogReported(buzzBeepBlink);
-            event = shouldLogReported ? NotificationReportedEvent.fromRecordPair(this) : null;
+            wasLogged = shouldLogReported(buzzBeepBlink);
+            event = wasLogged ? NotificationReportedEvent.fromRecordPair(this) : null;
         }
 
         CallRecord(NotificationRecord r, UiEventLogger.UiEventEnum event) {
             super(r, null);
-            shouldLogReported = false;
+            wasLogged = true;
             this.event = event;
         }
     }
     private List<CallRecord> mCalls = new ArrayList<>();
 
+    public int numCalls() {
+        return mCalls.size();
+    }
+
     List<CallRecord> getCalls() {
         return mCalls;
     }
@@ -57,6 +61,9 @@
     CallRecord get(int index) {
         return mCalls.get(index);
     }
+    UiEventLogger.UiEventEnum event(int index) {
+        return mCalls.get(index).event;
+    }
 
     @Override
     public void maybeLogNotificationPosted(NotificationRecord r, NotificationRecord old,
@@ -65,13 +72,12 @@
     }
 
     @Override
-    public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
-        mCalls.add(new CallRecord(r,
-                NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface)));
+    public void log(UiEventLogger.UiEventEnum event, NotificationRecord r) {
+        mCalls.add(new CallRecord(r, event));
     }
 
     @Override
-    public void logNotificationVisibility(NotificationRecord r, boolean visible) {
-        mCalls.add(new CallRecord(r, NotificationEvent.fromVisibility(visible)));
+    public void log(UiEventLogger.UiEventEnum event) {
+        mCalls.add(new CallRecord(null, event));
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
new file mode 100644
index 0000000..50fb9b4
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.notification;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableLooper;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper
+public class ShortcutHelperTest extends UiServiceTestCase {
+
+    private static final String SHORTCUT_ID = "shortcut";
+    private static final String PKG = "pkg";
+    private static final String KEY = "key";
+
+    @Mock
+    LauncherApps mLauncherApps;
+    @Mock
+    ShortcutHelper.ShortcutListener mShortcutListener;
+    @Mock
+    NotificationRecord mNr;
+    @Mock
+    Notification mNotif;
+    @Mock
+    StatusBarNotification mSbn;
+    @Mock
+    Notification.BubbleMetadata mBubbleMetadata;
+
+    ShortcutHelper mShortcutHelper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mShortcutHelper = new ShortcutHelper(mLauncherApps, mShortcutListener);
+        when(mNr.getKey()).thenReturn(KEY);
+        when(mNr.getSbn()).thenReturn(mSbn);
+        when(mSbn.getPackageName()).thenReturn(PKG);
+        when(mNr.getNotification()).thenReturn(mNotif);
+        when(mNotif.getBubbleMetadata()).thenReturn(mBubbleMetadata);
+        when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
+    }
+
+    private LauncherApps.Callback addShortcutBubbleAndVerifyListener() {
+        when(mNotif.isBubbleNotification()).thenReturn(true);
+
+        mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+                false /* removed */,
+                null /* handler */);
+
+        ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback =
+                ArgumentCaptor.forClass(LauncherApps.Callback.class);
+
+        verify(mLauncherApps, times(1)).registerCallback(
+                launcherAppsCallback.capture(), any());
+        return launcherAppsCallback.getValue();
+    }
+
+    @Test
+    public void testBubbleAdded_listenedAdded() {
+        addShortcutBubbleAndVerifyListener();
+    }
+
+    @Test
+    public void testBubbleRemoved_listenerRemoved() {
+        // First set it up to listen
+        addShortcutBubbleAndVerifyListener();
+
+        // Then remove the notif
+        mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+                true /* removed */,
+                null /* handler */);
+
+        verify(mLauncherApps, times(1)).unregisterCallback(any());
+    }
+
+    @Test
+    public void testBubbleNoLongerBubble_listenerRemoved() {
+        // First set it up to listen
+        addShortcutBubbleAndVerifyListener();
+
+        // Then make it not a bubble
+        when(mNotif.isBubbleNotification()).thenReturn(false);
+        mShortcutHelper.maybeListenForShortcutChangesForBubbles(mNr,
+                false /* removed */,
+                null /* handler */);
+
+        verify(mLauncherApps, times(1)).unregisterCallback(any());
+    }
+
+    @Test
+    public void testListenerNotifiedOnShortcutRemoved() {
+        LauncherApps.Callback callback = addShortcutBubbleAndVerifyListener();
+
+        List<ShortcutInfo> shortcutInfos = new ArrayList<>();
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
+
+        callback.onShortcutsChanged(PKG, shortcutInfos, mock(UserHandle.class));
+        verify(mShortcutListener).onShortcutRemoved(mNr.getKey());
+    }
+}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index cdba9a1..6cb8b86 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -20,6 +20,7 @@
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
         "servicestests-utils",
+        "testng",
         "truth-prebuilt",
         "testables",
         "ub-uiautomator",
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index e2c27ea..7928e76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -20,6 +20,9 @@
 import static android.view.Gravity.LEFT;
 import static android.view.Gravity.RIGHT;
 import static android.view.Gravity.TOP;
+import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.ITYPE_TOP_GESTURES;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
@@ -42,13 +45,17 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.spy;
+import static org.testng.Assert.expectThrows;
 
 import android.app.WindowConfiguration;
 import android.graphics.Insets;
@@ -154,6 +161,56 @@
     }
 
     @Test
+    public void addingWindow_withInsetsTypes() {
+        WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+        win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
+        win.getFrameLw().set(0, 0, 500, 100);
+
+        addWindow(win);
+        InsetsStateController controller = mDisplayContent.getInsetsStateController();
+        controller.onPostLayout();
+
+        InsetsSourceProvider statusBarProvider = controller.getSourceProvider(ITYPE_STATUS_BAR);
+        assertEquals(new Rect(0, 0, 500, 100), statusBarProvider.getSource().getFrame());
+        assertEquals(Insets.of(0, 100, 0, 0),
+                statusBarProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+                        false /* ignoreVisibility */));
+
+        InsetsSourceProvider topGesturesProvider = controller.getSourceProvider(ITYPE_TOP_GESTURES);
+        assertEquals(new Rect(0, 0, 500, 100), topGesturesProvider.getSource().getFrame());
+        assertEquals(Insets.of(0, 100, 0, 0),
+                topGesturesProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+                        false /* ignoreVisibility */));
+
+        InsetsSourceProvider navigationBarProvider = controller.getSourceProvider(
+                ITYPE_NAVIGATION_BAR);
+        assertNotEquals(new Rect(0, 0, 500, 100), navigationBarProvider.getSource().getFrame());
+    }
+
+    @Test
+    public void addingWindow_ignoresInsetsTypes_InWindowTypeWithPredefinedInsets() {
+        mDisplayPolicy.removeWindowLw(mStatusBarWindow);  // Removes the existing one.
+        WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar");
+        win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
+        win.getFrameLw().set(0, 0, 500, 100);
+
+        addWindow(win);
+        mDisplayContent.getInsetsStateController().onPostLayout();
+
+        InsetsSourceProvider provider =
+                mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR);
+        assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
+    }
+
+    @Test
+    public void addingWindow_throwsException_WithMultipleInsetTypes() {
+        WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+        win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
+
+        expectThrows(IllegalArgumentException.class, () -> addWindow(win));
+    }
+
+    @Test
     public void layoutWindowLw_fitStatusBars() {
         assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 277bc41..67b1dac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -27,6 +27,8 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -109,5 +111,27 @@
         assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
                 mWm.getDefaultDisplayContentLocked().getWindowingMode());
     }
+
+    /**
+     * This test ensures that an existing single instance activity with alias name can be found by
+     * the same activity info. So {@link ActivityStarter#getReusableTask} won't miss it that leads
+     * to create an unexpected new instance.
+     */
+    @Test
+    public void testFindActivityByTargetComponent() {
+        final ComponentName aliasComponent = ComponentName.createRelative(
+                ActivityTestsBase.DEFAULT_COMPONENT_PACKAGE_NAME, ".AliasActivity");
+        final ComponentName targetComponent = ComponentName.createRelative(
+                aliasComponent.getPackageName(), ".TargetActivity");
+        final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+                .setComponent(aliasComponent)
+                .setTargetActivity(targetComponent.getClassName())
+                .setLaunchMode(ActivityInfo.LAUNCH_SINGLE_INSTANCE)
+                .setCreateTask(true)
+                .build();
+
+        assertEquals(activity, mWm.mRoot.findActivity(activity.intent, activity.info,
+                false /* compareIntentFilters */));
+    }
 }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index ba4a82f..edf81ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -107,10 +107,16 @@
         setUpApp(display);
 
         // Put app window into freeform and then make it a compat app.
-        mTask.setBounds(100, 100, 400, 600);
+        final Rect bounds = new Rect(100, 100, 400, 600);
+        mTask.setBounds(bounds);
         prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+        assertEquals(bounds, mActivity.getBounds());
 
-        final Rect bounds = new Rect(mActivity.getBounds());
+        // The activity should be able to accept negative x position [-150, 100 - 150, 600].
+        final int dx = bounds.left + bounds.width() / 2;
+        mTask.setBounds(bounds.left - dx, bounds.top, bounds.right - dx, bounds.bottom);
+        assertEquals(mTask.getBounds(), mActivity.getBounds());
+
         final int density = mActivity.getConfiguration().densityDpi;
 
         // change display configuration to fullscreen
@@ -231,11 +237,7 @@
         // The position should be horizontal centered.
         assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
 
-        final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
-                mService.mWindowManager, mock(Session.class), new TestIWindow(),
-                new WindowManager.LayoutParams(), mActivity);
-        mActivity.addWindow(w);
-        mActivity.mDisplayContent.mInputMethodTarget = w;
+        mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity);
         // Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
         assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
     }
@@ -475,6 +477,25 @@
         assertEquals((int) (dw * maxAspect), mActivity.getBounds().width());
         // The bounds should be horizontal centered: (2500-1900)/2=350.
         assertEquals((dh - mActivity.getBounds().width()) / 2, mActivity.getBounds().left);
+
+        // The letterbox needs a main window to layout.
+        addWindowToActivity(mActivity);
+        // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
+        mActivity.mRootWindowContainer.performSurfacePlacement(false /* recoveringMemory */);
+        // The letterbox insets should be [350, 0 - 350, 0].
+        assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
+                mActivity.getLetterboxInsets());
+    }
+
+    private WindowState addWindowToActivity(ActivityRecord activity) {
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+        params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+        final WindowTestUtils.TestWindowState w = new WindowTestUtils.TestWindowState(
+                mService.mWindowManager, mock(Session.class), new TestIWindow(), params, mActivity);
+        WindowTestsBase.makeWindowVisible(w);
+        w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+        mActivity.addWindow(w);
+        return w;
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 560d03f..091f493 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -322,6 +322,8 @@
     }
 
     private void tearDown() {
+        mWmService.mRoot.forAllDisplayPolicies(DisplayPolicy::release);
+
         // Unregister display listener from root to avoid issues with subsequent tests.
         mContext.getSystemService(DisplayManager.class)
                 .unregisterDisplayListener(mAtmService.mRootWindowContainer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 5007e3a..8019e9d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -278,7 +278,7 @@
     }
 
     @Test
-    public void testContainerChanges() {
+    public void testContainerFocusableChanges() {
         removeGlobalMinSizeRestriction();
         final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
                 .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
@@ -288,6 +288,24 @@
         t.setFocusable(stack.mRemoteToken, false);
         mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
         assertFalse(task.isFocusable());
+        t.setFocusable(stack.mRemoteToken, true);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        assertTrue(task.isFocusable());
+    }
+
+    @Test
+    public void testContainerHiddenChanges() {
+        removeGlobalMinSizeRestriction();
+        final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        WindowContainerTransaction t = new WindowContainerTransaction();
+        assertTrue(stack.shouldBeVisible(null));
+        t.setHidden(stack.mRemoteToken, true);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        assertFalse(stack.shouldBeVisible(null));
+        t.setHidden(stack.mRemoteToken, false);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+        assertTrue(stack.shouldBeVisible(null));
     }
 
     @Test
@@ -551,6 +569,7 @@
         final Task task = createTaskInStack(stackController1, 0 /* userId */);
         final ITaskOrganizer organizer = registerMockOrganizer();
         final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+        makeWindowVisible(w);
 
         BLASTSyncEngine bse = new BLASTSyncEngine();
 
@@ -569,6 +588,28 @@
     }
 
     @Test
+    public void testBLASTCallbackWithInvisibleWindow() {
+        final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final ITaskOrganizer organizer = registerMockOrganizer();
+        final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+
+        BLASTSyncEngine bse = new BLASTSyncEngine();
+
+        BLASTSyncEngine.TransactionReadyListener transactionListener =
+            mock(BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(transactionListener);
+        bse.addToSyncSet(id, task);
+        bse.setReady(id);
+
+        // Since the window was invisible, the Task had no visible leaves and the sync should
+        // complete as soon as we call setReady.
+        verify(transactionListener)
+            .transactionReady(anyInt(), any());
+    }
+
+    @Test
     public void testBLASTCallbackWithChildWindow() {
         final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stackController1, 0 /* userId */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
index 2fb0b4c..8f913dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -81,9 +81,9 @@
         assertEquals(TEST_INSETS, snapshot.getContentInsets());
         assertNotNull(snapshot.getSnapshot());
         assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
+        assertNull(mLoader.loadTask(1, mTestUserId, true /* isLowResolution */));
     }
 
-
     @Test
     public void testRemoveObsoleteFiles() {
         mPersister.persistSnapshot(1, mTestUserId, createSnapshot());
@@ -132,10 +132,18 @@
         assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 false /* restoreFromDisk */, false /* isLowResolution */));
 
-        // Load it from disk
+        // Attempt to load the low-res snapshot from the disk
         assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 true /* restoreFromDisk */, true /* isLowResolution */));
 
+        // Load the high-res (default) snapshot from disk
+        assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
+                true /* restoreFromDisk */, false /* isLowResolution */));
+
+        // Make sure it's not in the cache now.
+        assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
+                false /* restoreFromDisk */, true /* isLowResolution */));
+
         // Make sure it's not in the cache now.
         assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId,
                 false /* restoreFromDisk */, false /* isLowResolution */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 9a9abba..bfee894 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -293,35 +293,6 @@
     }
 
     @Test
-    public void testDisabledLowResolutionPersistAndLoadSnapshot() {
-        mPersister.setEnableLowResSnapshots(false);
-
-        TaskSnapshot a = new TaskSnapshotBuilder()
-                .setScaleFraction(0.5f)
-                .setIsLowResolution(true)
-                .build();
-        assertTrue(a.isLowResolution());
-        mPersister.persistSnapshot(1, mTestUserId, a);
-        mPersister.waitForQueueEmpty();
-        final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"),
-                new File(FILES_DIR.getPath() + "/snapshots/1.jpg")};
-        final File[] nonExistsFiles = new File[]{
-                new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"),
-        };
-        assertTrueForFiles(files, File::exists, " must exist");
-        assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist");
-        final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */);
-        assertNotNull(snapshot);
-        assertEquals(TEST_INSETS, snapshot.getContentInsets());
-        assertNotNull(snapshot.getSnapshot());
-        assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation());
-
-        final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId,
-                true /* isLowResolution */);
-        assertNull(snapshotNotExist);
-    }
-
-    @Test
     public void testIsRealSnapshotPersistAndLoadSnapshot() {
         TaskSnapshot a = new TaskSnapshotBuilder()
                 .setIsRealSnapshot(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 6d78658..88de34d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -127,7 +127,6 @@
         private static final int SNAPSHOT_HEIGHT = 100;
 
         private float mScaleFraction = 1f;
-        private boolean mIsLowResolution = false;
         private boolean mIsRealSnapshot = true;
         private boolean mIsTranslucent = false;
         private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
@@ -142,11 +141,6 @@
             return this;
         }
 
-        TaskSnapshotBuilder setIsLowResolution(boolean isLowResolution) {
-            mIsLowResolution = isLowResolution;
-            return this;
-        }
-
         TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) {
             mIsRealSnapshot = isRealSnapshot;
             return this;
@@ -186,8 +180,11 @@
             return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer,
                     ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
                     mRotation, taskSize, TEST_INSETS,
-                    mIsLowResolution, mIsRealSnapshot,
-                    mWindowingMode, mSystemUiVisibility, mIsTranslucent);
+                    // When building a TaskSnapshot with the Builder class, isLowResolution
+                    // is always false. Low-res snapshots are only created when loading from
+                    // disk.
+                    false /* isLowResolution */,
+                    mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent);
         }
     }
 }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 8e85bb2..747c8d9 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -445,7 +445,6 @@
     abstract static class UsbHandler extends Handler {
 
         // current USB state
-        private boolean mConnected;
         private boolean mHostConnected;
         private boolean mSourcePower;
         private boolean mSinkPower;
@@ -473,6 +472,7 @@
         private final UsbPermissionManager mPermissionManager;
         private NotificationManager mNotificationManager;
 
+        protected boolean mConnected;
         protected long mScreenUnlockedFunctions;
         protected boolean mBootCompleted;
         protected boolean mCurrentFunctionsApplied;
@@ -1794,7 +1794,8 @@
                 case MSG_SET_FUNCTIONS_TIMEOUT:
                     Slog.e(TAG, "Set functions timed out! no reply from usb hal");
                     if (msg.arg1 != 1) {
-                        setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
+                        // Set this since default function may be selected from Developer options
+                        setEnabledFunctions(mScreenUnlockedFunctions, false);
                     }
                     break;
                 case MSG_GET_CURRENT_USB_FUNCTIONS:
@@ -1816,7 +1817,8 @@
                      * Dont force to default when the configuration is already set to default.
                      */
                     if (msg.arg1 != 1) {
-                        setEnabledFunctions(UsbManager.FUNCTION_NONE, !isAdbEnabled());
+                        // Set this since default function may be selected from Developer options
+                        setEnabledFunctions(mScreenUnlockedFunctions, false);
                     }
                     break;
                 case MSG_GADGET_HAL_REGISTERED:
@@ -1936,8 +1938,11 @@
                             SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS);
                     sendMessageDelayed(MSG_SET_FUNCTIONS_TIMEOUT, chargingFunctions,
                             SET_FUNCTIONS_TIMEOUT_MS);
-                    sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
-                            SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+                    if (mConnected) {
+                        // Only queue timeout of enumeration when the USB is connected
+                        sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
+                                SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+                    }
                     if (DEBUG) Slog.d(TAG, "timeout message queued");
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Remoteexception while calling setCurrentUsbFunctions", e);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 767010a..3196758 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE;
 import static android.content.Context.BIND_AUTO_CREATE;
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
 import static android.content.pm.PackageManager.GET_META_DATA;
 import static android.content.pm.PackageManager.GET_SERVICES;
 import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
@@ -1133,7 +1134,8 @@
                 }
 
                 mIsBound = mContext.bindServiceAsUser(i, this,
-                        BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE, mUser);
+                        BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES,
+                        mUser);
 
                 if (mIsBound) {
                     mRemoteServiceWakeLock.acquire();
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index d9ae48f..b3d7c0d 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -30,6 +30,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.util.ArrayUtils;
 
@@ -162,7 +163,7 @@
             for (ApplicationInfo ai : candidates) {
                 String packageName = ai.packageName;
                 String[] restrictedCarrierApps = Resources.getSystem().getStringArray(
-                        android.R.array.config_restrictedPreinstalledCarrierApps);
+                        R.array.config_restrictedPreinstalledCarrierApps);
                 boolean hasPrivileges = telephonyManager != null
                         && telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
                                 == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
diff --git a/telephony/common/com/android/internal/telephony/GsmAlphabet.java b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
index c62cec2..5c53f7e 100644
--- a/telephony/common/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/common/com/android/internal/telephony/GsmAlphabet.java
@@ -19,10 +19,12 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.os.Build;
-import android.text.TextUtils;
 import android.util.Log;
+import android.text.TextUtils;
 import android.util.SparseIntArray;
 
+import com.android.internal.R;
+
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
@@ -1087,10 +1089,8 @@
     private static void enableCountrySpecificEncodings() {
         Resources r = Resources.getSystem();
         // See comments in frameworks/base/core/res/res/values/config.xml for allowed values
-        sEnabledSingleShiftTables = r.getIntArray(
-                android.R.array.config_sms_enabled_single_shift_tables);
-        sEnabledLockingShiftTables = r.getIntArray(
-                android.R.array.config_sms_enabled_locking_shift_tables);
+        sEnabledSingleShiftTables = r.getIntArray(R.array.config_sms_enabled_single_shift_tables);
+        sEnabledLockingShiftTables = r.getIntArray(R.array.config_sms_enabled_locking_shift_tables);
 
         if (sEnabledSingleShiftTables.length > 0) {
             sHighestEnabledSingleShiftCode =
diff --git a/telephony/common/com/google/android/mms/util/SqliteWrapper.java b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
index 4871434..31fe4d7 100644
--- a/telephony/common/com/google/android/mms/util/SqliteWrapper.java
+++ b/telephony/common/com/google/android/mms/util/SqliteWrapper.java
@@ -60,7 +60,8 @@
     @UnsupportedAppUsage
     public static void checkSQLiteException(Context context, SQLiteException e) {
         if (isLowMemory(e)) {
-            Toast.makeText(context, android.R.string.low_memory, Toast.LENGTH_SHORT).show();
+            Toast.makeText(context, com.android.internal.R.string.low_memory,
+                    Toast.LENGTH_SHORT).show();
         } else {
             throw e;
         }
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index dcd4eb5..28cc7d0 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -598,48 +598,6 @@
     }
 
     /**
-     * Call forwarding function status
-     */
-    @IntDef(prefix = { "STATUS_" }, value = {
-        CallForwardingInfo.STATUS_ACTIVE,
-        CallForwardingInfo.STATUS_INACTIVE,
-        CallForwardingInfo.STATUS_UNKNOWN_ERROR,
-        CallForwardingInfo.STATUS_NOT_SUPPORTED,
-        CallForwardingInfo.STATUS_FDN_CHECK_FAILURE
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CallForwardingStatus {
-    }
-
-    /**
-     * Call forwarding reason types
-     */
-    @IntDef(flag = true, prefix = { "REASON_" }, value = {
-        CallForwardingInfo.REASON_UNCONDITIONAL,
-        CallForwardingInfo.REASON_BUSY,
-        CallForwardingInfo.REASON_NO_REPLY,
-        CallForwardingInfo.REASON_NOT_REACHABLE,
-        CallForwardingInfo.REASON_ALL,
-        CallForwardingInfo.REASON_ALL_CONDITIONAL
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CallForwardingReason {
-    }
-
-    /**
-     * Call waiting function status
-     */
-    @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
-        TelephonyManager.CALL_WAITING_STATUS_ACTIVE,
-        TelephonyManager.CALL_WAITING_STATUS_INACTIVE,
-        TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED,
-        TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface CallWaitingStatus {
-    }
-
-    /**
      * UICC SIM Application Types
      */
     @IntDef(prefix = { "APPTYPE_" }, value = {
diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java
index 1dd7539..7e777fa 100644
--- a/telephony/java/android/telephony/CallForwardingInfo.java
+++ b/telephony/java/android/telephony/CallForwardingInfo.java
@@ -15,24 +15,24 @@
  */
 
 package android.telephony;
+
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Annotation.CallForwardingReason;
-import android.telephony.Annotation.CallForwardingStatus;
 
 import com.android.telephony.Rlog;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
  * Defines the call forwarding information.
  * @hide
  */
-@SystemApi
 public final class CallForwardingInfo implements Parcelable {
     private static final String TAG = "CallForwardingInfo";
 
@@ -41,7 +41,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_INACTIVE = 0;
 
     /**
@@ -49,7 +48,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_ACTIVE = 1;
 
     /**
@@ -58,7 +56,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_FDN_CHECK_FAILURE = 2;
 
     /**
@@ -66,7 +63,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_UNKNOWN_ERROR = 3;
 
     /**
@@ -74,7 +70,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int STATUS_NOT_SUPPORTED = 4;
 
     /**
@@ -83,7 +78,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_UNCONDITIONAL = 0;
 
     /**
@@ -92,7 +86,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_BUSY = 1;
 
     /**
@@ -101,7 +94,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_NO_REPLY = 2;
 
     /**
@@ -110,7 +102,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_NOT_REACHABLE = 3;
 
     /**
@@ -120,7 +111,6 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_ALL = 4;
 
     /**
@@ -130,20 +120,48 @@
      *            and conditions +CCFC
      * @hide
      */
-    @SystemApi
     public static final int REASON_ALL_CONDITIONAL = 5;
 
     /**
+     * Call forwarding function status
+     */
+    @IntDef(prefix = { "STATUS_" }, value = {
+        STATUS_ACTIVE,
+        STATUS_INACTIVE,
+        STATUS_UNKNOWN_ERROR,
+        STATUS_NOT_SUPPORTED,
+        STATUS_FDN_CHECK_FAILURE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallForwardingStatus {
+    }
+
+    /**
+     * Call forwarding reason types
+     */
+    @IntDef(flag = true, prefix = { "REASON_" }, value = {
+        REASON_UNCONDITIONAL,
+        REASON_BUSY,
+        REASON_NO_REPLY,
+        REASON_NOT_REACHABLE,
+        REASON_ALL,
+        REASON_ALL_CONDITIONAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallForwardingReason {
+    }
+
+    /**
      * The call forwarding status.
      */
-    private @CallForwardingStatus int mStatus;
+    private int mStatus;
 
     /**
      * The call forwarding reason indicates the condition under which calls will be forwarded.
      * Reference: 3GPP TS 27.007 version 10.3.0 Release 10 - 7.11 Call forwarding number
      *            and conditions +CCFC
      */
-    private @CallForwardingReason int mReason;
+    private int mReason;
 
     /**
      * The phone number to which calls will be forwarded.
@@ -166,7 +184,6 @@
      * @param timeSeconds the timeout (in seconds) before the forwarding is attempted
      * @hide
      */
-    @SystemApi
     public CallForwardingInfo(@CallForwardingStatus int status, @CallForwardingReason int reason,
             @Nullable String number, int timeSeconds) {
         mStatus = status;
@@ -182,7 +199,6 @@
      *
      * @hide
      */
-    @SystemApi
     public @CallForwardingStatus int getStatus() {
         return mStatus;
     }
@@ -196,7 +212,6 @@
      *
      * @hide
      */
-    @SystemApi
     public @CallForwardingReason int getReason() {
         return mReason;
     }
@@ -209,7 +224,6 @@
      *
      * @hide
      */
-    @SystemApi
     @Nullable
     public String getNumber() {
         return mNumber;
@@ -227,7 +241,6 @@
      *
      * @hide
      */
-    @SystemApi
     @SuppressLint("MethodNameUnits")
     public int getTimeoutSeconds() {
         return mTimeSeconds;
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 34bac5d..3984bd7 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -19,9 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.Context;
 import android.telephony.SubscriptionManager;
 
@@ -44,8 +42,6 @@
      * issues.
      * @hide
      */
-    @SystemApi
-    @TestApi
     // Moved from TelephonyIntents, need to keep backwards compatibility with OEM apps that have
     // this value hard-coded in BroadcastReceiver.
     @SuppressLint("ActionValue")
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 1a79bf7..4940cb2 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -297,7 +297,7 @@
         mDataSpecificInfo = new DataSpecificRegistrationInfo(
                 maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo,
                 isUsingCarrierAggregation);
-        updateNrState(mDataSpecificInfo);
+        updateNrState();
     }
 
     private NetworkRegistrationInfo(Parcel source) {
@@ -686,12 +686,12 @@
      * DCNR is not restricted and NR is supported by the selected PLMN. Otherwise the use of 5G
      * NR is restricted.
      *
-     * @param state data specific registration state contains the 5G NR indicators.
+     * @hide
      */
-    private void updateNrState(DataSpecificRegistrationInfo state) {
+    public void updateNrState() {
         mNrState = NR_STATE_NONE;
-        if (state.isEnDcAvailable) {
-            if (!state.isDcNrRestricted && state.isNrAvailable) {
+        if (mDataSpecificInfo.isEnDcAvailable) {
+            if (!mDataSpecificInfo.isDcNrRestricted && mDataSpecificInfo.isNrAvailable) {
                 mNrState = NR_STATE_NOT_RESTRICTED;
             } else {
                 mNrState = NR_STATE_RESTRICTED;
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index e37a9b9..c2cfbef 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -95,7 +95,6 @@
      *        if there is no valid APN setting for the specific type, then this will be null
      * @hide
      */
-    @SystemApi
     public PreciseDataConnectionState(@DataState int state,
                                       @NetworkType int networkType,
                                       @ApnType int apnTypes, @NonNull String apn,
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index dd20d06..deba551 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1650,7 +1650,6 @@
      * @return Current data network type
      * @hide
      */
-    @SystemApi
     @TestApi
     public @NetworkType int getDataNetworkType() {
         final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index d3afa4a..6aea5ee 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -975,11 +975,7 @@
      *
      * @param packageName serves as the default package name if the package name that is
      *        associated with the user id is null.
-     *
-     * @hide
      */
-    @SystemApi
-    @TestApi
     public void sendMultipartTextMessage(
             @NonNull String destinationAddress, @Nullable String scAddress,
             @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents,
@@ -2898,7 +2894,7 @@
                         getSubscriptionId(), null);
             }
         } catch (RemoteException ex) {
-            // ignore it
+            throw new RuntimeException(ex);
         }
         return smsc;
     }
@@ -2920,7 +2916,7 @@
      * </p>
      *
      * @param smsc the SMSC address string.
-     * @return true for success, false otherwise.
+     * @return true for success, false otherwise. Failure can be due modem returning an error.
      */
     @SuppressAutoDoc // for carrier privileges and default SMS application.
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -2932,7 +2928,7 @@
                         smsc, getSubscriptionId(), null);
             }
         } catch (RemoteException ex) {
-            // ignore it
+            throw new RuntimeException(ex);
         }
         return false;
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 610ec5e..5339fb9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -70,13 +70,12 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.Annotation.ApnType;
-import android.telephony.Annotation.CallForwardingReason;
 import android.telephony.Annotation.CallState;
-import android.telephony.Annotation.CallWaitingStatus;
 import android.telephony.Annotation.NetworkType;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SimActivationState;
 import android.telephony.Annotation.UiccAppType;
+import android.telephony.CallForwardingInfo.CallForwardingReason;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
 import android.telephony.data.ApnSetting;
 import android.telephony.data.ApnSetting.MvnoType;
@@ -1565,6 +1564,7 @@
      * @hide
      */
     @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     @SuppressLint("ActionValue")
     public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED =
             "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
@@ -1786,6 +1786,7 @@
      * @hide
      */
     @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     @SuppressLint("ActionValue")
     public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED =
             "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
@@ -5280,8 +5281,8 @@
      *      not present or not loaded
      * @hide
      */
+    @UnsupportedAppUsage
     @Nullable
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public String[] getIsimImpu() {
         try {
@@ -9189,7 +9190,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @CdmaRoamingMode int getCdmaRoamingMode() {
         int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
@@ -9218,7 +9218,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) {
         try {
@@ -9244,19 +9243,16 @@
     /** Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
      * @hide
      */
-    @SystemApi
     public static final int CDMA_SUBSCRIPTION_UNKNOWN  = -1;
 
     /** Used for CDMA subscription mode: RUIM/SIM (default)
      * @hide
      */
-    @SystemApi
     public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
 
     /** Used for CDMA subscription mode: NV -> non-volatile memory
      * @hide
      */
-    @SystemApi
     public static final int CDMA_SUBSCRIPTION_NV       = 1;
 
     /** @hide */
@@ -9275,7 +9271,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) {
         try {
@@ -9983,18 +9978,30 @@
     }
 
     /**
-     * Gets the default Respond Via Message application
-     * @param context context from the calling app
-     * @param updateIfNeeded update the default app if there is no valid default app configured.
+     * Gets the default Respond Via Message application, updating the cache if there is no
+     * respond-via-message application currently configured.
      * @return component name of the app and class to direct Respond Via Message intent to, or
      * {@code null} if the functionality is not supported.
      * @hide
      */
     @SystemApi
     @TestApi
-    public static @Nullable ComponentName getDefaultRespondViaMessageApplication(
-            @NonNull Context context, boolean updateIfNeeded) {
-        return SmsApplication.getDefaultRespondViaMessageApplication(context, updateIfNeeded);
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    public @Nullable ComponentName getAndUpdateDefaultRespondViaMessageApplication() {
+        return SmsApplication.getDefaultRespondViaMessageApplication(mContext, true);
+    }
+
+    /**
+     * Gets the default Respond Via Message application.
+     * @return component name of the app and class to direct Respond Via Message intent to, or
+     * {@code null} if the functionality is not supported.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+    public @Nullable ComponentName getDefaultRespondViaMessageApplication() {
+        return SmsApplication.getDefaultRespondViaMessageApplication(mContext, false);
     }
 
     /**
@@ -11171,6 +11178,8 @@
      * PackageManager.FEATURE_TELEPHONY system feature, which is available
      * on any device with a telephony radio, even if the device is
      * voice-only.
+     *
+     * @hide
      */
     public boolean isDataCapable() {
         if (mContext == null) return true;
@@ -12706,7 +12715,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @NonNull
     public CallForwardingInfo getCallForwarding(@CallForwardingReason int callForwardingReason) {
@@ -12753,7 +12761,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setCallForwarding(@NonNull CallForwardingInfo callForwardingInfo) {
         if (callForwardingInfo == null) {
@@ -12794,7 +12801,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int CALL_WAITING_STATUS_ACTIVE = 1;
 
     /**
@@ -12802,7 +12808,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int CALL_WAITING_STATUS_INACTIVE = 2;
 
     /**
@@ -12810,7 +12815,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3;
 
     /**
@@ -12818,10 +12822,24 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4;
 
     /**
+     * Call waiting function status
+     *
+     * @hide
+     */
+    @IntDef(prefix = { "CALL_WAITING_STATUS_" }, value = {
+        CALL_WAITING_STATUS_ACTIVE,
+        CALL_WAITING_STATUS_INACTIVE,
+        CALL_WAITING_STATUS_NOT_SUPPORTED,
+        CALL_WAITING_STATUS_UNKNOWN_ERROR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallWaitingStatus {
+    }
+
+    /**
      * Gets the status of voice call waiting function. Call waiting function enables the waiting
      * for the incoming call when it reaches the user who is busy to make another call and allows
      * users to decide whether to switch to the incoming call.
@@ -12829,7 +12847,6 @@
      * @return the status of call waiting function.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @CallWaitingStatus int getCallWaitingStatus() {
         try {
@@ -12855,7 +12872,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setCallWaitingStatus(boolean isEnable) {
         try {
@@ -12886,7 +12902,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setDataAllowedDuringVoiceCall(boolean allow) {
         try {
@@ -12915,7 +12930,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isDataAllowedInVoiceCall() {
         try {
@@ -12962,7 +12976,6 @@
      * The IccLock state or password was changed successfully.
      * @hide
      */
-    @SystemApi
     public static final int CHANGE_ICC_LOCK_SUCCESS = Integer.MAX_VALUE;
 
     /**
@@ -12975,7 +12988,6 @@
      *
      * @hide
      */
-    @SystemApi
     @WorkerThread
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isIccLockEnabled() {
@@ -13012,7 +13024,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public int setIccLockEnabled(boolean enabled, @NonNull String password) {
         checkNotNull(password, "setIccLockEnabled password can't be null.");
@@ -13046,7 +13057,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public int changeIccLockPassword(@NonNull String oldPassword, @NonNull String newPassword) {
         checkNotNull(oldPassword, "changeIccLockPassword oldPassword can't be null.");
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 4b5303f..bd531da 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -21,7 +21,6 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SuppressLint;
@@ -124,7 +123,7 @@
          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
          */
         @Override
-        public void onUnregistered(@Nullable ImsReasonInfo info) {
+        public void onUnregistered(@NonNull ImsReasonInfo info) {
         }
 
         /**
@@ -136,7 +135,7 @@
         @Override
         public void onTechnologyChangeFailed(
                 @AccessNetworkConstants.TransportType int imsTransportType,
-                @Nullable ImsReasonInfo info) {
+                @NonNull ImsReasonInfo info) {
         }
     }
 
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 00fa942..1a606b7 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -85,6 +85,7 @@
 
     /**
      * There is no existing configuration for the queried provisioning key.
+     * @hide
      */
     public static final int PROVISIONING_RESULT_UNKNOWN = -1;
 
@@ -120,6 +121,7 @@
      * Value is in String format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0;
 
@@ -143,6 +145,7 @@
      * Value is in String format.
      * @see #setProvisioningStringValue(int, String)
      * @see #getProvisioningStringValue(int)
+     * @hide
      */
     public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1;
 
@@ -154,6 +157,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_SESSION_TIMER_SEC = 2;
 
@@ -165,6 +169,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3;
 
@@ -176,6 +181,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4;
 
@@ -184,6 +190,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5;
 
@@ -192,6 +199,7 @@
      * Value is in boolean format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_ENABLE_SILENT_REDIAL = 6;
 
@@ -207,6 +215,7 @@
      * The value is an integer.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_T1_TIMER_VALUE_MS = 7;
 
@@ -218,6 +227,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_T2_TIMER_VALUE_MS = 8;
 
@@ -229,6 +239,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_TF_TIMER_VALUE_MS = 9;
 
@@ -241,6 +252,7 @@
      * {@link #PROVISIONING_VALUE_DISABLED} to disable VoLTE provisioning.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VOLTE_PROVISIONING_STATUS = 10;
 
@@ -253,6 +265,7 @@
      * {@link #PROVISIONING_VALUE_DISABLED} to disable VT provisioning.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VT_PROVISIONING_STATUS = 11;
 
@@ -261,6 +274,7 @@
      * Value is in String format.
      * @see #setProvisioningStringValue(int, String)
      * @see #getProvisioningStringValue(int)
+     * @hide
      */
     public static final int KEY_REGISTRATION_DOMAIN_NAME = 12;
 
@@ -270,18 +284,21 @@
      * Valid values are {@link #SMS_FORMAT_3GPP} and {@link #SMS_FORMAT_3GPP2}.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SMS_FORMAT = 13;
 
     /**
      * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP2 SMS format is used.
      * See {@link android.telephony.SmsMessage#FORMAT_3GPP2} for more information.
+     * @hide
      */
     public static final int SMS_FORMAT_3GPP2 = 0;
 
     /**
      * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP SMS format is used.
      * See {@link android.telephony.SmsMessage#FORMAT_3GPP} for more information.
+     * @hide
      */
     public static final int SMS_FORMAT_3GPP = 1;
 
@@ -290,6 +307,7 @@
      * Value is in Integer format. ON (1), OFF(0).
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SMS_OVER_IP_ENABLED = 14;
 
@@ -300,6 +318,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15;
 
@@ -310,6 +329,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_PUBLISH_OFFLINE_AVAILABILITY_TIMER_SEC = 16;
 
@@ -322,6 +342,7 @@
      * enabled.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_CAPABILITY_DISCOVERY_ENABLED = 17;
 
@@ -334,6 +355,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18;
 
@@ -345,6 +367,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19;
 
@@ -356,6 +379,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20;
 
@@ -366,6 +390,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21;
 
@@ -376,6 +401,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22;
 
@@ -387,6 +413,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23;
 
@@ -395,6 +422,7 @@
      * Value is in Integer format. Enable (1), Disable(0).
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24;
 
@@ -407,6 +435,7 @@
      * {@link #PROVISIONING_VALUE_DISABLED} to disable EAB provisioning.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_EAB_PROVISIONING_STATUS = 25;
 
@@ -440,6 +469,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28;
 
@@ -448,6 +478,7 @@
      * Value is in Integer format. On (1), OFF(0).
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_MOBILE_DATA_ENABLED = 29;
 
@@ -456,12 +487,14 @@
      * Value is in Integer format. Opted-in (1) Opted-out (0).
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30;
 
     /**
      * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
      * Value is in String format.
+     * @hide
      */
     public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31;
 
@@ -470,6 +503,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32;
 
@@ -479,6 +513,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33;
 
@@ -488,6 +523,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34;
 
@@ -496,6 +532,7 @@
      * Value is in integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
 
     public static final int KEY_RTP_SPEECH_START_PORT = 35;
@@ -505,6 +542,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RTP_SPEECH_END_PORT = 36;
 
@@ -514,6 +552,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37;
 
@@ -523,6 +562,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38;
 
@@ -532,6 +572,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39;
 
@@ -541,6 +582,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40;
 
@@ -550,6 +592,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41;
 
@@ -559,6 +602,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42;
 
@@ -568,6 +612,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43;
 
@@ -577,6 +622,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44;
 
@@ -586,6 +632,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45;
 
@@ -595,6 +642,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46;
 
@@ -603,6 +651,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47;
 
@@ -611,6 +660,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48;
 
@@ -619,6 +669,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49;
 
@@ -627,6 +678,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50;
 
@@ -635,6 +687,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51;
 
@@ -643,6 +696,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52;
 
@@ -651,12 +705,14 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53;
 
     /**
      * SMS Public Service Identity.
      * Value is in String format.
+     * @hide
      */
     public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54;
 
@@ -666,16 +722,19 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_VIDEO_QUALITY = 55;
 
     /**
      * Used with {@link #KEY_VIDEO_QUALITY} to indicate low video quality.
+     * @hide
      */
     public static final int VIDEO_QUALITY_LOW = 0;
 
     /**
      * Used with {@link #KEY_VIDEO_QUALITY} to indicate high video quality.
+     * @hide
      */
     public static final int VIDEO_QUALITY_HIGH = 1;
 
@@ -685,6 +744,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_LTE_THRESHOLD_1 = 56;
 
@@ -696,6 +756,7 @@
      *
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_LTE_THRESHOLD_2 = 57;
 
@@ -707,6 +768,7 @@
      *
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_LTE_THRESHOLD_3 = 58;
 
@@ -716,6 +778,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_1X_THRESHOLD = 59;
 
@@ -727,6 +790,7 @@
      *
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_WIFI_THRESHOLD_A = 60;
 
@@ -737,6 +801,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_WIFI_THRESHOLD_B = 61;
 
@@ -746,6 +811,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_LTE_EPDG_TIMER_SEC = 62;
 
@@ -755,12 +821,14 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_WIFI_EPDG_TIMER_SEC = 63;
 
     /**
      * 1x ePDG timer (in seconds).
      * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+     * @hide
      */
     public static final int KEY_1X_EPDG_TIMER_SEC = 64;
 
@@ -769,6 +837,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_MULTIENDPOINT_ENABLED = 65;
 
@@ -777,6 +846,7 @@
      * Value is in Integer format.
      * @see #setProvisioningIntValue(int, int)
      * @see #getProvisioningIntValue(int)
+     * @hide
      */
     public static final int KEY_RTT_ENABLED = 66;
 
@@ -928,7 +998,7 @@
      *
      * @param key An integer that represents the provisioning key, which is defined by the OEM.
      * @return an integer value for the provided key, or
-     * {@link #PROVISIONING_RESULT_UNKNOWN} if the key doesn't exist.
+     * {@link ImsConfigImplBase#CONFIG_RESULT_UNKNOWN} if the key doesn't exist.
      * @throws IllegalArgumentException if the key provided was invalid.
      */
     @WorkerThread
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index 57b9b7a..dc36edf 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -19,8 +19,6 @@
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -37,8 +35,6 @@
  * Contains the User Capability Exchange capabilities corresponding to a contact's URI.
  * @hide
  */
-@SystemApi
-@TestApi
 public final class RcsContactUceCapability implements Parcelable {
 
     /** Supports 1-to-1 chat */
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 5c86ba7..1dbaff5 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -196,11 +196,11 @@
         }
 
         /**
-         * Notifies the framework when the IMS Provider is deregistered from the IMS network.
+         * Notifies the framework when the IMS Provider is unregistered from the IMS network.
          *
          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
          */
-        public void onUnregistered(@Nullable ImsReasonInfo info) {
+        public void onUnregistered(@NonNull ImsReasonInfo info) {
         }
 
         /**
@@ -211,7 +211,7 @@
          */
         public void onTechnologyChangeFailed(
                 @AccessNetworkConstants.TransportType int imsTransportType,
-                @Nullable ImsReasonInfo info) {
+                @NonNull ImsReasonInfo info) {
         }
 
         /**
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 14a64d2..7069e0a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -175,9 +175,11 @@
      */
     public final void onDeregistered(ImsReasonInfo info) {
         updateToDisconnectedState(info);
+        // ImsReasonInfo should never be null.
+        final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
         mCallbacks.broadcastAction((c) -> {
             try {
-                c.onDeregistered(info);
+                c.onDeregistered(reasonInfo);
             } catch (RemoteException e) {
                 Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
                         "callback.");
@@ -194,9 +196,10 @@
      */
     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
             ImsReasonInfo info) {
+        final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
         mCallbacks.broadcastAction((c) -> {
             try {
-                c.onTechnologyChangeFailed(imsRadioTech, info);
+                c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
             } catch (RemoteException e) {
                 Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
                         "callback.");
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index 386a58e..8564f7a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -349,6 +349,7 @@
 
     /**
      * Updates the configuration of the call barring for specified service class with password.
+     * @hide
      */
     public int updateCallBarringWithPassword(int cbType, int action, @Nullable String[] barrList,
             int serviceClass, @NonNull String password) {
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 8cc8cf4..dfaac2c 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -1063,6 +1063,20 @@
         assertThat(bootObserver2.mitigatedBootLoop()).isFalse();
     }
 
+    /**
+     * Ensure that passing a null list of failed packages does not cause any mitigation logic to
+     * execute.
+     */
+    @Test
+    public void testNullFailedPackagesList() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+        watchdog.startObservingHealth(observer1, List.of(APP_A), LONG_DURATION);
+
+        raiseFatalFailureAndDispatch(watchdog, null, PackageWatchdog.FAILURE_REASON_APP_CRASH);
+        assertThat(observer1.mMitigatedPackages).isEmpty();
+    }
+
     private void adoptShellPermissions(String... permissions) {
         InstrumentationRegistry
                 .getInstrumentation()
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index e415170..f254e4d 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -34,9 +34,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.Window;
 import android.view.WindowInsets;
-import android.view.WindowInsets.Type;
 import android.view.WindowInsetsAnimation;
 import android.view.WindowInsetsAnimation.Callback;
 import android.view.WindowInsetsAnimationControlListener;
@@ -101,7 +99,7 @@
                                 && !mRequestedController) {
                             mRequestedController = true;
                             v.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
-                                    1000, new LinearInterpolator(),
+                                    1000, new LinearInterpolator(), null /* cancellationSignal */,
                                     mCurrentRequest = new WindowInsetsAnimationControlListener() {
                                         @Override
                                         public void onReady(
@@ -208,7 +206,7 @@
                         if ((types & ime()) != 0 && !hasControl) {
                             hasControl = true;
                             controller.controlWindowInsetsAnimation(ime(), -1,
-                                    new LinearInterpolator(),
+                                    new LinearInterpolator(), null /* cancellationSignal */,
                                     new WindowInsetsAnimationControlListener() {
                                         @Override
                                         public void onReady(
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index 480b12b..009f817 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -47,6 +47,7 @@
     <uses-permission android:name="android.permission.NETWORK_STACK" />
     <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.NETWORK_FACTORY" />
+    <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java
index 99dac14..c74c112 100644
--- a/tests/net/common/java/android/net/LinkAddressTest.java
+++ b/tests/net/common/java/android/net/LinkAddressTest.java
@@ -367,6 +367,9 @@
                     -2, 100000L);
             fail("negative deprecation time should cause exception");
         } catch (IllegalArgumentException expected) { }
+
+        LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L);
+        assertEquals(100000L, addr.getDeprecationTime());
     }
 
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -382,6 +385,9 @@
                     100000L, -2);
             fail("negative expiration time should cause exception");
         } catch (IllegalArgumentException expected) { }
+
+        LinkAddress addr = new LinkAddress(V6_ADDRESS, 64, 0, 456, 100000L, 200000L);
+        assertEquals(200000L, addr.getExpirationTime());
     }
 
     @Test
diff --git a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
index 173dbd1..de65ba2 100644
--- a/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/net/common/java/android/net/NetworkAgentConfigTest.kt
@@ -22,6 +22,9 @@
 import com.android.testutils.DevSdkIgnoreRule
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.assertParcelSane
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -43,4 +46,27 @@
         }.build()
         assertParcelSane(config, 9)
     }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+    fun testBuilder() {
+        val config = NetworkAgentConfig.Builder().apply {
+            setExplicitlySelected(true)
+            setLegacyType(ConnectivityManager.TYPE_ETHERNET)
+            setSubscriberId("MySubId")
+            setPartialConnectivityAcceptable(false)
+            setUnvalidatedConnectivityAcceptable(true)
+            setLegacyTypeName("TEST_NETWORK")
+            disableNat64Detection()
+            disableProvisioningNotification()
+        }.build()
+
+        assertTrue(config.isExplicitlySelected())
+        assertEquals(ConnectivityManager.TYPE_ETHERNET, config.getLegacyType())
+        assertEquals("MySubId", config.getSubscriberId())
+        assertFalse(config.isPartialConnectivityAcceptable())
+        assertTrue(config.isUnvalidatedConnectivityAcceptable())
+        assertEquals("TEST_NETWORK", config.getLegacyTypeName())
+        assertFalse(config.isNat64DetectionEnabled())
+        assertFalse(config.isProvisioningNotificationEnabled())
+    }
 }
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 8eb5cfa..1d6c107 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -304,12 +304,12 @@
     }
 
     @Test
-    public void testConnectivityDiagnosticsCallbackOnConnectivityReport() {
-        mBinder.onConnectivityReport(createSampleConnectivityReport());
+    public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable() {
+        mBinder.onConnectivityReportAvailable(createSampleConnectivityReport());
 
         // The callback will be invoked synchronously by inline executor. Immediately check the
         // latch without waiting.
-        verify(mCb).onConnectivityReport(eq(createSampleConnectivityReport()));
+        verify(mCb).onConnectivityReportAvailable(eq(createSampleConnectivityReport()));
     }
 
     @Test
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 33d77d2..e71d599 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -64,15 +64,15 @@
     @Test
     public void testFindIndex() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 5)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12)
-                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12);
 
         assertEquals(4, stats.findIndex(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_YES,
@@ -94,21 +94,21 @@
     @Test
     public void testFindIndexHinted() {
         final NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 0L, 0L, 10)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1024L, 8L, 0L, 0L, 10)
-                .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 1024L, 8L, 11)
-                .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 1024L, 8L, 1024L, 8L, 12)
-                .addEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+                .insertEntry(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_NO, 1024L, 8L, 1024L, 8L, 12);
 
         // verify that we correctly find across regardless of hinting
@@ -143,27 +143,27 @@
         assertEquals(0, stats.size());
         assertEquals(4, stats.internalSize());
 
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_YES, 1L, 1L, 2L, 2L, 3);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_NO, 2L, 2L, 2L, 2L, 4);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                 DEFAULT_NETWORK_YES, 3L, 3L, 2L, 2L, 5);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                 DEFAULT_NETWORK_NO, 3L, 3L, 2L, 2L, 5);
 
         assertEquals(4, stats.size());
         assertEquals(4, stats.internalSize());
 
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_NO, 4L, 40L, 4L, 40L, 7);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_YES, 5L, 50L, 4L, 40L, 8);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                 DEFAULT_NETWORK_NO, 6L, 60L, 5L, 50L, 10);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                 DEFAULT_NETWORK_YES, 7L, 70L, 5L, 50L, 11);
-        stats.addEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
+        stats.insertEntry(TEST_IFACE, TEST_UID, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_YES,
                 DEFAULT_NETWORK_NO, 7L, 70L, 5L, 50L, 11);
 
         assertEquals(9, stats.size());
@@ -193,8 +193,8 @@
     public void testCombineExisting() throws Exception {
         final NetworkStats stats = new NetworkStats(TEST_START, 10);
 
-        stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
-        stats.addEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
+        stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 10);
+        stats.insertEntry(TEST_IFACE, 1001, SET_DEFAULT, 0xff, 128L, 1L, 128L, 1L, 2);
         stats.combineValues(TEST_IFACE, 1001, SET_DEFAULT, TAG_NONE, -128L, -1L,
                 -128L, -1L, -1);
 
@@ -215,12 +215,12 @@
     @Test
     public void testSubtractIdenticalData() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats result = after.subtract(before);
 
@@ -234,12 +234,12 @@
     @Test
     public void testSubtractIdenticalRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1025L, 9L, 2L, 1L, 15)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 3L, 1L, 1028L, 9L, 20);
 
         final NetworkStats result = after.subtract(before);
 
@@ -253,13 +253,13 @@
     @Test
     public void testSubtractNewRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12);
 
         final NetworkStats after = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
-                .addEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 11)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 12)
+                .insertEntry(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
 
         final NetworkStats result = after.subtract(before);
 
@@ -275,11 +275,11 @@
     @Test
     public void testSubtractMissingRows() throws Exception {
         final NetworkStats before = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
-                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
+                .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
+                .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
 
         final NetworkStats after = new NetworkStats(TEST_START, 1)
-                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
+                .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
 
         final NetworkStats result = after.subtract(before);
 
@@ -293,40 +293,40 @@
     @Test
     public void testTotalBytes() throws Exception {
         final NetworkStats iface = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
         assertEquals(384L, iface.getTotalBytes());
 
         final NetworkStats uidSet = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidSet.getTotalBytes());
 
         final NetworkStats uidTag = new NetworkStats(TEST_START, 6)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
         assertEquals(64L, uidTag.getTotalBytes());
 
         final NetworkStats uidMetered = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidMetered.getTotalBytes());
 
         final NetworkStats uidRoaming = new NetworkStats(TEST_START, 3)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
         assertEquals(96L, uidRoaming.getTotalBytes());
     }
@@ -343,11 +343,11 @@
     @Test
     public void testGroupedByIfaceAll() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 3)
-                .addEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(IFACE_ALL, 100, SET_ALL, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(IFACE_ALL, 101, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
+                .insertEntry(IFACE_ALL, 101, SET_ALL, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L);
         final NetworkStats grouped = uidStats.groupedByIface();
 
@@ -361,19 +361,19 @@
     @Test
     public void testGroupedByIface() throws Exception {
         final NetworkStats uidStats = new NetworkStats(TEST_START, 7)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L);
 
         final NetworkStats grouped = uidStats.groupedByIface();
@@ -390,19 +390,19 @@
     @Test
     public void testAddAllValues() {
         final NetworkStats first = new NetworkStats(TEST_START, 5)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
 
         final NetworkStats second = new NetworkStats(TEST_START, 2)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 32L, 0L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 100, SET_FOREGROUND, TAG_NONE, METERED_YES, ROAMING_YES,
                         DEFAULT_NETWORK_YES, 32L, 0L, 0L, 0L, 0L);
 
         first.combineAllValues(second);
@@ -421,19 +421,19 @@
     @Test
     public void testGetTotal() {
         final NetworkStats stats = new NetworkStats(TEST_START, 7)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L, 32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 4L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 512L,32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_YES,
                         DEFAULT_NETWORK_NO, 128L, 8L, 0L, 0L, 0L);
 
         assertValues(stats.getTotal(null), 1408L, 88L, 0L, 2L, 20L);
@@ -459,7 +459,7 @@
         assertEquals(0, after.size());
 
         // Test 1 item stats.
-        before.addEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
+        before.insertEntry(TEST_IFACE, 99, SET_DEFAULT, TAG_NONE, 1L, 128L, 0L, 2L, 20L);
         after = before.clone();
         after.removeUids(new int[0]);
         assertEquals(1, after.size());
@@ -469,12 +469,12 @@
         assertEquals(0, after.size());
 
         // Append remaining test items.
-        before.addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
-                .addEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
+        before.insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 2L, 64L, 0L, 2L, 20L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 4L, 32L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 16L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 8L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 4L, 0L, 0L, 0L)
+                .insertEntry(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 64L, 2L, 0L, 0L, 0L);
         assertEquals(7, before.size());
 
         // Test remove with empty uid list.
@@ -505,12 +505,12 @@
     @Test
     public void testClone() throws Exception {
         final NetworkStats original = new NetworkStats(TEST_START, 5)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
 
         // make clone and mutate original
         final NetworkStats clone = original.clone();
-        original.addEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
+        original.insertEntry(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
 
         assertEquals(3, original.size());
         assertEquals(2, clone.size());
@@ -523,8 +523,8 @@
     public void testAddWhenEmpty() throws Exception {
         final NetworkStats red = new NetworkStats(TEST_START, -1);
         final NetworkStats blue = new NetworkStats(TEST_START, 5)
-                .addEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
-                .addEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+                .insertEntry(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+                .insertEntry(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
 
         // We're mostly checking that we don't crash
         red.combineAllValues(blue);
@@ -537,37 +537,37 @@
         final String underlyingIface = "wlan0";
         final int testTag1 = 8888;
         NetworkStats delta = new NetworkStats(TEST_START, 17)
-                .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 39605L, 46L, 12259L, 55L, 0L)
-                .addEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
-                .addEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10120, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 72667L, 197L, 43909L, 241L, 0L)
-                .addEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10120, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 9297L, 17L, 4128L, 21L, 0L)
                 // VPN package also uses some traffic through unprotected network.
-                .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 4983L, 10L, 1801L, 12L, 0L)
-                .addEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
                 // Tag entries
-                .addEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10120, SET_DEFAULT, testTag1, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 21691L, 41L, 13820L, 51L, 0L)
-                .addEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10120, SET_FOREGROUND, testTag1, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1281L, 2L, 665L, 2L, 0L)
                 // Irrelevant entries
-                .addEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1685L, 5L, 2070L, 6L, 0L)
                 // Underlying Iface entries
-                .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 5178L, 8L, 2139L, 11L, 0L)
-                .addEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                        DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
-                .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L)
+                .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 149873L, 287L, 59217L /* smaller than sum(tun0) */,
                         299L /* smaller than sum(tun0) */, 0L)
-                .addEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
-                        DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
+                .insertEntry(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO,
+                        ROAMING_NO, DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
 
         delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
         assertEquals(20, delta.size());
@@ -635,19 +635,19 @@
         final String underlyingIface = "wlan0";
         NetworkStats delta = new NetworkStats(TEST_START, 9)
                 // 2 different apps sent/receive data via tun0.
-                .addEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
-                .addEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
                 // VPN package resends data through the tunnel (with exaggerated overhead)
-                .addEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(tunIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 240000, 100L, 120000L, 60L, 0L)
                 // 1 app already has some traffic on the underlying interface, the other doesn't yet
-                .addEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L)
                 // Traffic through the underlying interface via the vpn app.
                 // This test should redistribute this data correctly.
-                .addEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
 
         delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
@@ -697,9 +697,9 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3);
 
         stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL);
         assertEquals(3, stats.size());
@@ -724,9 +724,9 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3);
 
         stats.filter(testUid, INTERFACES_ALL, TAG_ALL);
         assertEquals(2, stats.size());
@@ -755,10 +755,10 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 4)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3)
-                .addEntry(entry4);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3)
+                .insertEntry(entry4);
 
         stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL);
         assertEquals(3, stats.size());
@@ -778,8 +778,8 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(entry1)
-                .addEntry(entry2);
+                .insertEntry(entry1)
+                .insertEntry(entry2);
 
         stats.filter(UID_ALL, new String[] { }, TAG_ALL);
         assertEquals(0, stats.size());
@@ -802,9 +802,9 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 3)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3);
 
         stats.filter(UID_ALL, INTERFACES_ALL, testTag);
         assertEquals(2, stats.size());
@@ -831,10 +831,10 @@
                 DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
 
         NetworkStats stats = new NetworkStats(TEST_START, 4)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3)
-                .addEntry(entry4);
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3)
+                .insertEntry(entry4);
 
         stats.filterDebugEntries();
 
@@ -891,14 +891,14 @@
                 0 /* operations */);
 
         final NetworkStats statsXt = new NetworkStats(TEST_START, 3)
-                .addEntry(appEntry)
-                .addEntry(xtRootUidEntry)
-                .addEntry(otherEntry);
+                .insertEntry(appEntry)
+                .insertEntry(xtRootUidEntry)
+                .insertEntry(otherEntry);
 
         final NetworkStats statsEbpf = new NetworkStats(TEST_START, 3)
-                .addEntry(appEntry)
-                .addEntry(ebpfRootUidEntry)
-                .addEntry(otherEntry);
+                .insertEntry(appEntry)
+                .insertEntry(ebpfRootUidEntry)
+                .insertEntry(otherEntry);
 
         statsXt.apply464xlatAdjustments(stackedIface, false);
         statsEbpf.apply464xlatAdjustments(stackedIface, true);
@@ -945,8 +945,8 @@
                 0 /* operations */);
 
         NetworkStats stats = new NetworkStats(TEST_START, 2)
-                .addEntry(firstEntry)
-                .addEntry(secondEntry);
+                .insertEntry(firstEntry)
+                .insertEntry(secondEntry);
 
         // Empty map: no adjustment
         stats.apply464xlatAdjustments(new ArrayMap<>(), false);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f9d66f9..c21772a 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2425,7 +2425,7 @@
         assertEquals(expectedRequestCount, testFactory.getMyRequestCount());
         assertTrue(testFactory.getMyStartRequested());
 
-        testFactory.unregister();
+        testFactory.terminate();
         if (networkCallback != null) mCm.unregisterNetworkCallback(networkCallback);
         handlerThread.quit();
     }
@@ -2451,6 +2451,38 @@
     }
 
     @Test
+    public void testNetworkFactoryUnregister() throws Exception {
+        final NetworkCapabilities filter = new NetworkCapabilities();
+        filter.clearAll();
+
+        final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
+        handlerThread.start();
+
+        // Checks that calling setScoreFilter on a NetworkFactory immediately before closing it
+        // does not crash.
+        for (int i = 0; i < 100; i++) {
+            final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
+                    mServiceContext, "testFactory", filter);
+            // Register the factory and don't be surprised when the default request arrives.
+            testFactory.register();
+            testFactory.expectAddRequestsWithScores(0);
+            testFactory.waitForNetworkRequests(1);
+
+            testFactory.setScoreFilter(42);
+            testFactory.terminate();
+
+            if (i % 2 == 0) {
+                try {
+                    testFactory.register();
+                    fail("Re-registering terminated NetworkFactory should throw");
+                } catch (IllegalStateException expected) {
+                }
+            }
+        }
+        handlerThread.quit();
+    }
+
+    @Test
     public void testNoMutableNetworkRequests() throws Exception {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
         NetworkRequest request1 = new NetworkRequest.Builder()
@@ -3482,7 +3514,7 @@
         cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
         assertLength(1, mCm.getAllNetworks());
 
-        testFactory.unregister();
+        testFactory.terminate();
         mCm.unregisterNetworkCallback(cellNetworkCallback);
         handlerThread.quit();
     }
@@ -3822,7 +3854,7 @@
             mCm.unregisterNetworkCallback(networkCallback);
         }
 
-        testFactory.unregister();
+        testFactory.terminate();
         handlerThread.quit();
     }
 
@@ -6757,6 +6789,26 @@
     }
 
     @Test
+    public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception {
+        final NetworkAgentInfo naiWithoutUid =
+                new NetworkAgentInfo(
+                        null, null, null, null, null, new NetworkCapabilities(), 0,
+                        mServiceContext, null, null, mService, null, null, null, 0);
+
+        mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+
+        try {
+            assertFalse(
+                    "Mismatched uid/package name should not pass the location permission check",
+                    mService.checkConnectivityDiagnosticsPermissions(
+                            Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid,
+                            mContext.getOpPackageName()));
+        } catch (SecurityException e) {
+            fail("checkConnectivityDiagnosticsPermissions shouldn't surface a SecurityException");
+        }
+    }
+
+    @Test
     public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
         final NetworkAgentInfo naiWithoutUid =
                 new NetworkAgentInfo(
@@ -6862,14 +6914,15 @@
     }
 
     @Test
-    public void testConnectivityDiagnosticsCallbackOnConnectivityReport() throws Exception {
+    public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable()
+            throws Exception {
         setUpConnectivityDiagnosticsCallback();
 
         // Block until all other events are done processing.
         HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
 
         // Verify onConnectivityReport fired
-        verify(mConnectivityDiagnosticsCallback).onConnectivityReport(
+        verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
                 argThat(report -> {
                     final NetworkCapabilities nc = report.getNetworkCapabilities();
                     return nc.getUids() == null
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index f0e5774..a6f7a36 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -240,7 +240,7 @@
 
         // Baseline
         NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+                .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
         NetworkStats uidSnapshot = null;
 
         mStatsObservers.updateStats(
@@ -264,14 +264,14 @@
 
         // Baseline
         NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+                .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
         NetworkStats uidSnapshot = null;
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
+                .insertEntry(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
         waitForObserverToIdle();
@@ -294,14 +294,14 @@
 
         // Baseline
         NetworkStats xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
+                .insertEntry(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L);
         NetworkStats uidSnapshot = null;
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */)
-                .addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
+                .insertEntry(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L,
                         BASE_BYTES + THRESHOLD_BYTES, 22L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
@@ -326,14 +326,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
@@ -359,14 +359,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
@@ -391,14 +391,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
@@ -424,14 +424,14 @@
         // Baseline
         NetworkStats xtSnapshot = null;
         NetworkStats uidSnapshot = new NetworkStats(TEST_START, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+                .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
                         ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
                 xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START);
 
         // Delta
         uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */)
-                .addEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
+                .insertEntry(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO,
                         ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L,
                         BASE_BYTES + THRESHOLD_BYTES, 2L, 0L);
         mStatsObservers.updateStats(
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 36deca3..b346c92 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -263,7 +263,7 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+                .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -276,7 +276,7 @@
         incrementCurrentTime(DAY_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 4096L, 4L, 8192L, 8L));
+                .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -306,13 +306,13 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1024L, 8L, 2048L, 16L));
+                .insertEntry(TEST_IFACE, 1024L, 8L, 2048L, 16L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
         mService.setUidForeground(UID_RED, false);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
         mService.setUidForeground(UID_RED, true);
@@ -375,7 +375,7 @@
         incrementCurrentTime(2 * HOUR_IN_MILLIS);
         expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 512L, 4L, 512L, 4L));
+                .insertEntry(TEST_IFACE, 512L, 4L, 512L, 4L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -415,11 +415,11 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+                .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 10);
 
         forcePollAndWaitForIdle();
@@ -437,11 +437,11 @@
         expectDefaultSettings();
         states = new NetworkState[] {buildMobile3gState(IMSI_2)};
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+                .insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
 
         mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
         forcePollAndWaitForIdle();
@@ -451,12 +451,12 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L));
+                .insertEntry(TEST_IFACE, 2176L, 17L, 1536L, 12L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
         mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10);
 
         forcePollAndWaitForIdle();
@@ -488,12 +488,13 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+                .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
-                .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+                        4096L, 258L, 512L, 32L, 0L)
+                .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xFAAD, 10);
 
         forcePollAndWaitForIdle();
@@ -509,12 +510,13 @@
         // special "removed" bucket.
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+                .insertEntry(TEST_IFACE, 4128L, 258L, 544L, 34L));
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
-                .addEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+                        4096L, 258L, 512L, 32L, 0L)
+                .insertEntry(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
         final Intent intent = new Intent(ACTION_UID_REMOVED);
         intent.putExtra(EXTRA_UID, UID_BLUE);
         mServiceContext.sendBroadcast(intent);
@@ -546,8 +548,8 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 5);
 
         forcePollAndWaitForIdle();
@@ -562,8 +564,8 @@
         states = new NetworkState[] {buildMobile4gState(TEST_IFACE2)};
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
 
         mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
         forcePollAndWaitForIdle();
@@ -574,10 +576,10 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
-                .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
-                .addEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+                .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+                .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
         mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
 
         forcePollAndWaitForIdle();
@@ -601,9 +603,9 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         forcePollAndWaitForIdle();
@@ -618,9 +620,10 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE,
+                        2048L, 16L, 1024L, 8L, 0L));
         forcePollAndWaitForIdle();
 
         // first verify entire history present
@@ -664,9 +667,9 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
-                .addEntry(entry1)
-                .addEntry(entry2)
-                .addEntry(entry3));
+                .insertEntry(entry1)
+                .insertEntry(entry2)
+                .insertEntry(entry3));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL);
@@ -714,11 +717,11 @@
                 .thenReturn(augmentedIfaceFilter);
         when(mStatsFactory.readNetworkStatsDetail(eq(UID_ALL), any(), eq(TAG_ALL)))
                 .thenReturn(new NetworkStats(getElapsedRealtime(), 1)
-                        .addEntry(uidStats));
+                        .insertEntry(uidStats));
         when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
                 .thenReturn(new NetworkStats(getElapsedRealtime(), 2)
-                        .addEntry(tetheredStats1)
-                        .addEntry(tetheredStats2));
+                        .insertEntry(tetheredStats1)
+                        .insertEntry(tetheredStats2));
 
         NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
 
@@ -755,8 +758,8 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
         forcePollAndWaitForIdle();
@@ -770,10 +773,10 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
+                .insertEntry(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
         mService.setUidForeground(UID_RED, true);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
 
@@ -814,9 +817,9 @@
         // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
         // We layer them on top by inspecting the iface properties.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_NO, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
@@ -853,9 +856,9 @@
         // ROAMING_NO, because metered and roaming isn't tracked at that layer. We layer it
         // on top by inspecting the iface properties.
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_ALL, ROAMING_NO,
                         DEFAULT_NETWORK_YES,  128L, 2L, 128L, 2L, 0L)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, METERED_ALL, ROAMING_NO,
                         DEFAULT_NETWORK_YES, 64L, 1L, 64L, 1L, 0L));
         forcePollAndWaitForIdle();
 
@@ -888,17 +891,17 @@
 
         // Traffic seen by kernel counters (includes software tethering).
         final NetworkStats ifaceStats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1536L, 12L, 384L, 3L);
+                .insertEntry(TEST_IFACE, 1536L, 12L, 384L, 3L);
         // Hardware tethering traffic, not seen by kernel counters.
         final NetworkStats tetherStatsHardware = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 512L, 4L, 128L, 1L);
+                .insertEntry(TEST_IFACE, 512L, 4L, 128L, 1L);
 
         // Traffic for UID_RED.
         final NetworkStats uidStats = new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
+                .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L);
         // All tethering traffic, both hardware and software.
         final NetworkStats tetherStats = new NetworkStats(getElapsedRealtime(), 1)
-                .addEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
+                .insertEntry(TEST_IFACE, UID_TETHERING, SET_DEFAULT, TAG_NONE, 1920L, 14L, 384L, 2L,
                         0L);
 
         expectNetworkStatsSummary(ifaceStats, tetherStatsHardware);
@@ -957,7 +960,7 @@
         incrementCurrentTime(HOUR_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
+                .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -972,7 +975,7 @@
         incrementCurrentTime(DAY_IN_MILLIS);
         expectDefaultSettings();
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
+                .insertEntry(TEST_IFACE, 4096000L, 4L, 8192000L, 8L));
         expectNetworkStatsUidDetail(buildEmptyStats());
         forcePollAndWaitForIdle();
 
@@ -1026,18 +1029,18 @@
         mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states), new VpnInfo[0]);
 
         // Verifies that one requestStatsUpdate will be called during iface update.
-        provider.expectStatsUpdate(0 /* unused */);
+        provider.expectOnRequestStatsUpdate(0 /* unused */);
 
         // Create some initial traffic and report to the service.
         incrementCurrentTime(HOUR_IN_MILLIS);
         final NetworkStats expectedStats = new NetworkStats(0L, 1)
-                .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
                         TAG_NONE, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
                         128L, 2L, 128L, 2L, 1L))
-                .addValues(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+                .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
                         0xF00D, METERED_YES, ROAMING_NO, DEFAULT_NETWORK_YES,
                         64L, 1L, 64L, 1L, 1L));
-        cb.onStatsUpdated(0 /* unused */, expectedStats, expectedStats);
+        cb.notifyStatsUpdated(0 /* unused */, expectedStats, expectedStats);
 
         // Make another empty mutable stats object. This is necessary since the new NetworkStats
         // object will be used to compare with the old one in NetworkStatsRecoder, two of them
@@ -1047,8 +1050,8 @@
         forcePollAndWaitForIdle();
 
         // Verifies that one requestStatsUpdate and setAlert will be called during polling.
-        provider.expectStatsUpdate(0 /* unused */);
-        provider.expectSetAlert(MB_IN_BYTES);
+        provider.expectOnRequestStatsUpdate(0 /* unused */);
+        provider.expectOnSetAlert(MB_IN_BYTES);
 
         // Verifies that service recorded history, does not verify uid tag part.
         assertUidTotal(sTemplateWifi, UID_RED, 128L, 2L, 128L, 2L, 1);
@@ -1082,13 +1085,13 @@
         assertNotNull(cb);
 
         // Simulates alert quota of the provider has been reached.
-        cb.onAlertReached();
+        cb.notifyAlertReached();
         HandlerUtilsKt.waitForIdle(mHandlerThread, WAIT_TIMEOUT);
 
         // Verifies that polling is triggered by alert reached.
-        provider.expectStatsUpdate(0 /* unused */);
+        provider.expectOnRequestStatsUpdate(0 /* unused */);
         // Verifies that global alert will be re-armed.
-        provider.expectSetAlert(MB_IN_BYTES);
+        provider.expectOnSetAlert(MB_IN_BYTES);
     }
 
     private static File getBaseDir(File statsDir) {
diff --git a/wifi/Android.bp b/wifi/Android.bp
index f4d2881..70c2741 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -72,8 +72,7 @@
 // classes before they are renamed.
 java_library {
     name: "framework-wifi-pre-jarjar",
-    // TODO(b/146757305): sdk_version should be "module_lib_current"
-    sdk_version: "core_current",
+    sdk_version: "module_current",
     static_libs: [
         "framework-wifi-util-lib",
         "android.hardware.wifi-V1.0-java-constants",
@@ -83,9 +82,6 @@
         "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
         "unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage
         "framework-telephony-stubs",
-        // TODO(b/146757305): should be unnecessary once
-        // sdk_version="module_lib_current"
-        "android_system_stubs_current",
     ],
     srcs: [
         ":framework-wifi-updatable-sources",
@@ -98,21 +94,12 @@
         "//frameworks/opt/net/wifi/service",
         "//frameworks/opt/net/wifi/tests/wifitests",
     ],
-
-    // TODO(b/146757305): should be unnecessary once
-    // sdk_version="module_lib_current"
-    aidl: {
-        include_dirs: [
-            "frameworks/base/core/java",
-        ],
-    },
 }
 
 // post-jarjar version of framework-wifi
 java_library {
     name: "framework-wifi",
-    // TODO(b/146757305): sdk_version should be "module_lib_current"
-    sdk_version: "core_current",
+    sdk_version: "module_current",
     static_libs: [
         "framework-wifi-pre-jarjar",
     ],
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 70542b5..727952c 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -695,31 +695,6 @@
      */
     public AnqpInformationElement[] anqpElements;
 
-    /**
-     * Flag indicating if this AP is a carrier AP. The determination is based
-     * on the AP's SSID and if AP is using EAP security.
-     *
-     * @hide
-     */
-    // TODO(b/144431927): remove once migrated to Suggestions
-    public boolean isCarrierAp;
-
-    /**
-     * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
-     *
-     * @hide
-     */
-    // TODO(b/144431927): remove once migrated to Suggestions
-    public int carrierApEapType;
-
-    /**
-     * The name of the carrier that's associated with this AP if it is a carrier AP.
-     *
-     * @hide
-     */
-    // TODO(b/144431927): remove once migrated to Suggestions
-    public String carrierName;
-
     /** {@hide} */
     public ScanResult(WifiSsid wifiSsid, String BSSID, long hessid, int anqpDomainId,
             byte[] osuProviders, String caps, int level, int frequency, long tsf) {
@@ -744,9 +719,6 @@
         this.centerFreq0 = UNSPECIFIED;
         this.centerFreq1 = UNSPECIFIED;
         this.flags = 0;
-        this.isCarrierAp = false;
-        this.carrierApEapType = UNSPECIFIED;
-        this.carrierName = null;
         this.radioChainInfos = null;
         this.mWifiStandard = WIFI_STANDARD_UNKNOWN;
     }
@@ -767,9 +739,6 @@
         this.centerFreq0 = UNSPECIFIED;
         this.centerFreq1 = UNSPECIFIED;
         this.flags = 0;
-        this.isCarrierAp = false;
-        this.carrierApEapType = UNSPECIFIED;
-        this.carrierName = null;
         this.radioChainInfos = null;
         this.mWifiStandard = WIFI_STANDARD_UNKNOWN;
     }
@@ -797,9 +766,6 @@
         } else {
             this.flags = 0;
         }
-        this.isCarrierAp = false;
-        this.carrierApEapType = UNSPECIFIED;
-        this.carrierName = null;
         this.radioChainInfos = null;
         this.mWifiStandard = WIFI_STANDARD_UNKNOWN;
     }
@@ -839,9 +805,6 @@
             venueName = source.venueName;
             operatorFriendlyName = source.operatorFriendlyName;
             flags = source.flags;
-            isCarrierAp = source.isCarrierAp;
-            carrierApEapType = source.carrierApEapType;
-            carrierName = source.carrierName;
             radioChainInfos = source.radioChainInfos;
             this.mWifiStandard = source.mWifiStandard;
         }
@@ -881,9 +844,6 @@
         sb.append(", standard: ").append(wifiStandardToString(mWifiStandard));
         sb.append(", 80211mcResponder: ");
         sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported");
-        sb.append(", Carrier AP: ").append(isCarrierAp ? "yes" : "no");
-        sb.append(", Carrier AP EAP Type: ").append(carrierApEapType);
-        sb.append(", Carrier name: ").append(carrierName);
         sb.append(", Radio Chain Infos: ").append(Arrays.toString(radioChainInfos));
         return sb.toString();
     }
@@ -954,9 +914,6 @@
         } else {
             dest.writeInt(0);
         }
-        dest.writeInt(isCarrierAp ? 1 : 0);
-        dest.writeInt(carrierApEapType);
-        dest.writeString(carrierName);
 
         if (radioChainInfos != null) {
             dest.writeInt(radioChainInfos.length);
@@ -1036,9 +993,6 @@
                                 new AnqpInformationElement(vendorId, elementId, payload);
                     }
                 }
-                sr.isCarrierAp = in.readInt() != 0;
-                sr.carrierApEapType = in.readInt();
-                sr.carrierName = in.readString();
                 n = in.readInt();
                 if (n != 0) {
                     sr.radioChainInfos = new RadioChainInfo[n];
diff --git a/wifi/java/android/net/wifi/WifiMigration.java b/wifi/java/android/net/wifi/WifiMigration.java
index a3482d7..008b18b 100755
--- a/wifi/java/android/net/wifi/WifiMigration.java
+++ b/wifi/java/android/net/wifi/WifiMigration.java
@@ -16,8 +16,6 @@
 
 package android.net.wifi;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -26,8 +24,6 @@
 import android.os.Parcelable;
 import android.provider.Settings;
 
-import java.util.List;
-
 /**
  * Class used to provide one time hooks for existing OEM devices to migrate their config store
  * data and other settings to the wifi mainline module.
@@ -39,166 +35,6 @@
     private WifiMigration() { }
 
     /**
-     * Container for all the wifi config data to migrate.
-     */
-    public static final class ConfigStoreMigrationData implements Parcelable {
-        /**
-         * Builder to create instance of {@link ConfigStoreMigrationData}.
-         */
-        public static final class Builder {
-            private List<WifiConfiguration> mUserSavedNetworkConfigurations;
-            private SoftApConfiguration mUserSoftApConfiguration;
-
-            public Builder() {
-                mUserSavedNetworkConfigurations = null;
-                mUserSoftApConfiguration = null;
-            }
-
-            /**
-             * Sets the list of all user's saved network configurations parsed from OEM config
-             * store files.
-             *
-             * @param userSavedNetworkConfigurations List of {@link WifiConfiguration} representing
-             *                                       the list of user's saved networks
-             * @return Instance of {@link Builder} to enable chaining of the builder method.
-             */
-            public @NonNull Builder setUserSavedNetworkConfigurations(
-                    @NonNull List<WifiConfiguration> userSavedNetworkConfigurations) {
-                checkNotNull(userSavedNetworkConfigurations);
-                mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
-                return this;
-            }
-
-            /**
-             * Sets the user's softap configuration parsed from OEM config store files.
-             *
-             * @param userSoftApConfiguration {@link SoftApConfiguration} representing user's
-             *                                SoftAp configuration
-             * @return Instance of {@link Builder} to enable chaining of the builder method.
-             */
-            public @NonNull Builder setUserSoftApConfiguration(
-                    @NonNull SoftApConfiguration userSoftApConfiguration) {
-                checkNotNull(userSoftApConfiguration);
-                mUserSoftApConfiguration  = userSoftApConfiguration;
-                return this;
-            }
-
-            /**
-             * Build an instance of {@link ConfigStoreMigrationData}.
-             *
-             * @return Instance of {@link ConfigStoreMigrationData}.
-             */
-            public @NonNull ConfigStoreMigrationData build() {
-                return new ConfigStoreMigrationData(
-                        mUserSavedNetworkConfigurations, mUserSoftApConfiguration);
-            }
-        }
-
-        private final List<WifiConfiguration> mUserSavedNetworkConfigurations;
-        private final SoftApConfiguration mUserSoftApConfiguration;
-
-        private ConfigStoreMigrationData(
-                @Nullable List<WifiConfiguration> userSavedNetworkConfigurations,
-                @Nullable SoftApConfiguration userSoftApConfiguration) {
-            mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
-            mUserSoftApConfiguration = userSoftApConfiguration;
-        }
-
-        public static final @NonNull Parcelable.Creator<ConfigStoreMigrationData> CREATOR =
-                new Parcelable.Creator<ConfigStoreMigrationData>() {
-                    @Override
-                    public ConfigStoreMigrationData createFromParcel(Parcel in) {
-                        List<WifiConfiguration> userSavedNetworkConfigurations =
-                                in.readArrayList(null);
-                        SoftApConfiguration userSoftApConfiguration = in.readParcelable(null);
-                        return new ConfigStoreMigrationData(
-                                userSavedNetworkConfigurations, userSoftApConfiguration);
-                    }
-
-                    @Override
-                    public ConfigStoreMigrationData[] newArray(int size) {
-                        return new ConfigStoreMigrationData[size];
-                    }
-                };
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(@NonNull Parcel dest, int flags) {
-            dest.writeList(mUserSavedNetworkConfigurations);
-            dest.writeParcelable(mUserSoftApConfiguration, flags);
-        }
-
-        /**
-         * Returns list of all user's saved network configurations.
-         *
-         * Note: Only to be returned if there is any format change in how OEM persisted this info.
-         * @return List of {@link WifiConfiguration} representing the list of user's saved networks,
-         * or null if no migration necessary.
-         */
-        @Nullable
-        public List<WifiConfiguration> getUserSavedNetworkConfigurations() {
-            return mUserSavedNetworkConfigurations;
-        }
-
-        /**
-         * Returns user's softap configuration.
-         *
-         * Note: Only to be returned if there is any format change in how OEM persisted this info.
-         * @return {@link SoftApConfiguration} representing user's SoftAp configuration,
-         * or null if no migration necessary.
-         */
-        @Nullable
-        public SoftApConfiguration getUserSoftApConfiguration() {
-            return mUserSoftApConfiguration;
-        }
-    }
-
-    /**
-     * Load data from OEM's config store.
-     * <p>
-     * Note:
-     * <li>OEMs need to implement {@link #loadFromConfigStore()} ()} only if their
-     * existing config store format or file locations differs from the vanilla AOSP implementation.
-     * </li>
-     * <li>The wifi mainline module will invoke {@link #loadFromConfigStore()} method on every
-     * bootup, its the responsibility of the OEM implementation to ensure that this method returns
-     * non-null data only on the first bootup. Once the migration is done, the OEM can safely delete
-     * their config store files when {@link #removeConfigStore()} is invoked.
-     * <li>The first & only relevant invocation of {@link #loadFromConfigStore()} occurs when a
-     * previously released device upgrades to the wifi mainline module from an OEM implementation
-     * of the wifi stack.
-     * </li>
-     *
-     * @return Instance of {@link ConfigStoreMigrationData} for migrating data, null if no
-     * migration is necessary.
-     */
-    @Nullable
-    public static ConfigStoreMigrationData loadFromConfigStore() {
-        // Note: OEMs should add code to parse data from their config store format here!
-        return null;
-    }
-
-    /**
-     * Remove OEM's config store.
-     * <p>
-     * Note:
-     * <li>OEMs need to implement {@link #removeConfigStore()} only if their
-     * existing config store format or file locations differs from the vanilla AOSP implementation (
-     * which is what the wifi mainline module understands).
-     * </li>
-     * <li> The wifi mainline module will invoke {@link #removeConfigStore()} after it migrates
-     * all the existing data retrieved from {@link #loadFromConfigStore()}.
-     * </li>
-     */
-    public static void removeConfigStore() {
-        // Note: OEMs should remove their custom config store files here!
-    }
-
-    /**
      * Container for all the wifi settings data to migrate.
      */
     public static final class SettingsMigrationData implements Parcelable {
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 4449860..72ca900 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -685,8 +685,7 @@
                     mIsAppInteractionRequired,
                     mIsUserInteractionRequired,
                     mIsSharedWithUser,
-                    mIsInitialAutojoinEnabled,
-                    mIsNetworkUntrusted);
+                    mIsInitialAutojoinEnabled);
         }
     }
 
@@ -729,13 +728,6 @@
      */
     public final boolean isInitialAutoJoinEnabled;
 
-    /**
-     * Whether this network will be brought up as untrusted (TRUSTED capability bit removed).
-     * @hide
-     */
-    public final boolean isNetworkUntrusted;
-
-
     /** @hide */
     public WifiNetworkSuggestion() {
         this.wifiConfiguration = new WifiConfiguration();
@@ -744,7 +736,6 @@
         this.isUserInteractionRequired = false;
         this.isUserAllowedToManuallyConnect = true;
         this.isInitialAutoJoinEnabled = true;
-        this.isNetworkUntrusted = false;
     }
 
     /** @hide */
@@ -753,8 +744,7 @@
                                  boolean isAppInteractionRequired,
                                  boolean isUserInteractionRequired,
                                  boolean isUserAllowedToManuallyConnect,
-                                 boolean isInitialAutoJoinEnabled,
-                                 boolean isNetworkUntrusted) {
+                                 boolean isInitialAutoJoinEnabled) {
         checkNotNull(networkConfiguration);
         this.wifiConfiguration = networkConfiguration;
         this.passpointConfiguration = passpointConfiguration;
@@ -763,7 +753,6 @@
         this.isUserInteractionRequired = isUserInteractionRequired;
         this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect;
         this.isInitialAutoJoinEnabled = isInitialAutoJoinEnabled;
-        this.isNetworkUntrusted = isNetworkUntrusted;
     }
 
     public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR =
@@ -776,8 +765,7 @@
                             in.readBoolean(), // isAppInteractionRequired
                             in.readBoolean(), // isUserInteractionRequired
                             in.readBoolean(), // isSharedCredentialWithUser
-                            in.readBoolean(),  // isAutojoinEnabled
-                            in.readBoolean()
+                            in.readBoolean()  // isAutojoinEnabled
                     );
                 }
 
@@ -800,7 +788,6 @@
         dest.writeBoolean(isUserInteractionRequired);
         dest.writeBoolean(isUserAllowedToManuallyConnect);
         dest.writeBoolean(isInitialAutoJoinEnabled);
-        dest.writeBoolean(isNetworkUntrusted);
     }
 
     @Override
@@ -843,7 +830,7 @@
                 .append(", isUserInteractionRequired=").append(isUserInteractionRequired)
                 .append(", isCredentialSharedWithUser=").append(isUserAllowedToManuallyConnect)
                 .append(", isInitialAutoJoinEnabled=").append(isInitialAutoJoinEnabled)
-                .append(", isUnTrusted=").append(isNetworkUntrusted)
+                .append(", isUnTrusted=").append(!wifiConfiguration.trusted)
                 .append(" ]");
         return sb.toString();
     }
@@ -934,7 +921,7 @@
 
     /** @see Builder#setUntrusted(boolean)  */
     public boolean isUntrusted() {
-        return isNetworkUntrusted;
+        return !wifiConfiguration.trusted;
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java
index 4c22d5d..6cb8324 100644
--- a/wifi/tests/src/android/net/wifi/ScanResultTest.java
+++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java
@@ -156,8 +156,7 @@
                 + "distance: 0(cm), distanceSd: 0(cm), "
                 + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, "
                 + "standard: 11ac, "
-                + "80211mcResponder: is not supported, Carrier AP: no, "
-                + "Carrier AP EAP Type: 0, Carrier name: null, "
+                + "80211mcResponder: is not supported, "
                 + "Radio Chain Infos: null", scanResult.toString());
     }
 
@@ -179,8 +178,7 @@
                 + "distanceSd: 0(cm), "
                 + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, "
                 + "standard: 11ac, "
-                + "80211mcResponder: is not supported, Carrier AP: no, "
-                + "Carrier AP EAP Type: 0, Carrier name: null, "
+                + "80211mcResponder: is not supported, "
                 + "Radio Chain Infos: [RadioChainInfo: id=0, level=-45, "
                 + "RadioChainInfo: id=1, level=-54]", scanResult.toString());
     }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 51bf738..01b2a8d 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -532,7 +532,7 @@
         configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
-                configuration, null, false, true, true, true, false);
+                configuration, null, false, true, true, true);
 
         Parcel parcelW = Parcel.obtain();
         suggestion.writeToParcel(parcelW, 0);
@@ -603,14 +603,14 @@
         configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null, true, false, true, true, false);
+                new WifiNetworkSuggestion(configuration, null, true, false, true, true);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
         configuration1.BSSID = TEST_BSSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, true, true, true, false);
+                new WifiNetworkSuggestion(configuration1, null, false, true, true, true);
 
         assertEquals(suggestion, suggestion1);
         assertEquals(suggestion.hashCode(), suggestion1.hashCode());
@@ -626,13 +626,13 @@
         configuration.SSID = TEST_SSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null, false, false, true, true, false);
+                new WifiNetworkSuggestion(configuration, null, false, false, true, true);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID_1;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, false, true, true, false);
+                new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
 
         assertNotEquals(suggestion, suggestion1);
     }
@@ -648,13 +648,13 @@
         configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null,  false, false, true, true, false);
+                new WifiNetworkSuggestion(configuration, null,  false, false, true, true);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, false, true, true, false);
+                new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
 
         assertNotEquals(suggestion, suggestion1);
     }
@@ -669,13 +669,13 @@
         configuration.SSID = TEST_SSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null, false, false, true, true, false);
+                new WifiNetworkSuggestion(configuration, null, false, false, true, true);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, false, true, true, false);
+                new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
 
         assertNotEquals(suggestion, suggestion1);
     }
@@ -770,7 +770,7 @@
                 .setWpa2Passphrase(TEST_PRESHARED_KEY)
                 .setUntrusted(true)
                 .build();
-        assertTrue(suggestion.isNetworkUntrusted);
+        assertTrue(suggestion.isUntrusted());
         assertFalse(suggestion.isUserAllowedToManuallyConnect);
     }
 
@@ -786,7 +786,7 @@
                 .setPasspointConfig(passpointConfiguration)
                 .setUntrusted(true)
                 .build();
-        assertTrue(suggestion.isNetworkUntrusted);
+        assertTrue(suggestion.isUntrusted());
         assertFalse(suggestion.isUserAllowedToManuallyConnect);
     }